Skip to content

Commit 4d5e9fc

Browse files
committed
Lexical: Fixed re-focus after modal in some cases
Updated code editor display so the editor area actually gains focus again. Updated modal handling to delegate focus handling to the editor if focus was on the edit area before, to prevent cursor jumps to the start.
1 parent 13a1883 commit 4d5e9fc

5 files changed

Lines changed: 53 additions & 24 deletions

File tree

resources/js/wysiwyg/lexical/rich-text/LexicalCodeBlockNode.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {EditorConfig} from "lexical/LexicalEditor";
1111
import {EditorDecoratorAdapter} from "../../ui/framework/decorator";
1212
import {CodeEditor} from "../../../components";
1313
import {el} from "../../utils/dom";
14+
import {focusEditor} from "../../utils/actions";
1415

1516
export type SerializedCodeBlockNode = Spread<{
1617
language: string;
@@ -197,8 +198,9 @@ export function $openCodeEditorForNode(editor: LexicalEditor, node: CodeBlockNod
197198
node.setCode(newCode);
198199
node.setLanguage(newLang);
199200
});
200-
// TODO - Re-focus
201+
202+
focusEditor(editor);
201203
}, () => {
202-
// TODO - Re-focus
204+
focusEditor(editor);
203205
});
204206
}

resources/js/wysiwyg/services/keyboard-handling.ts

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,32 @@ import {EditorUiContext} from "../ui/framework/core";
22
import {
33
$createParagraphNode,
44
$getSelection,
5-
$isDecoratorNode,
65
COMMAND_PRIORITY_LOW, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_UP_COMMAND,
76
KEY_BACKSPACE_COMMAND,
87
KEY_DELETE_COMMAND,
98
KEY_ENTER_COMMAND, KEY_TAB_COMMAND,
109
LexicalEditor,
1110
LexicalNode
1211
} from "lexical";
13-
import {$isImageNode} from "@lexical/rich-text/LexicalImageNode";
14-
import {$isMediaNode} from "@lexical/rich-text/LexicalMediaNode";
1512
import {getLastSelection} from "../utils/selection";
16-
import {$getNearestNodeBlockParent, $getParentOfType, $selectOrCreateAdjacent} from "../utils/nodes";
13+
import {
14+
$getNearestNodeBlockParent,
15+
$getParentOfType,
16+
$isSingleSelectableNode,
17+
$selectOrCreateAdjacent
18+
} from "../utils/nodes";
1719
import {$setInsetForSelection} from "../utils/lists";
1820
import {$isListItemNode} from "@lexical/list";
1921
import {$isDetailsNode, DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
20-
import {$isDiagramNode} from "../utils/diagrams";
2122
import {$unwrapDetailsNode} from "../utils/details";
2223

23-
function isSingleSelectedNode(nodes: LexicalNode[]): boolean {
24-
if (nodes.length === 1) {
25-
const node = nodes[0];
26-
if ($isDecoratorNode(node) || $isImageNode(node) || $isMediaNode(node) || $isDiagramNode(node)) {
27-
return true;
28-
}
29-
}
30-
31-
return false;
32-
}
33-
3424
/**
3525
* Delete the current node in the selection if the selection contains a single
3626
* selected node (like image, media etc...).
3727
*/
3828
function deleteSingleSelectedNode(editor: LexicalEditor) {
3929
const selectionNodes = getLastSelection(editor)?.getNodes() || [];
40-
if (isSingleSelectedNode(selectionNodes)) {
30+
if ($isSingleSelectableNode(selectionNodes)) {
4131
editor.update(() => {
4232
selectionNodes[0].remove();
4333
});
@@ -50,7 +40,7 @@ function deleteSingleSelectedNode(editor: LexicalEditor) {
5040
*/
5141
function insertAdjacentToSingleSelectedNode(editor: LexicalEditor, event: KeyboardEvent|null): boolean {
5242
const selectionNodes = getLastSelection(editor)?.getNodes() || [];
53-
if (isSingleSelectedNode(selectionNodes)) {
43+
if ($isSingleSelectableNode(selectionNodes)) {
5444
const node = selectionNodes[0];
5545
const nearestBlock = $getNearestNodeBlockParent(node) || node;
5646
const insertBefore = event?.shiftKey === true;
@@ -76,7 +66,7 @@ function insertAdjacentToSingleSelectedNode(editor: LexicalEditor, event: Keyboa
7666

7767
function focusAdjacentOrInsertForSingleSelectNode(editor: LexicalEditor, event: KeyboardEvent|null, after: boolean = true): boolean {
7868
const selectionNodes = getLastSelection(editor)?.getNodes() || [];
79-
if (!isSingleSelectedNode(selectionNodes)) {
69+
if (!$isSingleSelectableNode(selectionNodes)) {
8070
return false;
8171
}
8272

resources/js/wysiwyg/ui/framework/modals.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ export class EditorFormModal extends EditorContainerUiElement {
3939
hide() {
4040
this.getContext().manager.setModalInactive(this.key);
4141
this.teardown();
42-
if (this.originalFocus instanceof HTMLElement && this.originalFocus.isConnected) {
42+
43+
if (this.originalFocus === this.getContext().editorDOM) {
44+
this.getContext().editor.focus();
45+
} else if (this.originalFocus instanceof HTMLElement && this.originalFocus.isConnected) {
4346
this.originalFocus.focus();
4447
}
4548
}

resources/js/wysiwyg/utils/actions.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
1-
import {$createParagraphNode, $getRoot, $getSelection, $insertNodes, $isBlockElementNode, LexicalEditor} from "lexical";
1+
import {
2+
$createParagraphNode,
3+
$getRoot,
4+
$getSelection,
5+
$insertNodes,
6+
$isBlockElementNode,
7+
LexicalEditor,
8+
} from "lexical";
29
import {$generateHtmlFromNodes} from "@lexical/html";
3-
import {$getAllNodesOfType, $getNearestNodeBlockParent, $htmlToBlockNodes, $htmlToNodes} from "./nodes";
10+
import {
11+
$getAllNodesOfType,
12+
$getNearestNodeBlockParent,
13+
$htmlToBlockNodes,
14+
$htmlToNodes,
15+
$isSingleSelectableNode
16+
} from "./nodes";
417
import {$isHeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
518

619
export function setEditorContentFromHtml(editor: LexicalEditor, html: string) {
@@ -99,6 +112,10 @@ export function focusEditor(editor: LexicalEditor): void {
99112
if (firstChild && !selection) {
100113
firstChild.selectStart();
101114
}
115+
116+
if ($isSingleSelectableNode(selection?.getNodes() || [])) {
117+
editor._rootElement?.focus();
118+
}
102119
});
103120
editor.commitUpdates();
104121
editor.focus(() => {}, {defaultSelection: "rootStart"});

resources/js/wysiwyg/utils/nodes.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import {$generateNodesFromDOM} from "@lexical/html";
1414
import {htmlToDom} from "./dom";
1515
import {NodeHasAlignment, NodeHasInset} from "lexical/nodes/common";
1616
import {$findMatchingParent} from "@lexical/utils";
17+
import {$isImageNode} from "@lexical/rich-text/LexicalImageNode";
18+
import {$isMediaNode} from "@lexical/rich-text/LexicalMediaNode";
19+
import {$isDiagramNode} from "./diagrams";
1720

1821
function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
1922
return nodes.map(node => {
@@ -159,6 +162,20 @@ export function $selectOrCreateAdjacent(node: LexicalNode, after: boolean): Rang
159162
return after ? target.selectStart() : target.selectEnd();
160163
}
161164

165+
/**
166+
* Check if the range of nodes represents a single node which is wholly selectable.
167+
*/
168+
export function $isSingleSelectableNode(nodes: LexicalNode[]): boolean {
169+
if (nodes.length === 1) {
170+
const node = nodes[0];
171+
if ($isDecoratorNode(node) || $isImageNode(node) || $isMediaNode(node) || $isDiagramNode(node)) {
172+
return true;
173+
}
174+
}
175+
176+
return false;
177+
}
178+
162179
export function nodeHasAlignment(node: object): node is NodeHasAlignment {
163180
return '__alignment' in node;
164181
}

0 commit comments

Comments
 (0)