import {Box} from '@chakra-ui/react' import styled from '@emotion/styled' import * as React from 'react' import {OSGUploadAdapterPlugin} from './OSGUploadAdapter' const EditorWrapper = styled(Box)` width: 100%; height: 100%; min-height: 20px; .ck.ck-editor__editable_inline[dir='ltr'] { text-align: inherit; } .ck.ck-editor__editable_inline[dir='rtl'] { text-align: inherit; } .ck-content > * { all: revert; } .ck-focused { border: none !important; box-shadow: 0 0 0 2.5px #4fd1c5 !important; } .ck-blurred:hover { box-shadow: 0 0 0 2.5px #4fd1c5 !important; transition: box-shadow 0.3s ease-in-out; } .ck-editor__editable_inline { padding: 0 !important; border: none !important; overflow: unset !important; } .ck.ck-editor__editable_inline > *:first-of-type { margin-top: 0 !important; } .ck.ck-editor__editable_inline > *:last-child { margin-bottom: 0 !important; } ` const LoadableCKEditor = React.lazy(() => // @ts-ignore import('@ckeditor/ckeditor5-react').then(module => ({ default: module.CKEditor })) ) type EditorProps = { defaultValue: string value?: string editing: boolean disableToolbar: boolean /** * Get the data from the editor on blur. * * Only called when the editor is in edit mode and the data is not the same as the default value. * * @param data - The data that was changed */ onBlurValue: (data: string) => void } let BalloonEditor: any = undefined const cleanValue = (defaultValue: string, value?: string) => { let v = value || defaultValue || '' // Check if the default value does not contain any HTML tags // If so, wrap it in a

tag, else return the default value if (v.indexOf('<') === -1) { return `

${v}

` } return v } /** * TODO: Renders twice all the time. :( */ const Editor: React.FC = props => { const [isFocused, setIsFocused] = React.useState(false) React.useEffect(() => { if (!props.editing && isFocused) { setIsFocused(false) } }, [props.editing]) const [value, setValue] = React.useState(() => cleanValue(props.defaultValue, props.value) ) React.useEffect(() => { if (JSON.stringify(value) !== JSON.stringify(props.value)) { setValue(cleanValue(props.defaultValue, props.value)) } }, [props.value, props.editing]) const editorConfig: {[key: string]: any} = { mediaEmbed: { previewsInData: true }, extraPlugins: [OSGUploadAdapterPlugin], link: { addTargetToExternalLinks: true } } if (props.disableToolbar) { editorConfig['toolbar'] = [] } const [editor, setEditor] = React.useState(BalloonEditor) React.useEffect(() => { async function load() { if (!BalloonEditor && !editor && props.editing) { //@ts-ignore BalloonEditor = await import('@ckeditor/ckeditor5-build-balloon') setEditor(BalloonEditor) } } load() }) let hoverTimeout: NodeJS.Timeout const handleMouseOver = () => { if (!props.editing) { return } if (isFocused) { return } hoverTimeout = setTimeout(() => { setIsFocused(true) }, 100) } const handleMouseLeave = () => { if (!props.editing) { return } if (hoverTimeout) { clearTimeout(hoverTimeout) } } const fallbackRef = React.useRef(null) const fallbackElement = (
) React.useEffect(() => { if (fallbackRef.current) { fallbackRef.current.innerHTML = value } }, [value]) const editorElement = ( {props.editing && isFocused && editor ? ( <> { const data = editor.getData() if (data !== value) { setValue(data || props.defaultValue) props.onBlurValue(data) } }} onLoad={(editor: any) => { editor.writer.addClass('revert-css') }} /> ) : ( fallbackElement )} ) return ( {editorElement} ) } export default Editor