Skip to content

Commit 2a32475

Browse files
committed
Lexical: Made a range of selection improvements
Updated up/down handling to create where a selection candidate does not exist, to apply to a wider scenario via the selectPrevious/Next methods. Updated DOM selection change handling to identify single selections within decorated nodes to select them in full, instead of losing selection due to partial selection of their contents. Updated table selection handling so that our colgroups are ignored for internal selection focus handling.
1 parent 1243108 commit 2a32475

File tree

6 files changed

+46
-19
lines changed

6 files changed

+46
-19
lines changed

resources/js/wysiwyg/lexical/core/LexicalNode.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
internalMarkNodeAsDirty,
4949
removeFromParent,
5050
} from './LexicalUtils';
51+
import {$insertAndSelectNewEmptyAdjacentNode} from "../../utils/nodes";
5152

5253
export type NodeMap = Map<NodeKey, LexicalNode>;
5354

@@ -1130,7 +1131,7 @@ export class LexicalNode {
11301131
const prevSibling = this.getPreviousSibling();
11311132
const parent = this.getParentOrThrow();
11321133
if (prevSibling === null) {
1133-
return parent.select(0, 0);
1134+
return $insertAndSelectNewEmptyAdjacentNode(this, false);
11341135
}
11351136
if ($isElementNode(prevSibling)) {
11361137
return prevSibling.select();
@@ -1152,7 +1153,7 @@ export class LexicalNode {
11521153
const nextSibling = this.getNextSibling();
11531154
const parent = this.getParentOrThrow();
11541155
if (nextSibling === null) {
1155-
return parent.select();
1156+
return $insertAndSelectNewEmptyAdjacentNode(this, true);
11561157
}
11571158
if ($isElementNode(nextSibling)) {
11581159
return nextSibling.select(0, 0);

resources/js/wysiwyg/lexical/core/LexicalSelection.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import invariant from 'lexical/shared/invariant';
1717
import {
1818
$createLineBreakNode,
1919
$createParagraphNode,
20-
$createTextNode,
20+
$createTextNode, $getNearestNodeFromDOMNode,
2121
$isDecoratorNode,
2222
$isElementNode,
2323
$isLineBreakNode,
@@ -63,6 +63,7 @@ import {
6363
toggleTextFormatType,
6464
} from './LexicalUtils';
6565
import {$createTabNode, $isTabNode} from './nodes/LexicalTabNode';
66+
import {$selectSingleNode} from "../../utils/selection";
6667

6768
export type TextPointType = {
6869
_selection: BaseSelection;
@@ -2568,6 +2569,17 @@ export function updateDOMSelection(
25682569
}
25692570

25702571
if (!$isRangeSelection(nextSelection)) {
2572+
2573+
// If the DOM selection enters a decorator node update the selection to a single node selection
2574+
if (activeElement !== null && domSelection.isCollapsed && focusDOMNode instanceof Node) {
2575+
const node = $getNearestNodeFromDOMNode(focusDOMNode);
2576+
if ($isDecoratorNode(node)) {
2577+
domSelection.removeAllRanges();
2578+
$selectSingleNode(node);
2579+
return;
2580+
}
2581+
}
2582+
25712583
// We don't remove selection if the prevSelection is null because
25722584
// of editor.setRootElement(). If this occurs on init when the
25732585
// editor is already focused, then this can cause the editor to

resources/js/wysiwyg/lexical/table/LexicalTableSelectionHelpers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,11 @@ export function getTable(tableElement: HTMLElement): TableDOMTable {
917917
while (currentNode != null) {
918918
const nodeMame = currentNode.nodeName;
919919

920+
if (nodeMame === 'COLGROUP') {
921+
currentNode = currentNode.nextSibling;
922+
continue;
923+
}
924+
920925
if (nodeMame === 'TD' || nodeMame === 'TH') {
921926
const elem = currentNode as HTMLElement;
922927
const cell = {

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

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,21 @@ function deleteSingleSelectedNode(editor: LexicalEditor) {
4747
* Insert a new empty node before/after the selection if the selection contains a single
4848
* selected node (like image, media etc...).
4949
*/
50-
function insertAfterSingleSelectedNode(editor: LexicalEditor, event: KeyboardEvent|null): boolean {
50+
function insertAdjacentToSingleSelectedNode(editor: LexicalEditor, event: KeyboardEvent|null): boolean {
5151
const selectionNodes = getLastSelection(editor)?.getNodes() || [];
5252
if (isSingleSelectedNode(selectionNodes)) {
5353
const node = selectionNodes[0];
5454
const nearestBlock = $getNearestNodeBlockParent(node) || node;
55+
const insertBefore = event?.shiftKey === true;
5556
if (nearestBlock) {
5657
requestAnimationFrame(() => {
5758
editor.update(() => {
5859
const newParagraph = $createParagraphNode();
59-
nearestBlock.insertAfter(newParagraph);
60+
if (insertBefore) {
61+
nearestBlock.insertBefore(newParagraph);
62+
} else {
63+
nearestBlock.insertAfter(newParagraph);
64+
}
6065
newParagraph.select();
6166
});
6267
});
@@ -75,22 +80,14 @@ function focusAdjacentOrInsertForSingleSelectNode(editor: LexicalEditor, event:
7580
}
7681

7782
event?.preventDefault();
78-
7983
const node = selectionNodes[0];
80-
const nearestBlock = $getNearestNodeBlockParent(node) || node;
81-
let target = after ? nearestBlock.getNextSibling() : nearestBlock.getPreviousSibling();
8284

8385
editor.update(() => {
84-
if (!target) {
85-
target = $createParagraphNode();
86-
if (after) {
87-
nearestBlock.insertAfter(target)
88-
} else {
89-
nearestBlock.insertBefore(target);
90-
}
86+
if (after) {
87+
node.selectNext();
88+
} else {
89+
node.selectPrevious();
9190
}
92-
93-
target.selectStart();
9491
});
9592

9693
return true;
@@ -220,7 +217,7 @@ export function registerKeyboardHandling(context: EditorUiContext): () => void {
220217
}, COMMAND_PRIORITY_LOW);
221218

222219
const unregisterEnter = context.editor.registerCommand(KEY_ENTER_COMMAND, (event): boolean => {
223-
return insertAfterSingleSelectedNode(context.editor, event)
220+
return insertAdjacentToSingleSelectedNode(context.editor, event)
224221
|| moveAfterDetailsOnEmptyLine(context.editor, event);
225222
}, COMMAND_PRIORITY_LOW);
226223

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ export class EditorUIManager {
244244
if (selectionChange) {
245245
editor.update(() => {
246246
const selection = $getSelection();
247+
// console.log('manager::selection', selection);
247248
this.triggerStateUpdate({
248249
editor, selection,
249250
});

resources/js/wysiwyg/utils/nodes.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
$isTextNode,
77
ElementNode,
88
LexicalEditor,
9-
LexicalNode
9+
LexicalNode, RangeSelection
1010
} from "lexical";
1111
import {LexicalNodeMatcher} from "../nodes";
1212
import {$generateNodesFromDOM} from "@lexical/html";
@@ -118,6 +118,17 @@ export function $sortNodes(nodes: LexicalNode[]): LexicalNode[] {
118118
return sorted;
119119
}
120120

121+
export function $insertAndSelectNewEmptyAdjacentNode(node: LexicalNode, after: boolean): RangeSelection {
122+
const target = $createParagraphNode();
123+
if (after) {
124+
node.insertAfter(target)
125+
} else {
126+
node.insertBefore(target);
127+
}
128+
129+
return target.select();
130+
}
131+
121132
export function nodeHasAlignment(node: object): node is NodeHasAlignment {
122133
return '__alignment' in node;
123134
}

0 commit comments

Comments
 (0)