Created
March 22, 2022 09:27
-
-
Save najathi/00932c4b76287abf56ff3711ac8f9a4b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useCallback, useMemo, useState } from 'react'; | |
import PropTypes from 'prop-types'; | |
import { Editor } from "react-draft-wysiwyg"; | |
import { convertToRaw, RichUtils } from "draft-js"; | |
import { convertToHTML } from 'draft-convert'; | |
import DOMPurify from 'dompurify'; | |
import { toast } from 'react-toastify'; | |
import draftToHtml from 'draftjs-to-html'; | |
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css"; | |
import './styles.scss'; | |
export default function RichTextEditor(props) { | |
const { onChange, value, name, defaultValue, ...rest } = props; | |
const [convertedContent, setConvertedContent] = useState(null); | |
const [preview, setPreview] = useState(false); | |
const [codeView, setCodeView] = useState(false); | |
const onEditorStateChange = useCallback((editorState) => { | |
onChange({ name, value: editorState }); | |
setConvertedContent(convertToHTML(editorState.getCurrentContent())); | |
}, [setConvertedContent, onChange]); | |
const createMarkup = useCallback((html) => { | |
return { | |
__html: DOMPurify.sanitize(html) | |
} | |
}, []); | |
const previewHandler = useCallback(() => { | |
setPreview(!preview); | |
}, [preview, setPreview]); | |
const codeViewHandler = useCallback(() => { | |
setCodeView(!codeView); | |
}, [codeView, setCodeView]); | |
function uploadImageCallBack(file) { | |
return new Promise( | |
(resolve, reject) => { | |
const xhr = new XMLHttpRequest(); | |
xhr.open('POST', 'https://api.imgur.com/3/image'); | |
xhr.setRequestHeader('Authorization', 'Client-ID XXXXX'); | |
const data = new FormData(); | |
data.append('image', file); | |
xhr.send(data); | |
xhr.addEventListener('load', () => { | |
const response = JSON.parse(xhr.responseText); | |
resolve(response); | |
}); | |
xhr.addEventListener('error', () => { | |
const error = JSON.parse(xhr.responseText); | |
toast.error(xhr.responseText, { | |
position: toast.POSITION.BOTTOM_CENTER | |
}); | |
reject(error); | |
}); | |
} | |
); | |
} | |
const options = [ | |
'inline', 'blockType', 'fontSize', 'fontFamily', 'list', 'textAlign', 'colorPicker', 'link', 'embedded', 'emoji', 'image', 'remove', 'history' | |
]; | |
return useMemo(()=>( | |
<div className='editor'> | |
<Editor | |
editorState={value} | |
defaultEditorState={defaultValue} | |
onEditorStateChange={onEditorStateChange} | |
wrapperClassName="wrapper-class" | |
editorClassName="editor-class" | |
toolbarClassName="toolbar-class" | |
toolbar={{ | |
options: options, | |
inline: { inDropdown: true }, | |
list: { inDropdown: true }, | |
textAlign: { inDropdown: true }, | |
link: { inDropdown: true }, | |
history: { inDropdown: true }, | |
image: { uploadCallback: uploadImageCallBack, alt: { present: true, mandatory: true } }, | |
}} | |
toolbarCustomButtons={ | |
[ | |
<ToggleBold key={0} />, | |
<PreviewPanel key={1} | |
previewHandler={previewHandler} | |
codeViewHandler={codeViewHandler} | |
preview={preview} | |
codeView={codeView} | |
/>, | |
] | |
} | |
{...rest} | |
/> | |
{preview && | |
<div | |
className="preview" | |
dangerouslySetInnerHTML={createMarkup(convertedContent)}> | |
</div> | |
} | |
{codeView && | |
<textarea | |
disabled | |
className="RichTextEditor__codeview" | |
value={draftToHtml(convertToRaw(value.getCurrentContent()))} | |
/> | |
} | |
</div> | |
), [props, value, defaultValue, options, preview, codeView, onEditorStateChange, previewHandler, codeViewHandler]); | |
} | |
RichTextEditor.propTypes = { | |
onChange: PropTypes.any.isRequired, | |
} | |
const ToggleBold = (props) => { | |
const toggleBold = () => { | |
const { editorState, onChange } = props; | |
const newState = RichUtils.toggleInlineStyle( | |
editorState, | |
'BOLD', | |
); | |
if (newState) { | |
onChange(newState); | |
} | |
}; | |
return ( | |
<div className="tool" onClick={toggleBold}>B</div> | |
); | |
} | |
ToggleBold.propTypes = { | |
onChange: PropTypes.func, | |
editorState: PropTypes.object, | |
}; | |
const PreviewPanel = (props) => { | |
const { preview,codeView,previewHandler, codeViewHandler } = props; | |
return ( | |
<> | |
<div className="tool" onClick={previewHandler}>{!preview ? "Preview" : "Hide Preview"}</div> | |
<div className="tool" onClick={codeViewHandler}>{!codeView ? "Code view" : "Hide Code"}</div> | |
</> | |
); | |
} | |
PreviewPanel.propTypes = { | |
previewHandler: PropTypes.func, | |
codeViewHandler: PropTypes.func, | |
preview: PropTypes.any, | |
codeView: PropTypes.any, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment