diff --git a/src/api/agency/addAgencyData.ts b/src/api/agency/addAgencyData.ts index 2e24b1a5..844f0024 100644 --- a/src/api/agency/addAgencyData.ts +++ b/src/api/agency/addAgencyData.ts @@ -23,6 +23,7 @@ function buildAgencyDataRequestBody(consultingTypeResponseId: string | number, f offline: formData.offline, demographics: formData.demographics, counsellingRelations: formData.counsellingRelations, + dataProtection: formData.dataProtection, }); } diff --git a/src/components/RichText/BlockStyleButton.tsx b/src/components/FormPluginEditor/BlockStyleButton.tsx similarity index 73% rename from src/components/RichText/BlockStyleButton.tsx rename to src/components/FormPluginEditor/BlockStyleButton.tsx index b1a1174c..64f43446 100644 --- a/src/components/RichText/BlockStyleButton.tsx +++ b/src/components/FormPluginEditor/BlockStyleButton.tsx @@ -1,6 +1,5 @@ -/* eslint-disable react/no-children-prop */ import clsx from 'clsx'; -import { EditorState, RichUtils } from 'draft-js'; +import { RichUtils, SelectionState } from 'draft-js'; import React, { MouseEvent, ReactNode, useCallback, useMemo } from 'react'; import { DraftJsStyleButtonProps } from '@draft-js-plugins/buttons'; import { Button } from 'antd'; @@ -13,7 +12,7 @@ interface ButtonProps extends DraftJsStyleButtonProps { interface CreateBlockStyleButtonProps extends Omit { blockType: string; children: ReactNode; - editorState: EditorState; + selectionState: SelectionState; buttonProps?: ButtonProps; } @@ -23,14 +22,15 @@ const BlockStyleButton = ({ theme, buttonProps, setEditorState, - editorState, + getEditorState, + selectionState, }: CreateBlockStyleButtonProps) => { const toggleStyle = useCallback( (event: MouseEvent): void => { event.preventDefault(); - setEditorState(RichUtils.toggleBlockType(editorState, blockType)); + setEditorState(RichUtils.toggleBlockType(getEditorState(), blockType)); }, - [editorState, setEditorState], + [setEditorState, getEditorState], ); const preventBubblingUp = useCallback((event: MouseEvent): void => { @@ -39,19 +39,20 @@ const BlockStyleButton = ({ const blockTypeIsActive = useMemo((): boolean => { // if the button is rendered before the editor + const editorState = getEditorState(); if (!editorState) { return false; } - const type = editorState.getCurrentContent().getBlockForKey(editorState.getSelection().getStartKey()).getType(); + const type = + selectionState && editorState.getCurrentContent().getBlockForKey(selectionState.getStartKey()).getType(); return type === blockType; - }, [editorState]); + }, [selectionState, getEditorState]); const className = blockTypeIsActive ? clsx(theme.button, theme.active) : theme.button; return ( ); }; export default BlockStyleButton; diff --git a/src/components/FormPluginEditor/Editor.tsx b/src/components/FormPluginEditor/Editor.tsx new file mode 100644 index 00000000..b475ec00 --- /dev/null +++ b/src/components/FormPluginEditor/Editor.tsx @@ -0,0 +1,128 @@ +import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import DisabledContext from 'antd/es/config-provider/DisabledContext'; +import { + ContentState, + convertFromHTML, + DraftEditorCommand, + DraftHandleValue, + EditorState, + getDefaultKeyBinding, + RichUtils, +} from 'draft-js'; +import { stateToHTML } from 'draft-js-export-html'; +import PluginsEditor from '@draft-js-plugins/editor'; +import createPlaceholderPlugin from '../../utils/draftjs/placeholderPlugin'; + +const Editor = ({ + onChange, + value, + onSelectionChange, + onInlineStyleChange, + placeholders, + onBlur, + onFocus, + placeholder, + editorPlugins, +}: any) => { + const disabled = useContext(DisabledContext); + + const plugins = useMemo(() => [...editorPlugins, createPlaceholderPlugin({ placeholders })], [placeholders]); + + const [editorState, setEditorState] = useState(() => { + const { contentBlocks, entityMap } = convertFromHTML(value); + const contentState = ContentState.createFromBlockArray(contentBlocks, entityMap); + return EditorState.createWithContent(contentState); + }); + + useEffect(() => { + setEditorState((state) => { + const contentState = state.getCurrentContent(); + contentState.getAllEntities().forEach((entity, entityKey) => { + contentState.mergeEntityData(entityKey, { disabled }); + }); + return EditorState.createWithContent(contentState, state.getDecorator()); + }); + }, [disabled]); + + useEffect(() => { + const resetState = () => { + onSelectionChange(undefined); + onInlineStyleChange(undefined); + }; + + const selection = editorState.getSelection(); + if (!selection.getHasFocus()) return resetState; + onSelectionChange(selection); + onInlineStyleChange(editorState.getCurrentInlineStyle()); + + return resetState; + }, [editorState, onSelectionChange, onInlineStyleChange]); + + const handleChange = useCallback( + (edited: EditorState) => { + setEditorState(edited); + const contentState = edited.getCurrentContent(); + onChange(contentState.hasText() ? stateToHTML(contentState) : ''); + }, + [onChange], + ); + + // Just because the library isn't properly typed + const extraProps = { onBlur, onFocus }; + + let classN = `RichEditor-editor`; + const contentState = editorState.getCurrentContent(); + if (!contentState.hasText()) { + if (contentState.getBlockMap().first().getType() !== 'unstyled') { + classN += ' RichEditor-hidePlaceholder'; + } + } + + const handleKeyCommand = useCallback((command: string, editorStat: EditorState): DraftHandleValue => { + const newState = RichUtils.handleKeyCommand(editorStat, command); + if (newState) { + handleChange(newState); + return 'handled'; + } + return 'not-handled'; + }, []); + + const mapKeyToEditorCommand = useCallback( + (e): DraftEditorCommand | null => { + if (e.keyCode === 9) { + const newEditorState = RichUtils.onTab(e, editorState, 4); + if (newEditorState === editorState) { + handleChange(newEditorState); + } + return null; + } + return getDefaultKeyBinding(e); + }, + [editorState], + ); + + const editorRef = useRef(); + + const focus = useCallback(() => { + editorRef.current.focus(); + }, []); + + return ( + <> + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */} +
+ +
+ + ); +}; +export default Editor; diff --git a/src/components/RichText/RichTextEditor.styles.scss b/src/components/FormPluginEditor/FormPluginEditor.styles.scss similarity index 100% rename from src/components/RichText/RichTextEditor.styles.scss rename to src/components/FormPluginEditor/FormPluginEditor.styles.scss diff --git a/src/components/FormPluginEditor/FormPluginEditor.tsx b/src/components/FormPluginEditor/FormPluginEditor.tsx new file mode 100644 index 00000000..b717f566 --- /dev/null +++ b/src/components/FormPluginEditor/FormPluginEditor.tsx @@ -0,0 +1,147 @@ +import { useContext, useState } from 'react'; +import createToolbarPlugin from '@draft-js-plugins/static-toolbar'; +import '@draft-js-plugins/static-toolbar/lib/plugin.css'; +import { + FormatBold, + FormatItalic, + FormatUnderlined, + FormatListBulleted, + FormatListNumbered, +} from '@mui/icons-material'; +import { SelectionState, DraftInlineStyle } from 'draft-js'; +import { Form } from 'antd'; + +import './FormPluginEditor.styles.scss'; +import DisabledContext from 'antd/es/config-provider/DisabledContext'; +import classNames from 'classnames'; +import { FormItemProps } from 'antd/lib/form/FormItem'; +import styles from './styles.module.scss'; +import createLinkPlugin, { LinkControl } from '../../utils/draftjs/linkPlugin'; +import createImagePlugin, { ImageControl } from '../../utils/draftjs/imagePlugin'; +import { PlaceholderControl } from '../../utils/draftjs/placeholderPlugin'; +import BlockStyleButton from './BlockStyleButton'; +import InlineStyleButton from './InlineStyleButton'; +import TextStyleSelect from './TextStyleSelect'; +import Editor from './Editor'; + +type FormEditorProps = { + name?: string | string[]; + placeholder: string; + disabled?: boolean; + className?: string; + placeholders?: { [key: string]: string }; + itemProps: FormItemProps; +}; + +const FormPluginEditor = ({ placeholder, className, placeholders, itemProps, name }: FormEditorProps) => { + // This state handling for plugins and toolbar is required because multiple editors will collide on the same page + const [{ plugins, Toolbar }] = useState(() => { + const toolbarPlugin = createToolbarPlugin(); + const linkPlugin = createLinkPlugin(); + const imagePlugin = createImagePlugin(); + const { Toolbar: ToolbarComponent } = toolbarPlugin; + return { + plugins: [toolbarPlugin, linkPlugin, imagePlugin], + Toolbar: ToolbarComponent, + }; + }); + + const disabled = useContext(DisabledContext); + const [selectionState, setSelectionState] = useState(); + const [currentInlineStyle, setCurrentInlineStyle] = useState(); + const [focused, setFocused] = useState(false); + + return ( +
+ {!disabled && ( +
+ + { + // may be use React.Fragment instead of div to improve perfomance after React 16 + (externalProps) => ( + <> +
+
+ +
+
+ + + + + + + + + +
+
+ + + + + + +
+
+ +
+
+ +
+
+ {Object.keys(placeholders || {}).length > 0 && ( + + )} + + ) + } +
+
+ )} + + + setFocused(false)} + onFocus={() => setFocused(true)} + editorPlugins={plugins} + /> + +
+ ); +}; + +export default FormPluginEditor; diff --git a/src/components/RichText/InlineStyleButton.tsx b/src/components/FormPluginEditor/InlineStyleButton.tsx similarity index 64% rename from src/components/RichText/InlineStyleButton.tsx rename to src/components/FormPluginEditor/InlineStyleButton.tsx index daad178f..63886aad 100644 --- a/src/components/RichText/InlineStyleButton.tsx +++ b/src/components/FormPluginEditor/InlineStyleButton.tsx @@ -1,7 +1,6 @@ -/* eslint-disable react/no-children-prop */ import clsx from 'clsx'; -import { EditorState, RichUtils } from 'draft-js'; -import React, { MouseEvent, ReactNode, useCallback, useMemo } from 'react'; +import { RichUtils } from 'draft-js'; +import React, { MouseEvent, ReactNode, useCallback } from 'react'; import { DraftJsStyleButtonProps } from '@draft-js-plugins/buttons'; import { Button } from 'antd'; import { ButtonType } from 'antd/lib/button/button'; @@ -13,7 +12,7 @@ interface ButtonProps extends DraftJsStyleButtonProps { interface CreateInlineStyleButtonProps extends Omit { inlineStyle: string; children: ReactNode; - editorState: EditorState; + active: boolean; buttonProps?: ButtonProps; } @@ -23,29 +22,25 @@ const InlineStyleButton = ({ theme, buttonProps, setEditorState, - editorState, + getEditorState, + active, }: CreateInlineStyleButtonProps) => { const toggleStyle = useCallback( (event: MouseEvent): void => { event.preventDefault(); - setEditorState(RichUtils.toggleInlineStyle(editorState, inlineStyle)); + setEditorState(RichUtils.toggleInlineStyle(getEditorState(), inlineStyle)); }, - [editorState, setEditorState], + [getEditorState, setEditorState], ); const preventBubblingUp = useCallback((event: MouseEvent): void => { event.preventDefault(); }, []); - const styleIsActive = useMemo((): boolean => { - return editorState && editorState.getCurrentInlineStyle().has(inlineStyle); - }, [editorState]); - - const className = styleIsActive ? clsx(theme.button, theme.active) : theme.button; + const className = active ? clsx(theme.button, theme.active) : theme.button; return ( ); }; export default InlineStyleButton; diff --git a/src/components/FormPluginEditor/TextStyleSelect.tsx b/src/components/FormPluginEditor/TextStyleSelect.tsx new file mode 100644 index 00000000..9ab9f9c7 --- /dev/null +++ b/src/components/FormPluginEditor/TextStyleSelect.tsx @@ -0,0 +1,61 @@ +import { ToolbarChildrenProps } from '@draft-js-plugins/static-toolbar/lib/components/Toolbar'; +import { convertToRaw, EditorState, Modifier, SelectionState } from 'draft-js'; +import { useTranslation } from 'react-i18next'; +import { useCallback, useMemo } from 'react'; +import { Select } from 'antd'; + +const TEXT_STYLES = [ + { label: 'rte.text', value: 'unstyled' }, + { label: 'rte.h1', value: 'header-one' }, + { label: 'rte.h2', value: 'header-two' }, + { label: 'rte.h3', value: 'header-three' }, + { label: 'rte.h4', value: 'header-four' }, + { label: 'rte.h5', value: 'header-five' }, + { label: 'rte.h6', value: 'header-six' }, +]; + +const TextStyleSelect = ({ + setEditorState, + getEditorState, + selectionState, +}: ToolbarChildrenProps & { selectionState: SelectionState }) => { + const { t } = useTranslation(); + + const blockType = useMemo( + () => + selectionState && + getEditorState().getCurrentContent().getBlockForKey(selectionState.getStartKey()).getType(), + [selectionState, getEditorState], + ); + + const handleToggle = useCallback( + (type: string) => { + setEditorState( + EditorState.push( + getEditorState(), + Modifier.setBlockType(getEditorState().getCurrentContent(), selectionState, type), + 'change-block-type', + ), + ); + }, + [setEditorState, getEditorState], + ); + + return ( + ({ - value, - label: typeof label === 'string' ? t(label) : label, - }))} - getPopupContainer={(element: HTMLElement) => element.parentElement} - /> - ); -}; - -const toolbarPlugin = createToolbarPlugin(); -const linkPlugin = createLinkPlugin(); -const imagePlugin = createImagePlugin(); - -const { Toolbar } = toolbarPlugin; - -const editorPlugins = [toolbarPlugin, linkPlugin, imagePlugin]; - -const RTE = ({ - value, - onChange, - placeholder, - disabled, - className, - onBlur, - onFocus, - placeholders, -}: RichTextEditorProps) => { - const plugins = useMemo(() => [...editorPlugins, createPlaceholderPlugin({ placeholders })], [placeholders]); - - const [editorState, setEditorState] = useState(() => { - const { contentBlocks, entityMap } = convertFromHTML(value); - const contentState = ContentState.createFromBlockArray(contentBlocks, entityMap); - return EditorState.createWithContent(contentState); - }); - const [selectionState, setSelectionState] = useState(() => editorState.getSelection()); - - useEffect(() => { - setEditorState((state) => { - const contentState = state.getCurrentContent(); - contentState.getAllEntities().forEach((entity, entityKey) => { - contentState.mergeEntityData(entityKey, { disabled }); - }); - return EditorState.createWithContent(contentState, state.getDecorator()); - }); - }, [disabled]); - - useEffect(() => { - const selection = editorState.getSelection(); - setSelectionState((state) => { - return selection.getHasFocus() ? selection : state; - }); - }, [editorState]); - - const handleChange = useCallback( - (edited: EditorState) => { - setEditorState(edited); - const contentState = edited.getCurrentContent(); - onChange(contentState.hasText() ? stateToHTML(contentState) : ''); - }, - [onChange], - ); - - // Just because the library isn't properly typed - const extraProps = { onBlur, onFocus }; - - let classN = `RichEditor-editor`; - const contentState = editorState.getCurrentContent(); - if (!contentState.hasText()) { - if (contentState.getBlockMap().first().getType() !== 'unstyled') { - classN += ' RichEditor-hidePlaceholder'; - } - } - - const handleKeyCommand = useCallback((command: string, editorStat: EditorState): DraftHandleValue => { - const newState = RichUtils.handleKeyCommand(editorStat, command); - if (newState) { - handleChange(newState); - return 'handled'; - } - return 'not-handled'; - }, []); - - const mapKeyToEditorCommand = useCallback( - (e): DraftEditorCommand | null => { - if (e.keyCode === 9) { - const newEditorState = RichUtils.onTab(e, editorState, 4); - if (newEditorState === editorState) { - handleChange(newEditorState); - } - return null; - } - return getDefaultKeyBinding(e); - }, - [editorState], - ); - - const editorRef = useRef(); - - const focus = useCallback(() => { - editorRef.current.focus(); - }, []); - - return ( -
- {!disabled && ( -
- - { - // may be use React.Fragment instead of div to improve perfomance after React 16 - (externalProps) => ( - <> -
-
- -
-
- - - - - - - - - -
-
- - - - - - -
-
- -
-
- -
-
- {Object.keys(placeholders || {}).length > 0 && ( - - )} - - ) - } -
-
- )} - - {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */} -
- -
-
- ); -}; - -export default RTE; diff --git a/src/components/Tenants/LegalSettings/components/LegalText/index.tsx b/src/components/Tenants/LegalSettings/components/LegalText/index.tsx index 692ff1c7..049e1875 100644 --- a/src/components/Tenants/LegalSettings/components/LegalText/index.tsx +++ b/src/components/Tenants/LegalSettings/components/LegalText/index.tsx @@ -1,12 +1,13 @@ import set from 'lodash.set'; import { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { CardEditable } from '../../../../CardEditable'; -import { FormRichTextEditorField } from '../../../../FormRichTextEditorField'; import { Modal, ModalProps } from '../../../../Modal'; import { TranslatableFormField } from '../../../../TranslatableFormField'; import { useSingleTenantData } from '../../../../../hooks/useSingleTenantData'; import { useTenantAdminDataMutation } from '../../../../../hooks/useTenantAdminDataMutation.hook'; import styles from './styles.module.scss'; +import FormPluginEditor from '../../../../FormPluginEditor/FormPluginEditor'; interface LegalTextProps { tenantId: string | number; @@ -27,6 +28,7 @@ export const LegalText = ({ showConfirmationModal, placeholders, }: LegalTextProps) => { + const { t } = useTranslation(); const { data, isLoading } = useSingleTenantData({ id: tenantId }); const { mutate: updateTenant } = useTenantAdminDataMutation({ id: tenantId }); const [formDataContent, setFormData] = useState>(); @@ -61,7 +63,13 @@ export const LegalText = ({ }} > - + {showConfirmationModal && modalVisible && ( diff --git a/src/components/TranslatableFormField/index.tsx b/src/components/TranslatableFormField/index.tsx index def7491f..7061fca9 100644 --- a/src/components/TranslatableFormField/index.tsx +++ b/src/components/TranslatableFormField/index.tsx @@ -4,6 +4,7 @@ import { cloneElement, useContext, useMemo } from 'react'; import { CheckCircleTwoTone, WarningTwoTone } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; import DisabledContext from 'antd/es/config-provider/DisabledContext'; +import classNames from 'classnames'; import { SelectFormField } from '../SelectFormField'; import { useTenantAdminData } from '../../hooks/useTenantAdminData.hook'; import styles from './styles.module.scss'; @@ -62,10 +63,16 @@ export const TranslatableFormField = ({ name, children }: TranslatableFormFieldP )} - {cloneElement(children, { - name: [...namePath, fieldData?.translate || languages[0]], - key: fieldData?.translate || languages[0], - })} + {languages.map((language) => + cloneElement(children, { + name: [...namePath, language], + key: language, + className: classNames({ + [styles.activeLanguage]: fieldData?.translate === language || languages.length === 1, + [styles.notActive]: fieldData?.translate !== language && languages.length !== 1, + }), + }), + )} ); }; diff --git a/src/components/TranslatableFormField/styles.module.scss b/src/components/TranslatableFormField/styles.module.scss index 05fcfad7..87ef31c7 100644 --- a/src/components/TranslatableFormField/styles.module.scss +++ b/src/components/TranslatableFormField/styles.module.scss @@ -1,3 +1,11 @@ +.activeLanguage { + display: block; +} + +.notActive { + display: none; +} + .containerLabel { display: flex; flex-grow: 1; diff --git a/src/pages/Agency/Edit/index.tsx b/src/pages/Agency/Edit/index.tsx index e272348a..135312dc 100644 --- a/src/pages/Agency/Edit/index.tsx +++ b/src/pages/Agency/Edit/index.tsx @@ -176,7 +176,7 @@ export const AgencyPageEdit = () => { - {isEditing && isEnabled(FeatureFlag.CentralDataProtectionTemplate) && ( + {isEnabled(FeatureFlag.CentralDataProtectionTemplate) && (

diff --git a/src/utils/draftjs/imagePlugin/Control/Image.tsx b/src/utils/draftjs/imagePlugin/Control/Image.tsx index 88ba186e..d18d0766 100644 --- a/src/utils/draftjs/imagePlugin/Control/Image.tsx +++ b/src/utils/draftjs/imagePlugin/Control/Image.tsx @@ -71,18 +71,16 @@ const ImageAttributes = ({ export const ImageControl = ({ setEditorState, getEditorState, - editorState, selectionState, -}: ToolbarChildrenProps & { editorState: EditorState; selectionState: SelectionState }) => { +}: ToolbarChildrenProps & { selectionState: SelectionState }) => { const [showTooltip, setShowTooltip] = useState(false); - const [disabled, setDisabled] = useState(!editorState.getSelection().isCollapsed()); + const [disabled, setDisabled] = useState(!selectionState || !selectionState.isCollapsed()); useEffect(() => { - const selection = editorState.getSelection(); setDisabled((state) => { - return selection.getHasFocus() ? !selection.isCollapsed() : state; + return selectionState && selectionState.getHasFocus() ? !selectionState.isCollapsed() : state; }); - }, [editorState]); + }, [selectionState]); return (
diff --git a/src/utils/draftjs/linkPlugin/Control/Link.tsx b/src/utils/draftjs/linkPlugin/Control/Link.tsx index 4c18c600..bfe78112 100644 --- a/src/utils/draftjs/linkPlugin/Control/Link.tsx +++ b/src/utils/draftjs/linkPlugin/Control/Link.tsx @@ -92,29 +92,28 @@ const LinkAttributes = ({ export const LinkControl = ({ setEditorState, getEditorState, - editorState, selectionState, -}: ToolbarChildrenProps & { editorState: EditorState; selectionState: SelectionState }) => { +}: ToolbarChildrenProps & { selectionState: SelectionState }) => { const [showTooltip, setShowTooltip] = useState(false); - const [disabled, setDisabled] = useState(editorState.getSelection().isCollapsed()); + const [disabled, setDisabled] = useState(!selectionState || selectionState.isCollapsed()); useEffect(() => { - const selection = editorState.getSelection(); setDisabled((state) => { - return selection.getHasFocus() ? selection.isCollapsed() : state; + return selectionState && selectionState.getHasFocus() ? selectionState.isCollapsed() : state; }); - }, [editorState]); + }, [selectionState]); const linkEntity = useMemo(() => { - const contentState = editorState.getCurrentContent(); - const block = editorState.getCurrentContent().getBlockForKey(selectionState.getStartKey()); + if (!selectionState) return null; + const contentState = getEditorState().getCurrentContent(); + const block = contentState.getBlockForKey(selectionState.getStartKey()); const entityKey = block.getEntityAt(selectionState.getStartOffset()); return entityKey && contentState.getEntity(entityKey).getType() === 'LINK' ? entityKey : null; - }, [editorState, selectionState]); + }, [selectionState, getEditorState]); const handleDelete = useCallback( (entityKey: string) => { - const contentState = editorState.getCurrentContent(); + const contentState = getEditorState().getCurrentContent(); const block = contentState.getBlockForKey(selectionState.getStartKey()); block.findEntityRanges( (character) => entityKey === character.getEntity(), @@ -126,7 +125,7 @@ export const LinkControl = ({ setEditorState( EditorState.push( - editorState, + getEditorState(), Modifier.applyEntity(contentState, selection, null), 'apply-entity', ), @@ -134,7 +133,7 @@ export const LinkControl = ({ }, ); }, - [editorState], + [selectionState, getEditorState, setEditorState], ); return ( diff --git a/src/utils/draftjs/placeholderPlugin/Control/Placeholder.tsx b/src/utils/draftjs/placeholderPlugin/Control/Placeholder.tsx index ec3d24b4..10fc77bb 100644 --- a/src/utils/draftjs/placeholderPlugin/Control/Placeholder.tsx +++ b/src/utils/draftjs/placeholderPlugin/Control/Placeholder.tsx @@ -9,21 +9,20 @@ export const PlaceholderControl = ({ placeholders, setEditorState, getEditorState, - editorState, selectionState, -}: ToolbarChildrenProps & { placeholders: any; editorState: EditorState; selectionState: SelectionState }) => { +}: ToolbarChildrenProps & { placeholders: any; selectionState: SelectionState }) => { const { t } = useTranslation(); - const [disabled, setDisabled] = useState(!editorState.getSelection().isCollapsed()); + const [disabled, setDisabled] = useState(!selectionState || !selectionState.isCollapsed()); useEffect(() => { - const selection = editorState.getSelection(); setDisabled((state) => { - return selection.getHasFocus() ? !selection.isCollapsed() : state; + return selectionState && selectionState.getHasFocus() ? !selectionState.isCollapsed() : state; }); - }, [editorState]); + }, [selectionState]); const insertPlaceholder = useCallback( (key: string) => { + if (!selectionState) return; const state = getEditorState(); const selection = SelectionState.createEmpty(selectionState.getStartKey()).merge({ anchorOffset: selectionState.getAnchorOffset(),