import {$createTextNode, $getSelection, BaseSelection, LexicalEditor, TextNode} from "lexical";
import {$getBlockElementNodesInSelection, $selectNodes, $toggleSelection} from "./selection";
-import {nodeHasInset} from "./nodes";
+import {$sortNodes, nodeHasInset} from "./nodes";
import {$createListItemNode, $createListNode, $isListItemNode, $isListNode, ListItemNode} from "@lexical/list";
}
const laterSiblings = node.getNextSiblings();
-
parentListItem.insertAfter(node);
if (list.getChildren().length === 0) {
list.remove();
}
- if (parentListItem.getChildren().length === 0) {
- parentListItem.remove();
- }
-
if (laterSiblings.length > 0) {
const childList = $createListNode(list.getListType());
childList.append(...laterSiblings);
list.remove();
}
+ if (parentListItem.getChildren().length === 0) {
+ parentListItem.remove();
+ }
+
return node;
}
function getListItemsForSelection(selection: BaseSelection|null): (ListItemNode|null)[] {
const nodes = selection?.getNodes() || [];
- const listItemNodes = [];
+ let [start, end] = selection?.getStartEndPoints() || [null, null];
+
+ // Ensure we ignore parent list items of the top-most list item since,
+ // although technically part of the selection, from a user point of
+ // view the selection does not spread to encompass this outer element.
+ const itemsToIgnore: Set<string> = new Set();
+ if (selection && start) {
+ if (selection.isBackward() && end) {
+ [end, start] = [start, end];
+ }
+ const startParents = start.getNode().getParents();
+ let foundList = false;
+ for (const parent of startParents) {
+ if ($isListItemNode(parent)) {
+ if (foundList) {
+ itemsToIgnore.add(parent.getKey());
+ } else {
+ foundList = true;
+ }
+ }
+ }
+ }
+
+ const listItemNodes = [];
outer: for (const node of nodes) {
if ($isListItemNode(node)) {
- listItemNodes.push(node);
+ if (!itemsToIgnore.has(node.getKey())) {
+ listItemNodes.push(node);
+ }
continue;
}
const parents = node.getParents();
for (const parent of parents) {
if ($isListItemNode(parent)) {
- listItemNodes.push(parent);
+ if (!itemsToIgnore.has(parent.getKey())) {
+ listItemNodes.push(parent);
+ }
continue outer;
}
}
}
}
- return Object.values(listItemMap);
+ const items = Object.values(listItemMap);
+ return $sortNodes(items) as ListItemNode[];
}
export function $setInsetForSelection(editor: LexicalEditor, change: number): void {
return $findMatchingParent(node, isBlockNode);
}
+export function $sortNodes(nodes: LexicalNode[]): LexicalNode[] {
+ const idChain: string[] = [];
+ const addIds = (n: ElementNode) => {
+ for (const child of n.getChildren()) {
+ idChain.push(child.getKey())
+ if ($isElementNode(child)) {
+ addIds(child)
+ }
+ }
+ };
+
+ const root = $getRoot();
+ addIds(root);
+
+ const sorted = Array.from(nodes);
+ sorted.sort((a, b) => {
+ const aIndex = idChain.indexOf(a.getKey());
+ const bIndex = idChain.indexOf(b.getKey());
+ return aIndex - bIndex;
+ });
+
+ return sorted;
+}
+
export function nodeHasAlignment(node: object): node is NodeHasAlignment {
return '__alignment' in node;
}