@udecode/plate-list
Version:
List plugin for Plate
1 lines • 118 kB
Source Map (JSON)
{"version":3,"sources":["../../src/react/ListPlugin.tsx","../../src/lib/BaseListPlugin.ts","../../src/lib/transforms/moveListItems.ts","../../src/lib/queries/isListNested.ts","../../src/lib/transforms/moveListItemDown.ts","../../src/lib/queries/getHighestEmptyList.ts","../../src/lib/queries/getListTypes.ts","../../src/lib/queries/getListItemEntry.ts","../../src/lib/queries/getListRoot.ts","../../src/lib/queries/getTodoListItemEntry.ts","../../src/lib/BaseTodoListPlugin.ts","../../src/lib/withTodoList.ts","../../src/lib/queries/hasListChild.ts","../../src/lib/queries/isAcrossListItems.ts","../../src/lib/queries/isListRoot.ts","../../src/lib/transforms/moveListItemUp.ts","../../src/lib/transforms/moveListItemsToList.ts","../../src/lib/transforms/unwrapList.ts","../../src/lib/transforms/removeFirstListItem.ts","../../src/lib/transforms/insertListItem.ts","../../src/lib/transforms/insertTodoListItem.ts","../../src/lib/transforms/moveListItemSublistItemsToListItemSublist.ts","../../src/lib/transforms/removeListItem.ts","../../src/lib/transforms/toggleList.ts","../../src/lib/withDeleteForwardList.ts","../../src/lib/withDeleteFragmentList.ts","../../src/lib/withInsertFragmentList.ts","../../src/lib/withNormalizeList.ts","../../src/lib/normalizers/normalizeListItem.ts","../../src/lib/normalizers/normalizeNestedList.ts","../../src/react/onKeyDownList.ts","../../src/react/withDeleteBackwardList.ts","../../src/react/withInsertBreakList.ts","../../src/react/withList.ts","../../src/react/TodoListPlugin.tsx","../../src/react/hooks/useListToolbarButton.ts","../../src/react/hooks/useTodoListElement.ts"],"sourcesContent":["import { Key, toPlatePlugin } from '@udecode/plate/react';\n\nimport {\n BaseBulletedListPlugin,\n BaseListItemContentPlugin,\n BaseListItemPlugin,\n BaseListPlugin,\n BaseNumberedListPlugin,\n} from '../lib';\nimport { onKeyDownList } from './onKeyDownList';\nimport { withList } from './withList';\n\nexport const BulletedListPlugin = toPlatePlugin(BaseBulletedListPlugin, {\n dependencies: ['list'],\n handlers: {\n onKeyDown: onKeyDownList,\n },\n});\n\nexport const NumberedListPlugin = toPlatePlugin(BaseNumberedListPlugin, {\n dependencies: ['list'],\n handlers: {\n onKeyDown: onKeyDownList,\n },\n});\n\nexport const ListItemContentPlugin = toPlatePlugin(BaseListItemContentPlugin);\n\nexport const ListItemPlugin = toPlatePlugin(BaseListItemPlugin);\n\n/**\n * Enables support for bulleted, numbered and to-do lists with React-specific\n * features.\n */\nexport const ListPlugin = toPlatePlugin(BaseListPlugin, {\n plugins: [\n BulletedListPlugin,\n NumberedListPlugin,\n ListItemPlugin,\n ListItemContentPlugin,\n ],\n})\n .overrideEditor(withList)\n .extend(({ editor }) => ({\n shortcuts: {\n toggleBulletedList: {\n keys: [[Key.Mod, Key.Alt, '5']],\n preventDefault: true,\n handler: () => {\n editor.getTransforms(ListPlugin).toggle.bulletedList();\n },\n },\n toggleNumberedList: {\n keys: [[Key.Mod, Key.Alt, '6']],\n preventDefault: true,\n handler: () => {\n editor.getTransforms(ListPlugin).toggle.numberedList();\n },\n },\n },\n }));\n","import {\n type OmitFirst,\n type PluginConfig,\n bindFirst,\n createSlatePlugin,\n createTSlatePlugin,\n HtmlPlugin,\n} from '@udecode/plate';\n\nimport {\n toggleBulletedList,\n toggleList,\n toggleNumberedList,\n} from './transforms';\n\nexport type ListConfig = PluginConfig<\n 'list',\n {\n enableResetOnShiftTab?: boolean;\n /** Valid children types for list items, in addition to p and ul types. */\n validLiChildrenTypes?: string[];\n },\n {},\n {\n toggle: {\n bulletedList: OmitFirst<typeof toggleBulletedList>;\n list: OmitFirst<typeof toggleList>;\n numberedList: OmitFirst<typeof toggleNumberedList>;\n };\n }\n>;\n\nexport const BaseBulletedListPlugin = createSlatePlugin({\n key: 'ul',\n node: { isElement: true },\n parsers: {\n html: {\n deserializer: {\n rules: [\n {\n validNodeName: 'UL',\n },\n ],\n },\n },\n },\n});\n\nexport const BaseNumberedListPlugin = createSlatePlugin({\n key: 'ol',\n node: { isElement: true },\n parsers: { html: { deserializer: { rules: [{ validNodeName: 'OL' }] } } },\n});\n\nexport const BaseListItemPlugin = createSlatePlugin({\n key: 'li',\n inject: {\n plugins: {\n [HtmlPlugin.key]: {\n parser: {\n preInsert: ({ editor, type }) => {\n return editor.api.some({ match: { type } });\n },\n },\n },\n },\n },\n node: { isElement: true },\n parsers: { html: { deserializer: { rules: [{ validNodeName: 'LI' }] } } },\n});\n\nexport const BaseListItemContentPlugin = createSlatePlugin({\n key: 'lic',\n node: { isElement: true },\n});\n\n/** Enables support for bulleted, numbered and to-do lists. */\nexport const BaseListPlugin = createTSlatePlugin<ListConfig>({\n key: 'list',\n // TODO react\n // extendEditor: withList,\n plugins: [\n BaseBulletedListPlugin,\n BaseNumberedListPlugin,\n BaseListItemPlugin,\n BaseListItemContentPlugin,\n ],\n}).extendEditorTransforms(({ editor }) => ({\n toggle: {\n bulletedList: bindFirst(toggleBulletedList, editor),\n list: bindFirst(toggleList, editor),\n numberedList: bindFirst(toggleNumberedList, editor),\n },\n}));\n","import {\n type EditorNodesOptions,\n type Path,\n type PathRef,\n type SlateEditor,\n PathApi,\n} from '@udecode/plate';\n\nimport { BaseListItemContentPlugin } from '../BaseListPlugin';\nimport { isListNested } from '../queries/isListNested';\nimport { moveListItemDown } from './moveListItemDown';\nimport { moveListItemUp } from './moveListItemUp';\nimport { removeFirstListItem } from './removeFirstListItem';\n\nexport type MoveListItemsOptions = {\n at?: EditorNodesOptions['at'];\n enableResetOnShiftTab?: boolean;\n increase?: boolean;\n};\n\nexport const moveListItems = (\n editor: SlateEditor,\n {\n at = editor.selection ?? undefined,\n enableResetOnShiftTab,\n increase = true,\n }: MoveListItemsOptions = {}\n) => {\n const _nodes = editor.api.nodes({\n at,\n match: {\n type: editor.getType(BaseListItemContentPlugin),\n },\n });\n\n // Get the selected lic\n const lics = Array.from(_nodes);\n\n if (lics.length === 0) return;\n\n const highestLicPaths: Path[] = [];\n const highestLicPathRefs: PathRef[] = [];\n\n // Filter out the nested lic, we just need to move the highest ones\n lics.forEach((lic) => {\n const licPath = lic[1];\n const liPath = PathApi.parent(licPath);\n\n const isAncestor = highestLicPaths.some((path) => {\n const highestLiPath = PathApi.parent(path);\n\n return PathApi.isAncestor(highestLiPath, liPath);\n });\n\n if (!isAncestor) {\n highestLicPaths.push(licPath);\n highestLicPathRefs.push(editor.api.pathRef(licPath));\n }\n });\n\n const licPathRefsToMove = increase\n ? highestLicPathRefs\n : highestLicPathRefs.reverse();\n\n return editor.tf.withoutNormalizing(() => {\n let moved = false;\n\n licPathRefsToMove.forEach((licPathRef) => {\n const licPath = licPathRef.unref();\n\n if (!licPath) return;\n\n const listItem = editor.api.parent(licPath);\n\n if (!listItem) return;\n\n const parentList = editor.api.parent(listItem[1]);\n\n if (!parentList) return;\n\n let _moved: any;\n\n if (increase) {\n _moved = moveListItemDown(editor, {\n list: parentList as any,\n listItem: listItem as any,\n });\n } else if (isListNested(editor, parentList[1])) {\n // un-indent a sub-list item\n _moved = moveListItemUp(editor, {\n list: parentList as any,\n listItem: listItem as any,\n });\n } else if (enableResetOnShiftTab) {\n // unindenting a top level list item, effectively breaking apart the list.\n _moved = removeFirstListItem(editor, {\n list: parentList as any,\n listItem: listItem as any,\n });\n }\n\n moved = _moved || moved;\n });\n\n return moved;\n });\n};\n","import type { Path, SlateEditor, TElement } from '@udecode/plate';\n\nimport { BaseListItemPlugin } from '../BaseListPlugin';\n\n/** Is the list nested, i.e. its parent is a list item. */\nexport const isListNested = (editor: SlateEditor, listPath: Path) => {\n const listParentNode = editor.api.parent<TElement>(listPath)?.[0];\n\n return listParentNode?.type === editor.getType(BaseListItemPlugin);\n};\n","import {\n type ElementEntry,\n type SlateEditor,\n type TElement,\n match,\n PathApi,\n} from '@udecode/plate';\n\nimport { getListTypes } from '../queries/index';\n\nexport interface MoveListItemDownOptions {\n list: ElementEntry;\n listItem: ElementEntry;\n}\n\nexport const moveListItemDown = (\n editor: SlateEditor,\n { list, listItem }: MoveListItemDownOptions\n) => {\n let moved = false;\n\n const [listNode] = list;\n const [, listItemPath] = listItem;\n\n const previousListItemPath = PathApi.previous(listItemPath);\n\n if (!previousListItemPath) {\n return;\n }\n\n // Previous sibling is the new parent\n const previousSiblingItem = editor.api.node(previousListItemPath);\n\n if (previousSiblingItem) {\n const [previousNode, previousPath] = previousSiblingItem;\n\n const sublist = (previousNode.children as TElement[]).find((n) =>\n match(n, [], { type: getListTypes(editor) })\n );\n const newPath = previousPath.concat(\n sublist ? [1, sublist.children.length] : [1]\n );\n\n editor.tf.withoutNormalizing(() => {\n if (!sublist) {\n // Create new sublist\n editor.tf.wrapNodes<TElement>(\n { children: [], type: listNode.type },\n { at: listItemPath }\n );\n }\n\n // Move the current item to the sublist\n editor.tf.moveNodes({\n at: listItemPath,\n to: newPath,\n });\n\n moved = true;\n });\n }\n\n return moved;\n};\n","import { type Path, type SlateEditor, PathApi } from '@udecode/plate';\n\nimport { BaseListItemPlugin } from '../BaseListPlugin';\nimport { getListTypes } from './getListTypes';\n\n/**\n * Find the highest end list that can be deleted. Its path should be different\n * to diffListPath. If the highest end list 2+ items, return liPath. Get the\n * parent list until:\n *\n * - The list has less than 2 items.\n * - Its path is not equals to diffListPath.\n */\nexport const getHighestEmptyList = (\n editor: SlateEditor,\n {\n diffListPath,\n liPath,\n }: {\n liPath: Path;\n diffListPath?: Path;\n }\n): Path | undefined => {\n const list = editor.api.above({\n at: liPath,\n match: { type: getListTypes(editor) },\n });\n\n if (!list) return;\n\n const [listNode, listPath] = list;\n\n if (!diffListPath || !PathApi.equals(listPath, diffListPath)) {\n if (listNode.children.length < 2) {\n const liParent = editor.api.above({\n at: listPath,\n match: { type: editor.getType(BaseListItemPlugin) },\n });\n\n if (liParent) {\n return (\n getHighestEmptyList(editor, { diffListPath, liPath: liParent[1] }) ||\n listPath\n );\n }\n }\n\n return liPath;\n }\n};\n","import type { SlateEditor } from '@udecode/plate';\n\nimport {\n BaseBulletedListPlugin,\n BaseNumberedListPlugin,\n} from '../BaseListPlugin';\n\nexport const getListTypes = (editor: SlateEditor) => {\n return [\n editor.getType(BaseNumberedListPlugin),\n editor.getType(BaseBulletedListPlugin),\n ];\n};\n","import {\n type ElementEntry,\n type Path,\n type SlateEditor,\n type TElement,\n type TLocation,\n NodeApi,\n RangeApi,\n} from '@udecode/plate';\n\nimport { BaseListItemPlugin } from '../BaseListPlugin';\n\n/**\n * Returns the nearest li and ul / ol wrapping node entries for a given path\n * (default = selection)\n */\nexport const getListItemEntry = (\n editor: SlateEditor,\n { at = editor.selection }: { at?: TLocation | null } = {}\n): { list: ElementEntry; listItem: ElementEntry } | undefined => {\n const liType = editor.getType(BaseListItemPlugin);\n\n let _at: Path;\n\n if (RangeApi.isRange(at) && !RangeApi.isCollapsed(at)) {\n _at = at.focus.path;\n } else if (RangeApi.isRange(at)) {\n _at = at.anchor.path;\n } else {\n _at = at as Path;\n }\n if (_at) {\n const node = NodeApi.get<TElement>(editor, _at);\n\n if (node) {\n const listItem = editor.api.above<TElement>({\n at: _at,\n match: { type: liType },\n });\n\n if (listItem) {\n const list = editor.api.parent<TElement>(listItem[1])!;\n\n return { list, listItem };\n }\n }\n }\n};\n","import type {\n ElementEntry,\n Path,\n Point,\n SlateEditor,\n TElement,\n TRange,\n} from '@udecode/plate';\n\nimport {\n BaseBulletedListPlugin,\n BaseNumberedListPlugin,\n} from '../BaseListPlugin';\n\n/** Searches upward for the root list element */\nexport const getListRoot = (\n editor: SlateEditor,\n at: Path | Point | TRange | null = editor.selection\n): ElementEntry | undefined => {\n if (!at) return;\n\n const parentList = editor.api.above<TElement>({\n at,\n match: {\n type: [\n editor.getType(BaseBulletedListPlugin),\n editor.getType(BaseNumberedListPlugin),\n ],\n },\n });\n\n if (parentList) {\n const [, parentListPath] = parentList;\n\n return getListRoot(editor, parentListPath) ?? parentList;\n }\n};\n","import {\n type ElementEntry,\n type Path,\n type SlateEditor,\n type TElement,\n type TLocation,\n NodeApi,\n RangeApi,\n} from '@udecode/plate';\n\nimport { BaseTodoListPlugin } from '../BaseTodoListPlugin';\n\n/**\n * Returns the nearest li and ul / ol wrapping node entries for a given path\n * (default = selection)\n */\nexport const getTodoListItemEntry = (\n editor: SlateEditor,\n { at = editor.selection }: { at?: TLocation | null } = {}\n): { list: ElementEntry; listItem: ElementEntry } | undefined => {\n const todoType = editor.getType(BaseTodoListPlugin);\n\n let _at: Path;\n\n if (RangeApi.isRange(at) && !RangeApi.isCollapsed(at)) {\n _at = at.focus.path;\n } else if (RangeApi.isRange(at)) {\n _at = at.anchor.path;\n } else {\n _at = at as Path;\n }\n if (_at) {\n const node = NodeApi.get<TElement>(editor, _at);\n\n if (node) {\n const listItem = editor.api.above<TElement>({\n at: _at,\n match: { type: todoType },\n });\n\n if (listItem) {\n const list = editor.api.parent<TElement>(listItem[1])!;\n\n return { list, listItem };\n }\n }\n }\n};\n","import {\n type PluginConfig,\n type TElement,\n createTSlatePlugin,\n} from '@udecode/plate';\n\nimport { withTodoList } from './withTodoList';\n\nexport type TodoListConfig = PluginConfig<\n 'action_item',\n {\n inheritCheckStateOnLineEndBreak?: boolean;\n inheritCheckStateOnLineStartBreak?: boolean;\n }\n>;\n\nexport interface TTodoListItemElement extends TElement {\n checked?: boolean;\n}\n\nexport const BaseTodoListPlugin = createTSlatePlugin<TodoListConfig>({\n key: 'action_item',\n node: { isElement: true },\n}).overrideEditor(withTodoList);\n","import type { OverrideEditor } from '@udecode/plate';\n\nimport type { TodoListConfig } from './BaseTodoListPlugin';\n\nimport { getTodoListItemEntry } from './queries';\nimport { insertTodoListItem } from './transforms';\n\nexport const withTodoList: OverrideEditor<TodoListConfig> = ({\n editor,\n getOptions,\n tf: { insertBreak },\n}) => ({\n transforms: {\n insertBreak() {\n const insertBreakTodoList = () => {\n if (!editor.selection) return;\n\n const res = getTodoListItemEntry(editor);\n\n // If selection is in a todo li\n if (res) {\n const inserted = insertTodoListItem(editor, getOptions());\n\n if (inserted) return true;\n }\n };\n\n if (insertBreakTodoList()) return;\n\n insertBreak();\n },\n },\n});\n","import { type Ancestor, type SlateEditor, match } from '@udecode/plate';\n\nimport { getListTypes } from './getListTypes';\n\n/** Is there a list child in the node. */\nexport const hasListChild = (editor: SlateEditor, node: Ancestor) =>\n node.children.some((n) => match(n, [], { type: getListTypes(editor) }));\n","import { type SlateEditor, type TRange, RangeApi } from '@udecode/plate';\n\nimport { BaseListItemPlugin } from '../BaseListPlugin';\n\n/** Is selection across blocks with list items */\nexport const isAcrossListItems = (\n editor: SlateEditor,\n at: TRange | null = editor.selection\n) => {\n if (!at || RangeApi.isCollapsed(at)) {\n return false;\n }\n\n const isAcrossBlocks = editor.api.isAt({ at, blocks: true });\n\n if (!isAcrossBlocks) return false;\n\n return editor.api.some({\n at,\n match: { type: editor.getType(BaseListItemPlugin) },\n });\n};\n","import { type Descendant, type SlateEditor, ElementApi } from '@udecode/plate';\n\nimport { getListTypes } from './getListTypes';\n\nexport const isListRoot = (editor: SlateEditor, node: Descendant): boolean =>\n ElementApi.isElement(node) && getListTypes(editor).includes(node.type);\n","import {\n type ElementEntry,\n type SlateEditor,\n type TElement,\n NodeApi,\n PathApi,\n} from '@udecode/plate';\n\nimport { BaseListItemPlugin } from '../BaseListPlugin';\nimport { hasListChild } from '../queries/hasListChild';\nimport { moveListItemsToList } from './moveListItemsToList';\nimport { unwrapList } from './unwrapList';\n\nexport interface MoveListItemUpOptions {\n list: ElementEntry;\n listItem: ElementEntry;\n}\n\n/** Move a list item up. */\nexport const moveListItemUp = (\n editor: SlateEditor,\n { list, listItem }: MoveListItemUpOptions\n) => {\n const move = () => {\n const [listNode, listPath] = list;\n const [liNode, liPath] = listItem;\n\n const liParent = editor.api.above<TElement>({\n at: listPath,\n match: { type: editor.getType(BaseListItemPlugin) },\n });\n\n if (!liParent) {\n let toListPath;\n\n try {\n toListPath = PathApi.next(listPath);\n } catch (error) {\n return;\n }\n\n const condA = hasListChild(editor, liNode);\n const condB = !NodeApi.isLastChild(editor, liPath);\n\n if (condA || condB) {\n // Insert a new list next to `list`\n editor.tf.insertNodes(\n {\n children: [],\n type: listNode.type,\n },\n { at: toListPath }\n );\n }\n if (condA) {\n const toListNode = NodeApi.get<TElement>(editor, toListPath);\n\n if (!toListNode) return;\n\n // Move li sub-lis to the new list\n moveListItemsToList(editor, {\n fromListItem: listItem,\n toList: [toListNode, toListPath],\n });\n }\n // If there is siblings li, move them to the new list\n if (condB) {\n const toListNode = NodeApi.get<TElement>(editor, toListPath);\n\n if (!toListNode) return;\n\n // Move next lis to the new list\n moveListItemsToList(editor, {\n deleteFromList: false,\n fromList: list,\n fromStartIndex: liPath.at(-1)! + 1,\n toList: [toListNode, toListPath],\n });\n }\n\n // Finally, unwrap the list\n unwrapList(editor, { at: liPath.concat(0) });\n\n return true;\n }\n\n const [, liParentPath] = liParent;\n\n const toListPath = liPath.concat([1]);\n\n // If li has next siblings, we need to move them.\n if (!NodeApi.isLastChild(editor, liPath)) {\n // If li has no sublist, insert one.\n if (!hasListChild(editor, liNode)) {\n editor.tf.insertNodes(\n {\n children: [],\n type: listNode.type,\n },\n { at: toListPath }\n );\n }\n\n const toListNode = NodeApi.get<TElement>(editor, toListPath);\n\n if (!toListNode) return;\n\n // Move next siblings to li sublist.\n moveListItemsToList(editor, {\n deleteFromList: false,\n fromListItem: liParent,\n fromStartIndex: liPath.at(-1)! + 1,\n toList: [toListNode, toListPath],\n });\n }\n\n const movedUpLiPath = PathApi.next(liParentPath);\n\n // Move li one level up: next to the li parent.\n editor.tf.moveNodes({\n at: liPath,\n to: movedUpLiPath,\n });\n\n return true;\n };\n\n let moved: boolean | undefined = false;\n\n editor.tf.withoutNormalizing(() => {\n moved = move();\n });\n\n return moved;\n};\n","import {\n type ElementEntry,\n type Path,\n type SlateEditor,\n NodeApi,\n PathApi,\n} from '@udecode/plate';\n\nimport { getListTypes } from '../queries/getListTypes';\n\nexport interface MergeListItemIntoListOptions {\n /**\n * Delete `fromListItem` sublist if true.\n *\n * @default true\n */\n deleteFromList?: boolean;\n\n /** List items of the list will be moved. */\n fromList?: ElementEntry;\n\n /** List items of the sublist of this node will be moved. */\n fromListItem?: ElementEntry;\n\n fromStartIndex?: number;\n\n to?: Path;\n\n /** List items will be moved in this list. */\n toList?: ElementEntry;\n\n /** List position where to move the list items. */\n toListIndex?: number | null;\n}\n\n/**\n * Move the list items of the sublist of `fromListItem` to `toList` (if\n * `fromListItem` is defined). Move the list items of `fromList` to `toList` (if\n * `fromList` is defined).\n */\nexport const moveListItemsToList = (\n editor: SlateEditor,\n {\n deleteFromList = true,\n fromList,\n fromListItem,\n fromStartIndex,\n to: _to,\n toList,\n toListIndex = null,\n }: MergeListItemIntoListOptions\n) => {\n let fromListPath: Path | undefined;\n let moved: boolean | void = false;\n\n editor.tf.withoutNormalizing(() => {\n if (fromListItem) {\n const fromListItemSublist = editor.api.descendant({\n at: fromListItem[1],\n match: {\n type: getListTypes(editor),\n },\n });\n\n if (!fromListItemSublist) return;\n\n fromListPath = fromListItemSublist?.[1];\n } else if (fromList) {\n fromListPath = fromList[1];\n } else {\n return;\n }\n\n let to: Path | null = null;\n\n if (_to) to = _to;\n if (toList) {\n if (toListIndex === null) {\n const lastChildPath = NodeApi.lastChild(editor, toList[1])?.[1];\n to = lastChildPath\n ? PathApi.next(lastChildPath)\n : toList[1].concat([0]);\n } else {\n to = toList[1].concat([toListIndex]);\n }\n }\n if (!to) return;\n\n moved = editor.tf.moveNodes({\n at: fromListPath,\n children: true,\n fromIndex: fromStartIndex,\n to,\n });\n\n // Remove the empty list\n if (deleteFromList) {\n editor.tf.delete({ at: fromListPath });\n }\n });\n\n return moved;\n};\n","import {\n type Path,\n type SlateEditor,\n ElementApi,\n NodeApi,\n} from '@udecode/plate';\n\nimport {\n BaseBulletedListPlugin,\n BaseListItemPlugin,\n BaseNumberedListPlugin,\n} from '../BaseListPlugin';\nimport { getListTypes } from '../queries/index';\n\nexport const unwrapList = (editor: SlateEditor, { at }: { at?: Path } = {}) => {\n const ancestorListTypeCheck = () => {\n if (editor.api.above({ at, match: { type: getListTypes(editor) } })) {\n return true;\n }\n // The selection's common node might be a list type\n if (!at && editor.selection) {\n const commonNode = NodeApi.common(\n editor,\n editor.selection.anchor.path,\n editor.selection.focus.path\n )!;\n\n if (\n ElementApi.isElement(commonNode[0]) &&\n getListTypes(editor).includes(commonNode[0].type)\n ) {\n return true;\n }\n }\n\n return false;\n };\n\n editor.tf.withoutNormalizing(() => {\n do {\n // const licEntry = editor.api.block({\n // at,\n // match: { type: editor.getType(BaseListItemContentPlugin) },\n // });\n\n // Allow other LIC types\n // if (licEntry) {\n // editor.tf.setNodes(\n // { type: editor.getType(BaseParagraphPlugin) },\n // { at }\n // );\n // }\n\n editor.tf.unwrapNodes({\n at,\n match: { type: editor.getType(BaseListItemPlugin) },\n split: true,\n });\n\n editor.tf.unwrapNodes({\n at,\n match: {\n type: [\n editor.getType(BaseBulletedListPlugin),\n editor.getType(BaseNumberedListPlugin),\n ],\n },\n split: true,\n });\n } while (ancestorListTypeCheck());\n });\n};\n","import type { ElementEntry, SlateEditor } from '@udecode/plate';\n\nimport { isListNested } from '../queries/isListNested';\nimport { moveListItemUp } from './moveListItemUp';\n\n/** If list is not nested and if li is not the first child, move li up. */\nexport const removeFirstListItem = (\n editor: SlateEditor,\n {\n list,\n listItem,\n }: {\n list: ElementEntry;\n listItem: ElementEntry;\n }\n) => {\n const [, listPath] = list;\n\n if (!isListNested(editor, listPath)) {\n moveListItemUp(editor, { list, listItem });\n\n return true;\n }\n\n return false;\n};\n","import { type SlateEditor, type TElement, PathApi } from '@udecode/plate';\n\nimport {\n BaseListItemContentPlugin,\n BaseListItemPlugin,\n} from '../BaseListPlugin';\n\n/** Insert list item if selection in li>p. TODO: test */\nexport const insertListItem = (editor: SlateEditor): boolean => {\n const liType = editor.getType(BaseListItemPlugin);\n const licType = editor.getType(BaseListItemContentPlugin);\n\n if (!editor.selection) {\n return false;\n }\n\n const licEntry = editor.api.above({ match: { type: licType } });\n\n if (!licEntry) return false;\n\n const [, paragraphPath] = licEntry;\n\n const listItemEntry = editor.api.parent(paragraphPath);\n\n if (!listItemEntry) return false;\n\n const [listItemNode, listItemPath] = listItemEntry;\n\n if (listItemNode.type !== liType) return false;\n\n let success = false;\n\n editor.tf.withoutNormalizing(() => {\n if (!editor.api.isCollapsed()) {\n editor.tf.delete();\n }\n\n const isStart = editor.api.isStart(editor.selection!.focus, paragraphPath);\n const isEnd = editor.api.isEmpty(editor.selection, { after: true });\n\n const nextParagraphPath = PathApi.next(paragraphPath);\n const nextListItemPath = PathApi.next(listItemPath);\n\n /** If start, insert a list item before */\n if (isStart) {\n editor.tf.insertNodes(\n {\n children: [{ children: [{ text: '' }], type: licType }],\n type: liType,\n },\n { at: listItemPath }\n );\n\n success = true;\n\n return;\n }\n /**\n * If not end, split nodes, wrap a list item on the new paragraph and move\n * it to the next list item\n */\n if (isEnd) {\n /** If end, insert a list item after and select it */\n const marks = editor.api.marks() || {};\n editor.tf.insertNodes(\n {\n children: [{ children: [{ text: '', ...marks }], type: licType }],\n type: liType,\n },\n { at: nextListItemPath }\n );\n editor.tf.select(nextListItemPath);\n } else {\n editor.tf.withoutNormalizing(() => {\n editor.tf.splitNodes();\n editor.tf.wrapNodes<TElement>(\n {\n children: [],\n type: liType,\n },\n { at: nextParagraphPath }\n );\n editor.tf.moveNodes({\n at: nextParagraphPath,\n to: nextListItemPath,\n });\n editor.tf.select(nextListItemPath);\n editor.tf.collapse({\n edge: 'start',\n });\n });\n }\n /** If there is a list in the list item, move it to the next list item */\n if (listItemNode.children.length > 1) {\n editor.tf.moveNodes({\n at: nextParagraphPath,\n to: nextListItemPath.concat(1),\n });\n }\n\n success = true;\n });\n\n return success;\n};\n","import { type SlateEditor, PathApi } from '@udecode/plate';\n\nimport { type TodoListConfig, BaseTodoListPlugin } from '../BaseTodoListPlugin';\n\n/** Insert todo list item if selection in li>p. TODO: test */\nexport const insertTodoListItem = (\n editor: SlateEditor,\n {\n inheritCheckStateOnLineEndBreak = false,\n inheritCheckStateOnLineStartBreak = false,\n }: TodoListConfig['options']\n): boolean => {\n const todoType = editor.getType(BaseTodoListPlugin);\n\n if (!editor.selection) {\n return false;\n }\n\n const todoEntry = editor.api.above({ match: { type: todoType } });\n\n if (!todoEntry) return false;\n\n const [todo, paragraphPath] = todoEntry;\n\n let success = false;\n\n editor.tf.withoutNormalizing(() => {\n if (!editor.api.isCollapsed()) {\n editor.tf.delete();\n }\n\n const isStart = editor.api.isStart(editor.selection!.focus, paragraphPath);\n const isEnd = editor.api.isEmpty(editor.selection, { after: true });\n\n const nextParagraphPath = PathApi.next(paragraphPath);\n\n /** If start, insert a list item before */\n if (isStart) {\n editor.tf.insertNodes(\n {\n checked: inheritCheckStateOnLineStartBreak ? todo.checked : false,\n children: [{ text: '' }],\n type: todoType,\n },\n { at: paragraphPath }\n );\n\n success = true;\n\n return;\n }\n /** If not end, split the nodes */\n if (isEnd) {\n /** If end, insert a list item after and select it */\n const marks = editor.api.marks() || {};\n editor.tf.insertNodes(\n {\n checked: inheritCheckStateOnLineEndBreak ? todo.checked : false,\n children: [{ text: '', ...marks }],\n type: todoType,\n },\n { at: nextParagraphPath }\n );\n editor.tf.select(nextParagraphPath);\n } else {\n editor.tf.withoutNormalizing(() => {\n editor.tf.splitNodes();\n });\n }\n\n success = true;\n });\n\n return success;\n};\n","import {\n type ElementEntry,\n type Path,\n type SlateEditor,\n type TElement,\n NodeApi,\n PathApi,\n} from '@udecode/plate';\n\nimport { getListTypes } from '../queries/getListTypes';\n\nexport interface MoveListItemSublistItemsToListItemSublistOptions {\n /** The list item to merge. */\n fromListItem: ElementEntry;\n\n /** The list item where to merge. */\n toListItem: ElementEntry;\n\n /** Move to the start of the list instead of the end. */\n start?: boolean;\n}\n\n/**\n * Move fromListItem sublist list items to the end of `toListItem` sublist. If\n * there is no `toListItem` sublist, insert one.\n */\nexport const moveListItemSublistItemsToListItemSublist = (\n editor: SlateEditor,\n {\n fromListItem,\n start,\n toListItem,\n }: MoveListItemSublistItemsToListItemSublistOptions\n) => {\n const [, fromListItemPath] = fromListItem;\n const [, toListItemPath] = toListItem;\n let moved: boolean | void = false;\n\n editor.tf.withoutNormalizing(() => {\n const fromListItemSublist = editor.api.descendant<TElement>({\n at: fromListItemPath,\n match: {\n type: getListTypes(editor),\n },\n });\n\n if (!fromListItemSublist) return;\n\n const [, fromListItemSublistPath] = fromListItemSublist;\n\n const toListItemSublist = editor.api.descendant<TElement>({\n at: toListItemPath,\n match: {\n type: getListTypes(editor),\n },\n });\n\n let to: Path;\n\n if (!toListItemSublist) {\n const fromList = editor.api.parent(fromListItemPath);\n\n if (!fromList) return;\n\n const [fromListNode] = fromList;\n\n const fromListType = fromListNode.type;\n\n const toListItemSublistPath = toListItemPath.concat([1]);\n\n editor.tf.insertNodes(\n { children: [], type: fromListType as string },\n { at: toListItemSublistPath }\n );\n\n to = toListItemSublistPath.concat([0]);\n } else if (start) {\n const [, toListItemSublistPath] = toListItemSublist;\n to = toListItemSublistPath.concat([0]);\n } else {\n to = PathApi.next(NodeApi.lastChild(editor, toListItemSublist[1])![1]);\n }\n\n moved = editor.tf.moveNodes({\n at: fromListItemSublistPath,\n children: true,\n to,\n });\n\n // Remove the empty list\n editor.tf.delete({ at: fromListItemSublistPath });\n });\n\n return moved;\n};\n","import {\n type ElementEntry,\n type SlateEditor,\n type TElement,\n deleteMerge,\n PathApi,\n} from '@udecode/plate';\n\nimport {\n BaseListItemContentPlugin,\n BaseListItemPlugin,\n} from '../BaseListPlugin';\nimport { hasListChild } from '../queries/hasListChild';\nimport { moveListItemsToList } from './moveListItemsToList';\nimport { moveListItemSublistItemsToListItemSublist } from './moveListItemSublistItemsToListItemSublist';\n\nexport interface RemoveListItemOptions {\n list: ElementEntry;\n listItem: ElementEntry;\n reverse?: boolean;\n}\n\n/** Remove list item and move its sublist to list if any. */\nexport const removeListItem = (\n editor: SlateEditor,\n { list, listItem, reverse = true }: RemoveListItemOptions\n) => {\n const [liNode, liPath] = listItem;\n\n // Stop if the list item has no sublist\n if (editor.api.isExpanded() || !hasListChild(editor, liNode)) {\n return false;\n }\n\n const previousLiPath = PathApi.previous(liPath);\n\n let success = false;\n\n editor.tf.withoutNormalizing(() => {\n /**\n * If there is a previous li, we need to move sub-lis to the previous li. As\n * we need to delete first, we will:\n *\n * 1. Insert a temporary li: tempLi\n * 2. Move sub-lis to tempLi\n * 3. Delete\n * 4. Move sub-lis from tempLi to the previous li.\n * 5. Remove tempLi\n */\n if (previousLiPath) {\n const previousLi = editor.api.node<TElement>(previousLiPath);\n\n if (!previousLi) return;\n\n // 1\n let tempLiPath = PathApi.next(liPath);\n editor.tf.insertNodes(\n {\n children: [\n {\n children: [{ text: '' }],\n type: editor.getType(BaseListItemContentPlugin),\n },\n ],\n type: editor.getType(BaseListItemPlugin),\n },\n { at: tempLiPath }\n );\n\n const tempLi = editor.api.node<TElement>(tempLiPath);\n\n if (!tempLi) return;\n\n const tempLiPathRef = editor.api.pathRef(tempLi[1]);\n\n // 2\n moveListItemSublistItemsToListItemSublist(editor, {\n fromListItem: listItem,\n toListItem: tempLi,\n });\n\n // 3\n deleteMerge(editor, {\n reverse,\n });\n\n tempLiPath = tempLiPathRef.unref()!;\n\n // 4\n moveListItemSublistItemsToListItemSublist(editor, {\n fromListItem: [tempLi[0], tempLiPath],\n toListItem: previousLi,\n });\n\n // 5\n editor.tf.removeNodes({ at: tempLiPath });\n\n success = true;\n\n return;\n }\n\n // If it's the first li, move the sublist to the parent list\n moveListItemsToList(editor, {\n fromListItem: listItem,\n toList: list,\n toListIndex: 1,\n });\n });\n\n return success;\n};\n","import {\n type SlateEditor,\n type TElement,\n BaseParagraphPlugin,\n ElementApi,\n NodeApi,\n RangeApi,\n} from '@udecode/plate';\n\nimport {\n BaseBulletedListPlugin,\n BaseListItemContentPlugin,\n BaseListItemPlugin,\n BaseListPlugin,\n BaseNumberedListPlugin,\n} from '../BaseListPlugin';\nimport { getListItemEntry, getListTypes } from '../queries/index';\nimport { unwrapList } from './unwrapList';\n\nexport const toggleList = (editor: SlateEditor, { type }: { type: string }) =>\n editor.tf.withoutNormalizing(() => {\n if (!editor.selection) {\n return;\n }\n\n const { validLiChildrenTypes } = editor.getOptions(BaseListPlugin);\n\n if (editor.api.isCollapsed() || !editor.api.isAt({ blocks: true })) {\n // selection is collapsed\n const res = getListItemEntry(editor);\n\n if (res) {\n const { list } = res;\n\n if (list[0].type === type) {\n unwrapList(editor);\n } else {\n editor.tf.setNodes(\n { type },\n {\n at: editor.selection,\n mode: 'lowest',\n match: (n) =>\n ElementApi.isElement(n) &&\n getListTypes(editor).includes(n.type),\n }\n );\n }\n } else {\n const list = { children: [], type };\n editor.tf.wrapNodes<TElement>(list);\n\n const _nodes = editor.api.nodes({\n match: { type: editor.getType(BaseParagraphPlugin) },\n });\n const nodes = Array.from(_nodes);\n\n const blockAbove = editor.api.block({\n match: { type: validLiChildrenTypes },\n });\n\n if (!blockAbove) {\n editor.tf.setNodes({\n type: editor.getType(BaseListItemContentPlugin),\n });\n }\n\n const listItem = {\n children: [],\n type: editor.getType(BaseListItemPlugin),\n };\n\n for (const [, path] of nodes) {\n editor.tf.wrapNodes<TElement>(listItem, {\n at: path,\n });\n }\n }\n } else {\n // selection is a range\n\n const [startPoint, endPoint] = RangeApi.edges(editor.selection!);\n const commonEntry = NodeApi.common<TElement>(\n editor,\n startPoint.path,\n endPoint.path\n )!;\n\n if (\n getListTypes(editor).includes(commonEntry[0].type) ||\n (commonEntry[0] as TElement).type === editor.getType(BaseListItemPlugin)\n ) {\n if ((commonEntry[0] as TElement).type === type) {\n unwrapList(editor);\n } else {\n const startList = editor.api.node({\n at: RangeApi.start(editor.selection),\n match: { type: getListTypes(editor) },\n mode: 'lowest',\n });\n const endList = editor.api.node({\n at: RangeApi.end(editor.selection),\n match: { type: getListTypes(editor) },\n mode: 'lowest',\n });\n const rangeLength = Math.min(\n startList![1].length,\n endList![1].length\n );\n\n editor.tf.setNodes(\n { type },\n {\n at: editor.selection,\n mode: 'all',\n match: (n, path) =>\n ElementApi.isElement(n) &&\n getListTypes(editor).includes(n.type) &&\n path.length >= rangeLength,\n }\n );\n }\n } else {\n const rootPathLength = commonEntry[1].length;\n const _nodes = editor.api.nodes<TElement>({\n mode: 'all',\n });\n const nodes = Array.from(_nodes).filter(\n ([, path]) => path.length === rootPathLength + 1\n );\n\n nodes.forEach((n) => {\n if (getListTypes(editor).includes(n[0].type)) {\n editor.tf.setNodes(\n { type },\n {\n at: n[1],\n mode: 'all',\n match: (_n) =>\n ElementApi.isElement(_n) &&\n getListTypes(editor).includes(_n.type),\n }\n );\n } else {\n if (!validLiChildrenTypes?.includes(n[0].type)) {\n editor.tf.setNodes(\n { type: editor.getType(BaseListItemContentPlugin) },\n { at: n[1] }\n );\n }\n\n const listItem = {\n children: [],\n type: editor.getType(BaseListItemPlugin),\n };\n editor.tf.wrapNodes<TElement>(listItem, {\n at: n[1],\n });\n\n const list = { children: [], type };\n editor.tf.wrapNodes<TElement>(list, { at: n[1] });\n }\n });\n }\n }\n });\n\nexport const toggleBulletedList = (editor: SlateEditor) =>\n toggleList(editor, { type: editor.getType(BaseBulletedListPlugin) });\n\nexport const toggleNumberedList = (editor: SlateEditor) =>\n toggleList(editor, { type: editor.getType(BaseNumberedListPlugin) });\n","import {\n type EditorTransforms,\n type ElementEntry,\n type OverrideEditor,\n type SlateEditor,\n type TElement,\n NodeApi,\n PathApi,\n} from '@udecode/plate';\n\nimport type { ListConfig } from './BaseListPlugin';\n\nimport {\n BaseListItemContentPlugin,\n BaseListItemPlugin,\n} from './BaseListPlugin';\nimport {\n getListItemEntry,\n getListRoot,\n hasListChild,\n isAcrossListItems,\n} from './queries/index';\nimport {\n moveListItemsToList,\n moveListItemUp,\n removeFirstListItem,\n removeListItem,\n} from './transforms/index';\n\nconst selectionIsNotInAListHandler = (editor: SlateEditor): boolean => {\n const pointAfterSelection = editor.api.after(editor.selection!.focus);\n\n if (pointAfterSelection) {\n // there is a block after it\n const nextSiblingListRes = getListItemEntry(editor, {\n at: pointAfterSelection,\n });\n\n if (nextSiblingListRes) {\n // the next block is a list\n const { listItem } = nextSiblingListRes;\n const parentBlockEntity = editor.api.block({\n at: editor.selection!.anchor,\n });\n\n if (!editor.api.string(parentBlockEntity![1])) {\n // the selected block is empty\n editor.tf.removeNodes();\n\n return true;\n }\n if (hasListChild(editor, listItem[0])) {\n // the next block has children, so we have to move the first item up\n const sublistRes = getListItemEntry(editor, {\n at: [...listItem[1], 1, 0, 0],\n });\n\n moveListItemUp(editor, sublistRes!);\n }\n }\n }\n\n return false;\n};\n\nconst selectionIsInAListHandler = (\n editor: SlateEditor,\n res: { list: ElementEntry; listItem: ElementEntry },\n defaultDelete: EditorTransforms['deleteBackward'],\n unit: 'block' | 'character' | 'line' | 'word' = 'character'\n): boolean => {\n const { listItem } = res;\n\n // if it has no children\n if (!hasListChild(editor, listItem[0])) {\n const liType = editor.getType(BaseListItemPlugin);\n const _nodes = editor.api.nodes({\n at: listItem[1],\n mode: 'lowest',\n match: (node, path) => {\n if (path.length === 0) {\n return false;\n }\n\n const isNodeLi = (node as TElement).type === liType;\n const isSiblingOfNodeLi =\n NodeApi.get<TElement>(editor, PathApi.next(path))?.type === liType;\n\n return isNodeLi && isSiblingOfNodeLi;\n },\n });\n const liWithSiblings = Array.from(_nodes, (entry) => entry[1])[0];\n\n if (!liWithSiblings) {\n // there are no more list item in the list\n const pointAfterListItem = editor.api.after(listItem[1]);\n\n if (pointAfterListItem) {\n // there is a block after it\n const nextSiblingListRes = getListItemEntry(editor, {\n at: pointAfterListItem,\n });\n\n if (nextSiblingListRes) {\n // it is a list so we merge the lists\n const listRoot = getListRoot(editor, listItem[1]);\n\n moveListItemsToList(editor, {\n deleteFromList: true,\n fromList: nextSiblingListRes.list,\n toList: listRoot,\n });\n\n return true;\n }\n }\n\n return false;\n }\n\n const siblingListItem = editor.api.node<TElement>(\n PathApi.next(liWithSiblings)\n );\n\n if (!siblingListItem) return false;\n\n const siblingList = editor.api.parent<TElement>(siblingListItem[1]);\n\n if (\n siblingList &&\n removeListItem(editor, {\n list: siblingList,\n listItem: siblingListItem,\n reverse: false,\n })\n ) {\n return true;\n }\n\n const pointAfterListItem = editor.api.after(editor.selection!.focus);\n\n if (\n !pointAfterListItem ||\n !isAcrossListItems(editor, {\n anchor: editor.selection!.anchor,\n focus: pointAfterListItem,\n })\n ) {\n return false;\n }\n\n // get closest lic ancestor of next selectable\n const licType = editor.getType(BaseListItemContentPlugin);\n const _licNodes = editor.api.nodes<TElement>({\n at: pointAfterListItem.path,\n mode: 'lowest',\n match: (node) => node.type === licType,\n });\n const nextSelectableLic = [..._licNodes][0];\n\n // let slate handle single child cases\n if (nextSelectableLic[0].children.length < 2) return false;\n\n // manually run default delete\n defaultDelete(unit);\n\n const leftoverListItem = editor.api.node<TElement>(\n PathApi.parent(nextSelectableLic[1])\n )!;\n\n if (leftoverListItem && leftoverListItem[0].children.length === 0) {\n // remove the leftover empty list item\n editor.tf.removeNodes({ at: leftoverListItem[1] });\n }\n\n return true;\n }\n\n // if it has children\n const nestedList = editor.api.node<TElement>(\n PathApi.next([...listItem[1], 0])\n );\n\n if (!nestedList) return false;\n\n const nestedListItem = Array.from(\n NodeApi.children<TElement>(editor, nestedList[1])\n )[0];\n\n if (\n removeFirstListItem(editor, {\n list: nestedList,\n listItem: nestedListItem,\n })\n ) {\n return true;\n }\n if (\n removeListItem(editor, {\n list: nestedList,\n listItem: nestedListItem,\n })\n ) {\n return true;\n }\n\n return false;\n};\n\nexport const withDeleteForwardList: OverrideEditor<ListConfig> = ({\n editor,\n tf: { deleteForward },\n}) => ({\n transforms: {\n deleteForward(unit) {\n const deleteForwardList = () => {\n let skipDefaultDelete = false;\n\n if (!editor?.selection) {\n return skipDefaultDelete;\n }\n if (!editor.api.isAt({ end: true })) {\n return skipDefaultDelete;\n }\n\n editor.tf.withoutNormalizing(() => {\n const res = getListItemEntry(editor, {});\n\n if (!res) {\n skipDefaultDelete = selectionIsNotInAListHandler(editor);\n\n return;\n }\n\n skipDefaultDelete = selectionIsInAListHandler(\n editor,\n res,\n deleteForward,\n unit\n );\n });\n\n return skipDefaultDelete;\n };\n\n if (deleteForwardList()) return;\n\n deleteForward(unit);\n },\n },\n});\n","import {\n type OverrideEditor,\n type SlateEditor,\n type TRange,\n deleteMerge,\n} from '@udecode/plate';\n\nimport { type ListConfig, BaseListItemPlugin } from './BaseListPlugin';\nimport { getHighestEmptyList } from './queries/getHighestEmptyList';\nimport { hasListChild } from './queries/hasListChild';\nimport { isAcrossListItems } from './queries/isAcrossListItems';\n\nconst getLiStart = (editor: SlateEditor) => {\n const start = editor.api.start(editor.selection as TRange);\n\n return editor.api.above({\n at: start,\n match: { type: editor.getType(BaseListItemPlugin) },\n });\n};\n\nexport const withDeleteFragmentList: OverrideEditor<ListConfig> = ({\n editor,\n tf: { deleteFragment },\n}) => ({\n transforms: {\n deleteFragment(direction) {\n const deleteFragmentList = () => {\n let deleted = false;\n\n editor.tf.withoutNormalizing(() => {\n // Selection should be across list items\n if (!isAcrossListItems(editor)) return;\n\n /**\n * Check if the end li can be deleted (if it has no sublist). Store\n * the path ref to delete it after deleteMerge.\n */\n const end = editor.api.end(editor.selection as TRange);\n const liEnd = editor.api.above({\n at: end,\n match: { type: editor.getType(BaseListItemPlugin) },\n });\n const liEndCanBeDeleted = liEnd && !hasListChild(editor, liEnd[0]);\n const liEndPathRef = liEndCanBeDeleted\n ? editor.api.pathRef(liEnd![1])\n : undefined;\n\n // use deleteFragment when selection wrapped around list\n if (!getLiStart(editor) || !liEnd) {\n deleted = false;\n\n return;\n }\n\n /** Delete fragment and move end block children to start block */\n deleteMerge(editor);\n\n const liStart = getLiStart(editor);\n\n if (liEndPathRef) {\n const liEndPath = liEndPathRef.unref()!;\n const listStart = liStart && editor.api.parent(liStart[1]);\n\n const deletePath = getHighestEmptyList(editor, {\n diffListPath: listStart?.[1],\n liPath: liEndPath,\n });\n\n if (deletePath) {\n editor.tf.removeNodes({ at: deletePath });\n }\n\n deleted = true;\n }\n });\n\n return deleted;\n };\n\n if (deleteFragmentList()) return;\n\n deleteFragment(direction);\n },\n },\n});\n","import {\n type Ancestor,\n type AncestorEntry,\n type Descendant,\n type DescendantEntry,\n type ElementEntry,\n type OverrideEditor,\n type Path,\n type TElement,\n type TText,\n ElementApi,\n NodeApi,\n PathApi,\n} from '@udecode/plate';\n\nimport {\n type ListConfig,\n BaseListItemContentPlugin,\n BaseListItemPlugin,\n} from './BaseListPlugin';\nimport { isListRoot } from './queries';\n\nexport const withInsertFragmentList: OverrideEditor<ListConfig> = ({\n editor,\n tf: { insertFragment