fix: sync Editor editable state with readOnly prop (#523)
The rich-text Editor (used for card descriptions) creates its Tiptap instance once via useEditor with an empty dependency array. This means the initial value of `editable: !readOnly` is captured at mount time and never updated, and the onChange/onBlur callbacks are frozen as the first-render closures. In CardPage, the description Editor mounts as soon as the card query resolves but before the permissions query resolves, so `readOnly` is `true` and onChange/onBlur are `undefined` at that moment. When permissions resolve a moment later and `canEdit` flips to `true`, the Editor never becomes editable — leaving the description stuck read-only even for workspace admins. Fix mirrors the existing pattern in PlainTextEditor.tsx: - Use refs for onChange/onBlur so the editor reads the latest values - Add a useEffect that calls editor.setEditable(!readOnly) when the readOnly prop changes Co-authored-by: Jay <CodeEngineering@pm.me>
This commit is contained in:
@@ -457,6 +457,17 @@ export default function Editor({
|
|||||||
}) {
|
}) {
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
// useEditor is created once (empty deps below), so keep the latest callbacks
|
||||||
|
// in refs to avoid the editor capturing stale closures on re-render.
|
||||||
|
const onChangeRef = useRef(onChange);
|
||||||
|
const onBlurRef = useRef(onBlur);
|
||||||
|
useEffect(() => {
|
||||||
|
onChangeRef.current = onChange;
|
||||||
|
}, [onChange]);
|
||||||
|
useEffect(() => {
|
||||||
|
onBlurRef.current = onBlur;
|
||||||
|
}, [onBlur]);
|
||||||
|
|
||||||
const editor = useEditor(
|
const editor = useEditor(
|
||||||
{
|
{
|
||||||
extensions: [
|
extensions: [
|
||||||
@@ -553,7 +564,7 @@ export default function Editor({
|
|||||||
...(enableYouTubeEmbed ? [YouTubeNode] : []),
|
...(enableYouTubeEmbed ? [YouTubeNode] : []),
|
||||||
],
|
],
|
||||||
content,
|
content,
|
||||||
onUpdate: ({ editor }) => onChange?.(editor.getHTML()),
|
onUpdate: ({ editor }) => onChangeRef.current?.(editor.getHTML()),
|
||||||
onBlur: ({ event }) => {
|
onBlur: ({ event }) => {
|
||||||
if (
|
if (
|
||||||
document
|
document
|
||||||
@@ -563,7 +574,7 @@ export default function Editor({
|
|||||||
return;
|
return;
|
||||||
// Only trigger onBlur if the click was outside both the editor and menu
|
// Only trigger onBlur if the click was outside both the editor and menu
|
||||||
if (!containerRef.current?.contains(event.relatedTarget as Node)) {
|
if (!containerRef.current?.contains(event.relatedTarget as Node)) {
|
||||||
onBlur?.();
|
onBlurRef.current?.();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editorProps: {
|
editorProps: {
|
||||||
@@ -587,6 +598,16 @@ export default function Editor({
|
|||||||
}
|
}
|
||||||
}, [content, editor]);
|
}, [content, editor]);
|
||||||
|
|
||||||
|
// useEditor captures `readOnly` once at creation time (empty deps above), so
|
||||||
|
// explicitly sync `editable` when the prop changes. Without this the editor
|
||||||
|
// gets stuck read-only when `readOnly` flips from true to false after mount
|
||||||
|
// (e.g. card permissions resolving slower than the card data on first load).
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editor) return;
|
||||||
|
if (editor.isEditable === !readOnly) return;
|
||||||
|
editor.setEditable(!readOnly);
|
||||||
|
}, [editor, readOnly]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef}>
|
<div ref={containerRef}>
|
||||||
<style jsx global>{`
|
<style jsx global>{`
|
||||||
|
|||||||
Reference in New Issue
Block a user