mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-04-17 16:59:00 +08:00
Lexical: Made list selections & intendting more reliable
- Added handling to not include parent of top-most list range selection so that it's not also changed while not visually part of the selection range. - Fixed issue where list items could be left over after unnesting, due to empty checks/removals occuring before all child handling. - Added node sorting, applied to list items during nest operations so that selection range remains reliable.
This commit is contained in:
parent
c03e44124a
commit
62c8eb3357
@ -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";
|
||||
|
||||
|
||||
@ -49,16 +49,11 @@ export function $unnestListItem(node: ListItemNode): ListItemNode {
|
||||
}
|
||||
|
||||
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);
|
||||
@ -69,23 +64,54 @@ export function $unnestListItem(node: ListItemNode): ListItemNode {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -110,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 {
|
||||
|
@ -94,6 +94,30 @@ export function $getNearestNodeBlockParent(node: LexicalNode): LexicalNode|null
|
||||
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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user