summary |
shortlog |
log |
commit | commitdiff |
tree
raw |
inline | side by side (from parent 1:
1243108)
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.
internalMarkNodeAsDirty,
removeFromParent,
} from './LexicalUtils';
internalMarkNodeAsDirty,
removeFromParent,
} from './LexicalUtils';
+import {$insertAndSelectNewEmptyAdjacentNode} from "../../utils/nodes";
export type NodeMap = Map<NodeKey, LexicalNode>;
export type NodeMap = Map<NodeKey, LexicalNode>;
const prevSibling = this.getPreviousSibling();
const parent = this.getParentOrThrow();
if (prevSibling === null) {
const prevSibling = this.getPreviousSibling();
const parent = this.getParentOrThrow();
if (prevSibling === null) {
- return parent.select(0, 0);
+ return $insertAndSelectNewEmptyAdjacentNode(this, false);
}
if ($isElementNode(prevSibling)) {
return prevSibling.select();
}
if ($isElementNode(prevSibling)) {
return prevSibling.select();
const nextSibling = this.getNextSibling();
const parent = this.getParentOrThrow();
if (nextSibling === null) {
const nextSibling = this.getNextSibling();
const parent = this.getParentOrThrow();
if (nextSibling === null) {
- return parent.select();
+ return $insertAndSelectNewEmptyAdjacentNode(this, true);
}
if ($isElementNode(nextSibling)) {
return nextSibling.select(0, 0);
}
if ($isElementNode(nextSibling)) {
return nextSibling.select(0, 0);
import {
$createLineBreakNode,
$createParagraphNode,
import {
$createLineBreakNode,
$createParagraphNode,
+ $createTextNode, $getNearestNodeFromDOMNode,
$isDecoratorNode,
$isElementNode,
$isLineBreakNode,
$isDecoratorNode,
$isElementNode,
$isLineBreakNode,
toggleTextFormatType,
} from './LexicalUtils';
import {$createTabNode, $isTabNode} from './nodes/LexicalTabNode';
toggleTextFormatType,
} from './LexicalUtils';
import {$createTabNode, $isTabNode} from './nodes/LexicalTabNode';
+import {$selectSingleNode} from "../../utils/selection";
export type TextPointType = {
_selection: BaseSelection;
export type TextPointType = {
_selection: BaseSelection;
}
if (!$isRangeSelection(nextSelection)) {
}
if (!$isRangeSelection(nextSelection)) {
+
+ // If the DOM selection enters a decorator node update the selection to a single node selection
+ if (activeElement !== null && domSelection.isCollapsed && focusDOMNode instanceof Node) {
+ const node = $getNearestNodeFromDOMNode(focusDOMNode);
+ if ($isDecoratorNode(node)) {
+ domSelection.removeAllRanges();
+ $selectSingleNode(node);
+ return;
+ }
+ }
+
// We don't remove selection if the prevSelection is null because
// of editor.setRootElement(). If this occurs on init when the
// editor is already focused, then this can cause the editor to
// We don't remove selection if the prevSelection is null because
// of editor.setRootElement(). If this occurs on init when the
// editor is already focused, then this can cause the editor to
while (currentNode != null) {
const nodeMame = currentNode.nodeName;
while (currentNode != null) {
const nodeMame = currentNode.nodeName;
+ if (nodeMame === 'COLGROUP') {
+ currentNode = currentNode.nextSibling;
+ continue;
+ }
+
if (nodeMame === 'TD' || nodeMame === 'TH') {
const elem = currentNode as HTMLElement;
const cell = {
if (nodeMame === 'TD' || nodeMame === 'TH') {
const elem = currentNode as HTMLElement;
const cell = {
* Insert a new empty node before/after the selection if the selection contains a single
* selected node (like image, media etc...).
*/
* 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 {
+function insertAdjacentToSingleSelectedNode(editor: LexicalEditor, event: KeyboardEvent|null): boolean {
const selectionNodes = getLastSelection(editor)?.getNodes() || [];
if (isSingleSelectedNode(selectionNodes)) {
const node = selectionNodes[0];
const nearestBlock = $getNearestNodeBlockParent(node) || node;
const selectionNodes = getLastSelection(editor)?.getNodes() || [];
if (isSingleSelectedNode(selectionNodes)) {
const node = selectionNodes[0];
const nearestBlock = $getNearestNodeBlockParent(node) || node;
+ const insertBefore = event?.shiftKey === true;
if (nearestBlock) {
requestAnimationFrame(() => {
editor.update(() => {
const newParagraph = $createParagraphNode();
if (nearestBlock) {
requestAnimationFrame(() => {
editor.update(() => {
const newParagraph = $createParagraphNode();
- nearestBlock.insertAfter(newParagraph);
+ if (insertBefore) {
+ nearestBlock.insertBefore(newParagraph);
+ } else {
+ nearestBlock.insertAfter(newParagraph);
+ }
newParagraph.select();
});
});
newParagraph.select();
});
});
}
event?.preventDefault();
}
event?.preventDefault();
const node = selectionNodes[0];
const node = selectionNodes[0];
- const nearestBlock = $getNearestNodeBlockParent(node) || node;
- let target = after ? nearestBlock.getNextSibling() : nearestBlock.getPreviousSibling();
- if (!target) {
- target = $createParagraphNode();
- if (after) {
- nearestBlock.insertAfter(target)
- } else {
- nearestBlock.insertBefore(target);
- }
+ if (after) {
+ node.selectNext();
+ } else {
+ node.selectPrevious();
-
- target.selectStart();
}, COMMAND_PRIORITY_LOW);
const unregisterEnter = context.editor.registerCommand(KEY_ENTER_COMMAND, (event): boolean => {
}, COMMAND_PRIORITY_LOW);
const unregisterEnter = context.editor.registerCommand(KEY_ENTER_COMMAND, (event): boolean => {
- return insertAfterSingleSelectedNode(context.editor, event)
+ return insertAdjacentToSingleSelectedNode(context.editor, event)
|| moveAfterDetailsOnEmptyLine(context.editor, event);
}, COMMAND_PRIORITY_LOW);
|| moveAfterDetailsOnEmptyLine(context.editor, event);
}, COMMAND_PRIORITY_LOW);
if (selectionChange) {
editor.update(() => {
const selection = $getSelection();
if (selectionChange) {
editor.update(() => {
const selection = $getSelection();
+ // console.log('manager::selection', selection);
this.triggerStateUpdate({
editor, selection,
});
this.triggerStateUpdate({
editor, selection,
});
$isTextNode,
ElementNode,
LexicalEditor,
$isTextNode,
ElementNode,
LexicalEditor,
+ LexicalNode, RangeSelection
} from "lexical";
import {LexicalNodeMatcher} from "../nodes";
import {$generateNodesFromDOM} from "@lexical/html";
} from "lexical";
import {LexicalNodeMatcher} from "../nodes";
import {$generateNodesFromDOM} from "@lexical/html";
+export function $insertAndSelectNewEmptyAdjacentNode(node: LexicalNode, after: boolean): RangeSelection {
+ const target = $createParagraphNode();
+ if (after) {
+ node.insertAfter(target)
+ } else {
+ node.insertBefore(target);
+ }
+
+ return target.select();
+}
+
export function nodeHasAlignment(node: object): node is NodeHasAlignment {
return '__alignment' in node;
}
export function nodeHasAlignment(node: object): node is NodeHasAlignment {
return '__alignment' in node;
}