fix(BlockEditor): close slash menu when clicking outside or on different block
- add `handleFocus` in Block to reopen slash menu on focus if content starts with `/` - add `outerRef` on editor root div to detect outside clicks - add `useEffect` with document `mousedown` listener to close slash menu when clicking outside the editor or on a different block - add `data-slash-menu` attribute on SlashMenu to exclude it from the close trigger
This commit is contained in:
@@ -205,6 +205,17 @@ const Block = forwardRef(function Block(
|
||||
}
|
||||
}
|
||||
|
||||
function handleFocus() {
|
||||
if (def?.isText && !disabled) {
|
||||
const el = editableRef.current;
|
||||
const text = el?.textContent ?? '';
|
||||
if (text.startsWith('/') && !text.slice(1).includes(' ')) {
|
||||
onSlashOpen?.({ blockId: block.id, query: text.slice(1) });
|
||||
}
|
||||
}
|
||||
onFocus?.(block.id);
|
||||
}
|
||||
|
||||
function handlePaste(e) {
|
||||
// MVP : on colle uniquement du texte brut pour éviter le HTML externe.
|
||||
e.preventDefault();
|
||||
@@ -324,7 +335,7 @@ const Block = forwardRef(function Block(
|
||||
onInput={handleInput}
|
||||
onKeyDown={handleKeyDown}
|
||||
onPaste={handlePaste}
|
||||
onFocus={() => onFocus?.(block.id)}
|
||||
onFocus={handleFocus}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
@@ -336,7 +347,7 @@ const Block = forwardRef(function Block(
|
||||
onInput={handleInput}
|
||||
onKeyDown={handleKeyDown}
|
||||
onPaste={handlePaste}
|
||||
onFocus={() => onFocus?.(block.id)}
|
||||
onFocus={handleFocus}
|
||||
/>
|
||||
)
|
||||
) : def.Component ? (
|
||||
|
||||
@@ -71,6 +71,7 @@ export default function BlockEditor({
|
||||
const blocks = useMemo(() => ensureNonEmpty(value), [value]);
|
||||
const blockRefs = useRef(new Map());
|
||||
const containerRef = useRef(null);
|
||||
const outerRef = useRef(null);
|
||||
const [focusBlockId, setFocusBlockId] = useState(null);
|
||||
const [focusOffset, setFocusOffset] = useState(null);
|
||||
const [selectedBlockIds, setSelectedBlockIds] = useState(() => new Set());
|
||||
@@ -764,6 +765,26 @@ export default function BlockEditor({
|
||||
selectAllBlocks();
|
||||
}
|
||||
|
||||
// Ferme le slash menu quand on clique en dehors de l'éditeur ou sur un
|
||||
// bloc différent de celui qui a ouvert le menu.
|
||||
useEffect(() => {
|
||||
if (!slashState) return;
|
||||
function onDocMouseDown(e) {
|
||||
if (e.target instanceof Element && e.target.closest('[data-slash-menu]')) return;
|
||||
const outer = outerRef.current;
|
||||
if (!outer || !outer.contains(e.target)) {
|
||||
setSlashState(null);
|
||||
return;
|
||||
}
|
||||
const blockEl = e.target instanceof Element ? e.target.closest('[data-block-id]') : null;
|
||||
if (!blockEl || blockEl.getAttribute('data-block-id') !== slashState.blockId) {
|
||||
setSlashState(null);
|
||||
}
|
||||
}
|
||||
document.addEventListener('mousedown', onDocMouseDown, true);
|
||||
return () => document.removeEventListener('mousedown', onDocMouseDown, true);
|
||||
}, [slashState]);
|
||||
|
||||
// Le slash menu utilise un listener au niveau document en phase capture pour
|
||||
// intercepter les touches avant que le contentEditable ne gère ses défauts
|
||||
// (sinon ↑/↓ déplacent le caret et Entrée insère une nouvelle ligne).
|
||||
@@ -804,7 +825,7 @@ export default function BlockEditor({
|
||||
}, [slashState, enabledBlocks]);
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div ref={outerRef} className="space-y-2">
|
||||
{label && (
|
||||
<label className="block text-xs font-medium text-neutral-700 dark:text-neutral-400">
|
||||
{label}
|
||||
|
||||
@@ -120,6 +120,7 @@ export default function SlashMenu({
|
||||
return (
|
||||
<div
|
||||
ref={listRef}
|
||||
data-slash-menu
|
||||
className="fixed z-50 overflow-y-auto rounded-lg border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-900 shadow-md py-1"
|
||||
style={{ top, left, width: MENU_WIDTH, maxHeight }}
|
||||
onMouseDown={(e) => e.preventDefault()} // ne pas voler le focus
|
||||
|
||||
Reference in New Issue
Block a user