From 9bfcadd95f4c3917ff4aa4fd4b10bfa7ce4c6c09 Mon Sep 17 00:00:00 2001 From: Dan Brown <ssddanbrown@googlemail.com> Date: Fri, 28 Mar 2025 14:17:52 +0000 Subject: [PATCH] Lexical: Improved navigation around images/media - Added specific handling to move/insert-up/down on arrow press. - Prevented resize overlay from interrupting image node focus. --- .../lexical/rich-text/LexicalImageNode.ts | 2 +- .../js/wysiwyg/services/keyboard-handling.ts | 43 +++++++++++++++++-- resources/sass/_editor.scss | 2 + 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/resources/js/wysiwyg/lexical/rich-text/LexicalImageNode.ts b/resources/js/wysiwyg/lexical/rich-text/LexicalImageNode.ts index 9f42ad732..40f4ab711 100644 --- a/resources/js/wysiwyg/lexical/rich-text/LexicalImageNode.ts +++ b/resources/js/wysiwyg/lexical/rich-text/LexicalImageNode.ts @@ -133,7 +133,7 @@ export class ImageNode extends ElementNode { element.addEventListener('click', e => { _editor.update(() => { - $selectSingleNode(this); + this.select(); }); }); diff --git a/resources/js/wysiwyg/services/keyboard-handling.ts b/resources/js/wysiwyg/services/keyboard-handling.ts index ff6117b2b..0ef0b81bf 100644 --- a/resources/js/wysiwyg/services/keyboard-handling.ts +++ b/resources/js/wysiwyg/services/keyboard-handling.ts @@ -3,7 +3,7 @@ import { $createParagraphNode, $getSelection, $isDecoratorNode, - COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, + COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, KEY_ENTER_COMMAND, KEY_TAB_COMMAND, @@ -43,7 +43,7 @@ function deleteSingleSelectedNode(editor: LexicalEditor) { } /** - * Insert a new empty node after the selection if the selection contains a single + * Insert a new empty node before/after the selection if the selection contains a single * selected node (like image, media etc...). */ function insertAfterSingleSelectedNode(editor: LexicalEditor, event: KeyboardEvent|null): boolean { @@ -67,6 +67,37 @@ function insertAfterSingleSelectedNode(editor: LexicalEditor, event: KeyboardEve return false; } +function focusAdjacentOrInsertForSingleSelectNode(editor: LexicalEditor, event: KeyboardEvent|null, after: boolean = true): boolean { + const selectionNodes = getLastSelection(editor)?.getNodes() || []; + if (!isSingleSelectedNode(selectionNodes)) { + return false; + } + + event?.preventDefault(); + + const node = selectionNodes[0]; + const nearestBlock = $getNearestNodeBlockParent(node) || node; + let target = after ? nearestBlock.getNextSibling() : nearestBlock.getPreviousSibling(); + + requestAnimationFrame(() => { + editor.update(() => { + if (!target) { + target = $createParagraphNode(); + if (after) { + nearestBlock.insertAfter(target) + } else { + nearestBlock.insertBefore(target); + } + } + + target.selectStart(); + }); + }); + + + return true; +} + /** * Insert a new node after a details node, if inside a details node that's * the last element, and if the cursor is at the last block within the details node. @@ -199,8 +230,13 @@ export function registerKeyboardHandling(context: EditorUiContext): () => void { return handleInsetOnTab(context.editor, event); }, COMMAND_PRIORITY_LOW); + const unregisterUp = context.editor.registerCommand(KEY_ARROW_UP_COMMAND, (event): boolean => { + return focusAdjacentOrInsertForSingleSelectNode(context.editor, event, false); + }, COMMAND_PRIORITY_LOW); + const unregisterDown = context.editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (event): boolean => { - return insertAfterDetails(context.editor, event); + return insertAfterDetails(context.editor, event) + || focusAdjacentOrInsertForSingleSelectNode(context.editor, event, true) }, COMMAND_PRIORITY_LOW); return () => { @@ -208,6 +244,7 @@ export function registerKeyboardHandling(context: EditorUiContext): () => void { unregisterDelete(); unregisterEnter(); unregisterTab(); + unregisterUp(); unregisterDown(); }; } \ No newline at end of file diff --git a/resources/sass/_editor.scss b/resources/sass/_editor.scss index 9f7694e85..35f11c5a2 100644 --- a/resources/sass/_editor.scss +++ b/resources/sass/_editor.scss @@ -370,8 +370,10 @@ body.editor-is-fullscreen { display: inline-block; outline: 2px dashed var(--editor-color-primary); direction: ltr; + pointer-events: none; } .editor-node-resizer-handle { + pointer-events: auto; position: absolute; display: block; width: 10px;