1 import {$getSelection, BaseSelection, LexicalEditor} from "lexical";
2 import {$getBlockElementNodesInSelection, $selectNodes, $toggleSelection} from "./selection";
3 import {nodeHasInset} from "./nodes";
4 import {$createListItemNode, $createListNode, $isListItemNode, $isListNode, ListItemNode} from "@lexical/list";
7 export function $nestListItem(node: ListItemNode): ListItemNode {
8 const list = node.getParent();
9 if (!$isListNode(list)) {
13 const listItems = list.getChildren() as ListItemNode[];
14 const nodeIndex = listItems.findIndex((n) => n.getKey() === node.getKey());
15 const isFirst = nodeIndex === 0;
17 const newListItem = $createListItemNode();
18 const newList = $createListNode(list.getListType());
19 newList.append(newListItem);
20 newListItem.append(...node.getChildren());
25 const prevListItem = listItems[nodeIndex - 1];
26 prevListItem.append(newList);
33 export function $unnestListItem(node: ListItemNode): ListItemNode {
34 const list = node.getParent();
35 const parentListItem = list?.getParent();
36 const outerList = parentListItem?.getParent();
37 if (!$isListNode(list) || !$isListNode(outerList) || !$isListItemNode(parentListItem)) {
41 parentListItem.insertAfter(node);
42 if (list.getChildren().length === 0) {
46 if (parentListItem.getChildren().length === 0) {
47 parentListItem.remove();
53 function getListItemsForSelection(selection: BaseSelection|null): (ListItemNode|null)[] {
54 const nodes = selection?.getNodes() || [];
55 const listItemNodes = [];
57 outer: for (const node of nodes) {
58 if ($isListItemNode(node)) {
59 listItemNodes.push(node);
63 const parents = node.getParents();
64 for (const parent of parents) {
65 if ($isListItemNode(parent)) {
66 listItemNodes.push(parent);
71 listItemNodes.push(null);
77 function $reduceDedupeListItems(listItems: (ListItemNode|null)[]): ListItemNode[] {
78 const listItemMap: Record<string, ListItemNode> = {};
80 for (const item of listItems) {
85 const key = item.getKey();
86 if (typeof listItemMap[key] === 'undefined') {
87 listItemMap[key] = item;
91 return Object.values(listItemMap);
94 export function $setInsetForSelection(editor: LexicalEditor, change: number): void {
95 const selection = $getSelection();
96 const listItemsInSelection = getListItemsForSelection(selection);
97 const isListSelection = listItemsInSelection.length > 0 && !listItemsInSelection.includes(null);
99 if (isListSelection) {
100 const alteredListItems = [];
101 const listItems = $reduceDedupeListItems(listItemsInSelection);
103 for (const listItem of listItems) {
104 alteredListItems.push($nestListItem(listItem));
106 } else if (change < 0) {
107 for (const listItem of [...listItems].reverse()) {
108 alteredListItems.push($unnestListItem(listItem));
110 alteredListItems.reverse();
113 $selectNodes(alteredListItems);
117 const elements = $getBlockElementNodesInSelection(selection);
118 for (const node of elements) {
119 if (nodeHasInset(node)) {
120 const currentInset = node.getInset();
121 const newInset = Math.min(Math.max(currentInset + change, 0), 500);
122 node.setInset(newInset)
126 $toggleSelection(editor);