Skip to content

Instantly share code, notes, and snippets.

@najathi
Created March 22, 2022 09:27
Show Gist options
  • Save najathi/00932c4b76287abf56ff3711ac8f9a4b to your computer and use it in GitHub Desktop.
Save najathi/00932c4b76287abf56ff3711ac8f9a4b to your computer and use it in GitHub Desktop.
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