From 7b6bf67f36df1b90b2a7aa9b9750ed701de2068d Mon Sep 17 00:00:00 2001 From: Hyko Date: Sat, 25 Apr 2026 18:30:02 -0400 Subject: [PATCH] fix(ui): keep inline toolbar visible when popover is open - add `toolbarPinnedRef` in BlockEditor to prevent toolbar from closing while a popover is focused - pass `onPinChange` callback to InlineToolbar to toggle the pinned state - notify parent via `onPinChange` whenever popover state changes - remove rect-change effect that was incorrectly closing popovers on selection update --- src/shared/components/BlockEditor/BlockEditor.client.js | 6 ++++++ .../components/BlockEditor/inline/Toolbar.client.js | 8 +++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/shared/components/BlockEditor/BlockEditor.client.js b/src/shared/components/BlockEditor/BlockEditor.client.js index e251372..3317bd4 100644 --- a/src/shared/components/BlockEditor/BlockEditor.client.js +++ b/src/shared/components/BlockEditor/BlockEditor.client.js @@ -441,12 +441,17 @@ export default function BlockEditor({ // qu'elle se trouve dans un contentEditable de l'éditeur. const [toolbar, setToolbar] = useState(null); // { blockId, start, end, rect, marks } + const toolbarPinnedRef = useRef(false); const updateToolbar = useCallback(() => { if (disabled) { setToolbar(null); return; } + // Quand un popover du toolbar est ouvert (lien, couleur…), le focus + // passe sur l'input/bouton et la sélection peut paraître hors-bloc : + // on garde la toolbar visible. + if (toolbarPinnedRef.current) return; const sel = typeof window !== 'undefined' ? window.getSelection() : null; if (!sel || sel.rangeCount === 0 || sel.isCollapsed) { setToolbar(null); @@ -837,6 +842,7 @@ export default function BlockEditor({ rect={toolbar.rect} activeMarks={marks} onToggleMark={applyToggleMark} + onPinChange={(p) => { toolbarPinnedRef.current = p; }} /> ); })()} diff --git a/src/shared/components/BlockEditor/inline/Toolbar.client.js b/src/shared/components/BlockEditor/inline/Toolbar.client.js index 34027ef..a03c305 100644 --- a/src/shared/components/BlockEditor/inline/Toolbar.client.js +++ b/src/shared/components/BlockEditor/inline/Toolbar.client.js @@ -22,12 +22,16 @@ const SIMPLE_BUTTONS = [ { type: 'code', label: '', title: 'Code (Ctrl+E)', className: 'font-mono text-[11px]' }, ]; -export default function InlineToolbar({ rect, activeMarks, onToggleMark }) { +export default function InlineToolbar({ rect, activeMarks, onToggleMark, onPinChange }) { const ref = useRef(null); const [pos, setPos] = useState({ top: 0, left: 0, flipped: false }); const [popover, setPopover] = useState(null); // 'color' | 'highlight' | 'link' | null const [linkUrl, setLinkUrl] = useState(''); + useEffect(() => { + onPinChange?.(popover !== null); + }, [popover, onPinChange]); + useLayoutEffect(() => { if (!rect || typeof window === 'undefined') return; const width = ref.current?.offsetWidth ?? 280; @@ -47,8 +51,6 @@ export default function InlineToolbar({ rect, activeMarks, onToggleMark }) { setPos({ top, left, flipped: flipBelow }); }, [rect]); - // Ferme un popover ouvert quand la sélection change (rect change). - useEffect(() => { setPopover(null); }, [rect?.top, rect?.left]); function isActive(type, payloadKey) { if (!Array.isArray(activeMarks)) return false;