diff --git a/.changeset/short-squids-remember.md b/.changeset/short-squids-remember.md new file mode 100644 index 000000000..6544cb1f9 --- /dev/null +++ b/.changeset/short-squids-remember.md @@ -0,0 +1,5 @@ +--- +'slate-react': minor +--- + +Support selection in readOnly=true editors. diff --git a/packages/slate-react/src/components/android/android-editable.tsx b/packages/slate-react/src/components/android/android-editable.tsx index 2f42bd058..7ccab2ab7 100644 --- a/packages/slate-react/src/components/android/android-editable.tsx +++ b/packages/slate-react/src/components/android/android-editable.tsx @@ -234,11 +234,7 @@ export const AndroidEditable = (props: EditableProps): JSX.Element => { const onDOMSelectionChange = useCallback( throttle(() => { try { - if ( - !readOnly && - !state.isUpdatingSelection && - !inputManager.isReconciling.current - ) { + if (!state.isUpdatingSelection && !inputManager.isReconciling.current) { const root = ReactEditor.findDocumentOrShadowRoot(editor) const { activeElement } = root const el = ReactEditor.toDOMNode(editor, editor) diff --git a/packages/slate-react/src/components/editable.tsx b/packages/slate-react/src/components/editable.tsx index 03ebe1849..783da8998 100644 --- a/packages/slate-react/src/components/editable.tsx +++ b/packages/slate-react/src/components/editable.tsx @@ -478,7 +478,6 @@ export const Editable = (props: EditableProps) => { const onDOMSelectionChange = useCallback( throttle(() => { if ( - !readOnly && !state.isComposing && !state.isUpdatingSelection && !state.isDraggingInternally @@ -585,7 +584,12 @@ export const Editable = (props: EditableProps) => { } data-slate-editor data-slate-node="value" - contentEditable={readOnly ? undefined : true} + // explicitly set this + contentEditable={!readOnly} + // in some cases, a decoration needs access to the range / selection to decorate a text node, + // then you will select the whole text node when you select part the of text + // this magic zIndex="-1" will fix it + zindex={-1} suppressContentEditableWarning ref={ref} style={{ diff --git a/packages/slate-react/src/plugin/react-editor.ts b/packages/slate-react/src/plugin/react-editor.ts index b07e5e3aa..9bf74b2fd 100644 --- a/packages/slate-react/src/plugin/react-editor.ts +++ b/packages/slate-react/src/plugin/react-editor.ts @@ -216,9 +216,12 @@ export const ReactEditor = { return ( targetEl.closest(`[data-slate-editor]`) === editorEl && - (!editable || - targetEl.isContentEditable || - !!targetEl.getAttribute('data-slate-zero-width')) + (!editable || targetEl.isContentEditable + ? true + : (typeof targetEl.isContentEditable === 'boolean' && // isContentEditable exists only on HTMLElement, and on other nodes it will be undefined + // this is the core logic that lets you know you got the right editor.selection instead of null when editor is contenteditable="false"(readOnly) + targetEl.closest('[contenteditable="false"]') === editorEl) || + !!targetEl.getAttribute('data-slate-zero-width')) ) },