X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/ace8af077dfa5173c24cdf8b50eb82ccbd1dbf7e..2668aae09b93feb95ac81aceb19e296879052dbb:/resources/js/wysiwyg/utils/lists.ts diff --git a/resources/js/wysiwyg/utils/lists.ts b/resources/js/wysiwyg/utils/lists.ts index 2fc1c5f6b..3deb9dfb6 100644 --- a/resources/js/wysiwyg/utils/lists.ts +++ b/resources/js/wysiwyg/utils/lists.ts @@ -1,6 +1,6 @@ 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"; @@ -10,6 +10,9 @@ export function $nestListItem(node: ListItemNode): ListItemNode { return node; } + const nodeChildList = node.getChildren().filter(n => $isListNode(n))[0] || null; + const nodeChildItems = nodeChildList?.getChildren() || []; + const listItems = list.getChildren() as ListItemNode[]; const nodeIndex = listItems.findIndex((n) => n.getKey() === node.getKey()); const isFirst = nodeIndex === 0; @@ -27,6 +30,13 @@ export function $nestListItem(node: ListItemNode): ListItemNode { node.remove(); } + if (nodeChildList) { + for (const child of nodeChildItems) { + newListItem.insertAfter(child); + } + nodeChildList.remove(); + } + return newListItem; } @@ -38,11 +48,22 @@ export function $unnestListItem(node: ListItemNode): ListItemNode { return node; } + const laterSiblings = node.getNextSiblings(); parentListItem.insertAfter(node); if (list.getChildren().length === 0) { list.remove(); } + if (laterSiblings.length > 0) { + const childList = $createListNode(list.getListType()); + childList.append(...laterSiblings); + node.append(childList); + } + + if (list.getChildrenSize() === 0) { + list.remove(); + } + if (parentListItem.getChildren().length === 0) { parentListItem.remove(); } @@ -52,18 +73,45 @@ export function $unnestListItem(node: ListItemNode): ListItemNode { 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 = 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; } } @@ -88,7 +136,8 @@ function $reduceDedupeListItems(listItems: (ListItemNode|null)[]): ListItemNode[ } } - return Object.values(listItemMap); + const items = Object.values(listItemMap); + return $sortNodes(items) as ListItemNode[]; } export function $setInsetForSelection(editor: LexicalEditor, change: number): void {