]> BookStack Code Mirror - bookstack/blobdiff - resources/js/wysiwyg/utils/lists.ts
Lexical: Improved list tab handling, Improved test utils
[bookstack] / resources / js / wysiwyg / utils / lists.ts
index edde994e5a059435eb9ce5c05c2857df13ca6d19..2fc1c5f6b4ed3d2a3ff14368017381e7513061d6 100644 (file)
@@ -1,22 +1,21 @@
-import {$createCustomListItemNode, $isCustomListItemNode, CustomListItemNode} from "../nodes/custom-list-item";
-import {$createCustomListNode, $isCustomListNode} from "../nodes/custom-list";
-import {BaseSelection, LexicalEditor} from "lexical";
-import {$getBlockElementNodesInSelection, $selectNodes, $toggleSelection, getLastSelection} from "./selection";
+import {$createTextNode, $getSelection, BaseSelection, LexicalEditor, TextNode} from "lexical";
+import {$getBlockElementNodesInSelection, $selectNodes, $toggleSelection} from "./selection";
 import {nodeHasInset} from "./nodes";
+import {$createListItemNode, $createListNode, $isListItemNode, $isListNode, ListItemNode} from "@lexical/list";
 
 
-export function $nestListItem(node: CustomListItemNode) {
+export function $nestListItem(node: ListItemNode): ListItemNode {
     const list = node.getParent();
-    if (!$isCustomListNode(list)) {
-        return;
+    if (!$isListNode(list)) {
+        return node;
     }
 
-    const listItems = list.getChildren() as CustomListItemNode[];
+    const listItems = list.getChildren() as ListItemNode[];
     const nodeIndex = listItems.findIndex((n) => n.getKey() === node.getKey());
     const isFirst = nodeIndex === 0;
 
-    const newListItem = $createCustomListItemNode();
-    const newList = $createCustomListNode(list.getListType());
+    const newListItem = $createListItemNode();
+    const newList = $createListNode(list.getListType());
     newList.append(newListItem);
     newListItem.append(...node.getChildren());
 
@@ -27,14 +26,16 @@ export function $nestListItem(node: CustomListItemNode) {
         prevListItem.append(newList);
         node.remove();
     }
+
+    return newListItem;
 }
 
-export function $unnestListItem(node: CustomListItemNode) {
+export function $unnestListItem(node: ListItemNode): ListItemNode {
     const list = node.getParent();
     const parentListItem = list?.getParent();
     const outerList = parentListItem?.getParent();
-    if (!$isCustomListNode(list) || !$isCustomListNode(outerList) || !$isCustomListItemNode(parentListItem)) {
-        return;
+    if (!$isListNode(list) || !$isListNode(outerList) || !$isListItemNode(parentListItem)) {
+        return node;
     }
 
     parentListItem.insertAfter(node);
@@ -45,21 +46,23 @@ export function $unnestListItem(node: CustomListItemNode) {
     if (parentListItem.getChildren().length === 0) {
         parentListItem.remove();
     }
+
+    return node;
 }
 
-function getListItemsForSelection(selection: BaseSelection|null): (CustomListItemNode|null)[] {
+function getListItemsForSelection(selection: BaseSelection|null): (ListItemNode|null)[] {
     const nodes = selection?.getNodes() || [];
     const listItemNodes = [];
 
     outer: for (const node of nodes) {
-        if ($isCustomListItemNode(node)) {
+        if ($isListItemNode(node)) {
             listItemNodes.push(node);
             continue;
         }
 
         const parents = node.getParents();
         for (const parent of parents) {
-            if ($isCustomListItemNode(parent)) {
+            if ($isListItemNode(parent)) {
                 listItemNodes.push(parent);
                 continue outer;
             }
@@ -71,8 +74,8 @@ function getListItemsForSelection(selection: BaseSelection|null): (CustomListIte
     return listItemNodes;
 }
 
-function $reduceDedupeListItems(listItems: (CustomListItemNode|null)[]): CustomListItemNode[] {
-    const listItemMap: Record<string, CustomListItemNode> = {};
+function $reduceDedupeListItems(listItems: (ListItemNode|null)[]): ListItemNode[] {
+    const listItemMap: Record<string, ListItemNode> = {};
 
     for (const item of listItems) {
         if (item === null) {
@@ -89,24 +92,38 @@ function $reduceDedupeListItems(listItems: (CustomListItemNode|null)[]): CustomL
 }
 
 export function $setInsetForSelection(editor: LexicalEditor, change: number): void {
-    const selection = getLastSelection(editor);
-
+    const selection = $getSelection();
+    const selectionBounds = selection?.getStartEndPoints();
     const listItemsInSelection = getListItemsForSelection(selection);
     const isListSelection = listItemsInSelection.length > 0 && !listItemsInSelection.includes(null);
 
     if (isListSelection) {
+        const alteredListItems = [];
         const listItems = $reduceDedupeListItems(listItemsInSelection);
         if (change > 0) {
             for (const listItem of listItems) {
-                $nestListItem(listItem);
+                alteredListItems.push($nestListItem(listItem));
             }
         } else if (change < 0) {
             for (const listItem of [...listItems].reverse()) {
-                $unnestListItem(listItem);
+                alteredListItems.push($unnestListItem(listItem));
+            }
+            alteredListItems.reverse();
+        }
+
+        if (alteredListItems.length === 1 && selectionBounds) {
+            // Retain selection range if moving just one item
+            const listItem = alteredListItems[0] as ListItemNode;
+            let child = listItem.getChildren()[0] as TextNode;
+            if (!child) {
+                child = $createTextNode('');
+                listItem.append(child);
             }
+            child.select(selectionBounds[0].offset, selectionBounds[1].offset);
+        } else {
+            $selectNodes(alteredListItems);
         }
 
-        $selectNodes(listItems);
         return;
     }