@tiptap/extension-list 3.24.0 → 3.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,114 @@
1
1
  // src/item/list-item.ts
2
2
  import { mergeAttributes, Node, renderNestedMarkdownContent } from "@tiptap/core";
3
+
4
+ // src/helpers/createBranchingListDeleteKeymap.ts
5
+ import { Extension } from "@tiptap/core";
6
+
7
+ // src/helpers/hoistBranchingNestedList.ts
8
+ import { Fragment } from "@tiptap/pm/model";
9
+
10
+ // src/helpers/getBranchingNestedListAtCursor.ts
11
+ var getBranchingNestedListAtCursor = (state, itemName, wrapperNames) => {
12
+ const { selection } = state;
13
+ if (!selection.empty) {
14
+ return null;
15
+ }
16
+ const { $from } = selection;
17
+ if (!$from.parent.isTextblock) {
18
+ return null;
19
+ }
20
+ if ($from.parentOffset !== $from.parent.content.size) {
21
+ return null;
22
+ }
23
+ let listItemDepth = -1;
24
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
25
+ if ($from.node(depth).type.name === itemName) {
26
+ listItemDepth = depth;
27
+ break;
28
+ }
29
+ }
30
+ if (listItemDepth < 0) {
31
+ return null;
32
+ }
33
+ const listItem = $from.node(listItemDepth);
34
+ const indexInListItem = $from.index(listItemDepth);
35
+ if (indexInListItem + 1 >= listItem.childCount) {
36
+ return null;
37
+ }
38
+ const nextChild = listItem.child(indexInListItem + 1);
39
+ if (!wrapperNames.includes(nextChild.type.name)) {
40
+ return null;
41
+ }
42
+ const itemType = state.schema.nodes[itemName];
43
+ let hasBranching = false;
44
+ nextChild.forEach((child) => {
45
+ if (child.type === itemType && child.childCount > 1) {
46
+ hasBranching = true;
47
+ }
48
+ });
49
+ if (!hasBranching) {
50
+ return null;
51
+ }
52
+ const nodeAfter = state.doc.resolve($from.after()).nodeAfter;
53
+ if (!nodeAfter || !wrapperNames.includes(nodeAfter.type.name)) {
54
+ return null;
55
+ }
56
+ const items = [];
57
+ nodeAfter.forEach((child) => {
58
+ items.push(child);
59
+ });
60
+ if (items.length === 0) {
61
+ return null;
62
+ }
63
+ return {
64
+ listItemDepth,
65
+ nestedList: nodeAfter,
66
+ nestedListPos: $from.after(),
67
+ insertPos: $from.after(listItemDepth),
68
+ items
69
+ };
70
+ };
71
+
72
+ // src/helpers/hoistBranchingNestedList.ts
73
+ var hoistBranchingNestedList = (state, dispatch, itemName, wrapperNames) => {
74
+ const context = getBranchingNestedListAtCursor(state, itemName, wrapperNames);
75
+ if (!context) {
76
+ return false;
77
+ }
78
+ const { selection } = state;
79
+ const { nestedList, nestedListPos, insertPos, items } = context;
80
+ const tr = state.tr;
81
+ tr.delete(nestedListPos, nestedListPos + nestedList.nodeSize);
82
+ const mappedInsertPos = tr.mapping.map(insertPos);
83
+ tr.insert(mappedInsertPos, Fragment.from(items));
84
+ tr.setSelection(selection.map(tr.doc, tr.mapping));
85
+ if (dispatch) {
86
+ dispatch(tr);
87
+ }
88
+ return true;
89
+ };
90
+
91
+ // src/helpers/handleDeleteBranchingNestedList.ts
92
+ var handleDeleteBranchingNestedList = (editor, itemName, wrapperNames) => {
93
+ return hoistBranchingNestedList(editor.state, editor.view.dispatch, itemName, wrapperNames);
94
+ };
95
+
96
+ // src/helpers/createBranchingListDeleteKeymap.ts
97
+ var createBranchingListDeleteKeymap = (itemName, wrapperNames) => {
98
+ return Extension.create({
99
+ name: `${itemName}BranchingDeleteKeymap`,
100
+ priority: 101,
101
+ addKeyboardShortcuts() {
102
+ const handleDelete = () => handleDeleteBranchingNestedList(this.editor, itemName, wrapperNames);
103
+ return {
104
+ Delete: handleDelete,
105
+ "Mod-Delete": handleDelete
106
+ };
107
+ }
108
+ });
109
+ };
110
+
111
+ // src/item/list-item.ts
3
112
  function isSameLineOrderedListToken(token) {
4
113
  var _a, _b;
5
114
  const nestedToken = (_a = token.tokens) == null ? void 0 : _a[0];
@@ -114,6 +223,14 @@ var ListItem = Node.create({
114
223
  ctx
115
224
  );
116
225
  },
226
+ addExtensions() {
227
+ return [
228
+ createBranchingListDeleteKeymap(this.name, [
229
+ this.options.bulletListTypeName,
230
+ this.options.orderedListTypeName
231
+ ])
232
+ ];
233
+ },
117
234
  addKeyboardShortcuts() {
118
235
  return {
119
236
  Enter: () => this.editor.commands.splitListItem(this.name),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/item/list-item.ts"],"sourcesContent":["import type { JSONContent, MarkdownParseHelpers, MarkdownToken } from '@tiptap/core'\nimport { mergeAttributes, Node, renderNestedMarkdownContent } from '@tiptap/core'\n\nexport interface ListItemOptions {\n /**\n * The HTML attributes for a list item node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>\n\n /**\n * The node type for bulletList nodes\n * @default 'bulletList'\n * @example 'myCustomBulletList'\n */\n bulletListTypeName: string\n\n /**\n * The node type for orderedList nodes\n * @default 'orderedList'\n * @example 'myCustomOrderedList'\n */\n orderedListTypeName: string\n}\n\nfunction isSameLineOrderedListToken(token: MarkdownToken): boolean {\n const nestedToken = token.tokens?.[0]\n\n return Boolean(\n token.text &&\n token.tokens?.length === 1 &&\n nestedToken?.type === 'list' &&\n nestedToken.ordered &&\n nestedToken.raw === token.text,\n )\n}\n\nfunction parseSameLineOrderedListText(text: string, helpers: MarkdownParseHelpers): JSONContent[] {\n if (helpers.tokenizeInline) {\n return helpers.parseInline(helpers.tokenizeInline(text))\n }\n\n return helpers.parseInline([\n {\n type: 'text',\n raw: text,\n text,\n },\n ])\n}\n\n/**\n * This extension allows you to create list items.\n * @see https://www.tiptap.dev/api/nodes/list-item\n */\nexport const ListItem = Node.create<ListItemOptions>({\n name: 'listItem',\n\n addOptions() {\n return {\n HTMLAttributes: {},\n bulletListTypeName: 'bulletList',\n orderedListTypeName: 'orderedList',\n }\n },\n\n content: 'paragraph block*',\n\n defining: true,\n\n parseHTML() {\n return [\n {\n tag: 'li',\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['li', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n markdownTokenName: 'list_item',\n\n parseMarkdown: (token, helpers) => {\n if (token.type !== 'list_item') {\n return []\n }\n\n const parseBlockChildren = helpers.parseBlockChildren ?? helpers.parseChildren\n let content: any[] = []\n\n if (token.tokens && token.tokens.length > 0) {\n if (isSameLineOrderedListToken(token)) {\n return {\n type: 'listItem',\n content: [\n {\n type: 'paragraph',\n content: parseSameLineOrderedListText(token.text || '', helpers),\n },\n ],\n }\n }\n\n // Check if we have paragraph tokens (complex list items)\n const hasParagraphTokens = token.tokens.some(t => t.type === 'paragraph')\n\n if (hasParagraphTokens) {\n // If we have paragraph tokens, parse them as block elements\n content = parseBlockChildren(token.tokens)\n } else {\n // Check if the first token is a text token with nested inline tokens\n const firstToken = token.tokens[0]\n\n if (\n firstToken &&\n firstToken.type === 'text' &&\n firstToken.tokens &&\n firstToken.tokens.length > 0\n ) {\n // Parse the inline content from the text token\n const inlineContent = helpers.parseInline(firstToken.tokens)\n\n // Start with the paragraph containing the inline content\n content = [\n {\n type: 'paragraph',\n content: inlineContent,\n },\n ]\n\n // If there are additional tokens after the first text token (like nested lists),\n // parse them as block elements and add them\n if (token.tokens.length > 1) {\n const remainingTokens = token.tokens.slice(1)\n const additionalContent = parseBlockChildren(remainingTokens)\n content.push(...additionalContent)\n }\n } else {\n // Fallback: parse all tokens as block elements\n content = parseBlockChildren(token.tokens)\n }\n }\n }\n\n // Ensure we always have at least an empty paragraph\n if (content.length === 0) {\n content = [\n {\n type: 'paragraph',\n content: [],\n },\n ]\n }\n\n return {\n type: 'listItem',\n content,\n }\n },\n\n renderMarkdown: (node, h, ctx) => {\n return renderNestedMarkdownContent(\n node,\n h,\n (context: any) => {\n if (context.parentType === 'bulletList') {\n return '- '\n }\n if (context.parentType === 'orderedList') {\n const start = context.meta?.parentAttrs?.start || 1\n return `${start + context.index}. `\n }\n // Fallback to bullet list for unknown parent types\n return '- '\n },\n ctx,\n )\n },\n\n addKeyboardShortcuts() {\n return {\n Enter: () => this.editor.commands.splitListItem(this.name),\n Tab: () => this.editor.commands.sinkListItem(this.name),\n 'Shift-Tab': () => this.editor.commands.liftListItem(this.name),\n }\n },\n})\n"],"mappings":";AACA,SAAS,iBAAiB,MAAM,mCAAmC;AAyBnE,SAAS,2BAA2B,OAA+B;AA1BnE;AA2BE,QAAM,eAAc,WAAM,WAAN,mBAAe;AAEnC,SAAO;AAAA,IACL,MAAM,UACN,WAAM,WAAN,mBAAc,YAAW,MACzB,2CAAa,UAAS,UACtB,YAAY,WACZ,YAAY,QAAQ,MAAM;AAAA,EAC5B;AACF;AAEA,SAAS,6BAA6B,MAAc,SAA8C;AAChG,MAAI,QAAQ,gBAAgB;AAC1B,WAAO,QAAQ,YAAY,QAAQ,eAAe,IAAI,CAAC;AAAA,EACzD;AAEA,SAAO,QAAQ,YAAY;AAAA,IACzB;AAAA,MACE,MAAM;AAAA,MACN,KAAK;AAAA,MACL;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAMO,IAAM,WAAW,KAAK,OAAwB;AAAA,EACnD,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,gBAAgB,CAAC;AAAA,MACjB,oBAAoB;AAAA,MACpB,qBAAqB;AAAA,IACvB;AAAA,EACF;AAAA,EAEA,SAAS;AAAA,EAET,UAAU;AAAA,EAEV,YAAY;AACV,WAAO;AAAA,MACL;AAAA,QACE,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAAA,EAEA,WAAW,EAAE,eAAe,GAAG;AAC7B,WAAO,CAAC,MAAM,gBAAgB,KAAK,QAAQ,gBAAgB,cAAc,GAAG,CAAC;AAAA,EAC/E;AAAA,EAEA,mBAAmB;AAAA,EAEnB,eAAe,CAAC,OAAO,YAAY;AArFrC;AAsFI,QAAI,MAAM,SAAS,aAAa;AAC9B,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,sBAAqB,aAAQ,uBAAR,YAA8B,QAAQ;AACjE,QAAI,UAAiB,CAAC;AAEtB,QAAI,MAAM,UAAU,MAAM,OAAO,SAAS,GAAG;AAC3C,UAAI,2BAA2B,KAAK,GAAG;AACrC,eAAO;AAAA,UACL,MAAM;AAAA,UACN,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,SAAS,6BAA6B,MAAM,QAAQ,IAAI,OAAO;AAAA,YACjE;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,YAAM,qBAAqB,MAAM,OAAO,KAAK,OAAK,EAAE,SAAS,WAAW;AAExE,UAAI,oBAAoB;AAEtB,kBAAU,mBAAmB,MAAM,MAAM;AAAA,MAC3C,OAAO;AAEL,cAAM,aAAa,MAAM,OAAO,CAAC;AAEjC,YACE,cACA,WAAW,SAAS,UACpB,WAAW,UACX,WAAW,OAAO,SAAS,GAC3B;AAEA,gBAAM,gBAAgB,QAAQ,YAAY,WAAW,MAAM;AAG3D,oBAAU;AAAA,YACR;AAAA,cACE,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,UACF;AAIA,cAAI,MAAM,OAAO,SAAS,GAAG;AAC3B,kBAAM,kBAAkB,MAAM,OAAO,MAAM,CAAC;AAC5C,kBAAM,oBAAoB,mBAAmB,eAAe;AAC5D,oBAAQ,KAAK,GAAG,iBAAiB;AAAA,UACnC;AAAA,QACF,OAAO;AAEL,oBAAU,mBAAmB,MAAM,MAAM;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAGA,QAAI,QAAQ,WAAW,GAAG;AACxB,gBAAU;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,SAAS,CAAC;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAAA,EAEA,gBAAgB,CAAC,MAAM,GAAG,QAAQ;AAChC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,CAAC,YAAiB;AAvKxB;AAwKQ,YAAI,QAAQ,eAAe,cAAc;AACvC,iBAAO;AAAA,QACT;AACA,YAAI,QAAQ,eAAe,eAAe;AACxC,gBAAM,UAAQ,mBAAQ,SAAR,mBAAc,gBAAd,mBAA2B,UAAS;AAClD,iBAAO,GAAG,QAAQ,QAAQ,KAAK;AAAA,QACjC;AAEA,eAAO;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,uBAAuB;AACrB,WAAO;AAAA,MACL,OAAO,MAAM,KAAK,OAAO,SAAS,cAAc,KAAK,IAAI;AAAA,MACzD,KAAK,MAAM,KAAK,OAAO,SAAS,aAAa,KAAK,IAAI;AAAA,MACtD,aAAa,MAAM,KAAK,OAAO,SAAS,aAAa,KAAK,IAAI;AAAA,IAChE;AAAA,EACF;AACF,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../src/item/list-item.ts","../../src/helpers/createBranchingListDeleteKeymap.ts","../../src/helpers/hoistBranchingNestedList.ts","../../src/helpers/getBranchingNestedListAtCursor.ts","../../src/helpers/handleDeleteBranchingNestedList.ts"],"sourcesContent":["import type { JSONContent, MarkdownParseHelpers, MarkdownToken } from '@tiptap/core'\nimport { mergeAttributes, Node, renderNestedMarkdownContent } from '@tiptap/core'\n\nimport { createBranchingListDeleteKeymap } from '../helpers/createBranchingListDeleteKeymap.js'\n\nexport interface ListItemOptions {\n /**\n * The HTML attributes for a list item node.\n * @default {}\n * @example { class: 'foo' }\n */\n HTMLAttributes: Record<string, any>\n\n /**\n * The node type for bulletList nodes\n * @default 'bulletList'\n * @example 'myCustomBulletList'\n */\n bulletListTypeName: string\n\n /**\n * The node type for orderedList nodes\n * @default 'orderedList'\n * @example 'myCustomOrderedList'\n */\n orderedListTypeName: string\n}\n\nfunction isSameLineOrderedListToken(token: MarkdownToken): boolean {\n const nestedToken = token.tokens?.[0]\n\n return Boolean(\n token.text &&\n token.tokens?.length === 1 &&\n nestedToken?.type === 'list' &&\n nestedToken.ordered &&\n nestedToken.raw === token.text,\n )\n}\n\nfunction parseSameLineOrderedListText(text: string, helpers: MarkdownParseHelpers): JSONContent[] {\n if (helpers.tokenizeInline) {\n return helpers.parseInline(helpers.tokenizeInline(text))\n }\n\n return helpers.parseInline([\n {\n type: 'text',\n raw: text,\n text,\n },\n ])\n}\n\n/**\n * This extension allows you to create list items.\n * @see https://www.tiptap.dev/api/nodes/list-item\n */\nexport const ListItem = Node.create<ListItemOptions>({\n name: 'listItem',\n\n addOptions() {\n return {\n HTMLAttributes: {},\n bulletListTypeName: 'bulletList',\n orderedListTypeName: 'orderedList',\n }\n },\n\n content: 'paragraph block*',\n\n defining: true,\n\n parseHTML() {\n return [\n {\n tag: 'li',\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['li', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n markdownTokenName: 'list_item',\n\n parseMarkdown: (token, helpers) => {\n if (token.type !== 'list_item') {\n return []\n }\n\n const parseBlockChildren = helpers.parseBlockChildren ?? helpers.parseChildren\n let content: any[] = []\n\n if (token.tokens && token.tokens.length > 0) {\n if (isSameLineOrderedListToken(token)) {\n return {\n type: 'listItem',\n content: [\n {\n type: 'paragraph',\n content: parseSameLineOrderedListText(token.text || '', helpers),\n },\n ],\n }\n }\n\n // Check if we have paragraph tokens (complex list items)\n const hasParagraphTokens = token.tokens.some(t => t.type === 'paragraph')\n\n if (hasParagraphTokens) {\n // If we have paragraph tokens, parse them as block elements\n content = parseBlockChildren(token.tokens)\n } else {\n // Check if the first token is a text token with nested inline tokens\n const firstToken = token.tokens[0]\n\n if (\n firstToken &&\n firstToken.type === 'text' &&\n firstToken.tokens &&\n firstToken.tokens.length > 0\n ) {\n // Parse the inline content from the text token\n const inlineContent = helpers.parseInline(firstToken.tokens)\n\n // Start with the paragraph containing the inline content\n content = [\n {\n type: 'paragraph',\n content: inlineContent,\n },\n ]\n\n // If there are additional tokens after the first text token (like nested lists),\n // parse them as block elements and add them\n if (token.tokens.length > 1) {\n const remainingTokens = token.tokens.slice(1)\n const additionalContent = parseBlockChildren(remainingTokens)\n content.push(...additionalContent)\n }\n } else {\n // Fallback: parse all tokens as block elements\n content = parseBlockChildren(token.tokens)\n }\n }\n }\n\n // Ensure we always have at least an empty paragraph\n if (content.length === 0) {\n content = [\n {\n type: 'paragraph',\n content: [],\n },\n ]\n }\n\n return {\n type: 'listItem',\n content,\n }\n },\n\n renderMarkdown: (node, h, ctx) => {\n return renderNestedMarkdownContent(\n node,\n h,\n (context: any) => {\n if (context.parentType === 'bulletList') {\n return '- '\n }\n if (context.parentType === 'orderedList') {\n const start = context.meta?.parentAttrs?.start || 1\n return `${start + context.index}. `\n }\n // Fallback to bullet list for unknown parent types\n return '- '\n },\n ctx,\n )\n },\n\n addExtensions() {\n return [\n createBranchingListDeleteKeymap(this.name, [\n this.options.bulletListTypeName,\n this.options.orderedListTypeName,\n ]),\n ]\n },\n\n addKeyboardShortcuts() {\n return {\n Enter: () => this.editor.commands.splitListItem(this.name),\n Tab: () => this.editor.commands.sinkListItem(this.name),\n 'Shift-Tab': () => this.editor.commands.liftListItem(this.name),\n }\n },\n})\n","import { Extension } from '@tiptap/core'\n\nimport { handleDeleteBranchingNestedList } from './handleDeleteBranchingNestedList.js'\n\n/**\n * Creates a high-priority keymap extension that handles Delete for branching nested lists.\n * Kept separate from the list item node so Enter/Tab shortcuts keep their default priority.\n */\nexport const createBranchingListDeleteKeymap = (itemName: string, wrapperNames: string[]) => {\n return Extension.create({\n name: `${itemName}BranchingDeleteKeymap`,\n priority: 101,\n\n addKeyboardShortcuts() {\n const handleDelete = () =>\n handleDeleteBranchingNestedList(this.editor, itemName, wrapperNames)\n\n return {\n Delete: handleDelete,\n 'Mod-Delete': handleDelete,\n }\n },\n })\n}\n","import { Fragment } from '@tiptap/pm/model'\nimport type { EditorState, Transaction } from '@tiptap/pm/state'\n\nimport { getBranchingNestedListAtCursor } from './getBranchingNestedListAtCursor.js'\n\n/**\n * Hoists all list items from a branching nested list after the cursor into the parent list.\n *\n * Use this when `joinForward` cannot restructure a nested list that contains list items\n * with sublists (see issue #6906).\n *\n * @param state - The editor state to transform.\n * @param dispatch - Optional dispatch function for the transaction.\n * @param itemName - The list item node name (for example `listItem` or `taskItem`).\n * @param wrapperNames - List wrapper node names (for example `bulletList` and `orderedList`).\n * @returns `true` when the nested list was hoisted, otherwise `false`.\n *\n * @example\n * ```ts\n * // Cursor at the end of \"Item 1\" before a nested list with branching items.\n * hoistBranchingNestedList(editor.state, editor.view.dispatch, 'listItem', [\n * 'bulletList',\n * 'orderedList',\n * ])\n * ```\n */\nexport const hoistBranchingNestedList = (\n state: EditorState,\n dispatch: ((tr: Transaction) => void) | undefined,\n itemName: string,\n wrapperNames: string[],\n) => {\n const context = getBranchingNestedListAtCursor(state, itemName, wrapperNames)\n\n if (!context) {\n return false\n }\n\n const { selection } = state\n const { nestedList, nestedListPos, insertPos, items } = context\n const tr = state.tr\n\n tr.delete(nestedListPos, nestedListPos + nestedList.nodeSize)\n\n const mappedInsertPos = tr.mapping.map(insertPos)\n\n tr.insert(mappedInsertPos, Fragment.from(items))\n\n tr.setSelection(selection.map(tr.doc, tr.mapping))\n\n if (dispatch) {\n dispatch(tr)\n }\n\n return true\n}\n","import type { Node } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nexport type BranchingNestedListAtCursor = {\n listItemDepth: number\n nestedList: Node\n nestedListPos: number\n insertPos: number\n items: Node[]\n}\n\n/**\n * Resolves a branching nested list immediately after the cursor when the selection is\n * collapsed at the end of a textblock inside a list item.\n *\n * @param state - The editor state to inspect.\n * @param itemName - The list item node name (for example `listItem` or `taskItem`).\n * @param wrapperNames - List wrapper node names (for example `bulletList` and `orderedList`).\n * @returns Resolved positions and nodes for hoisting, or `null` when not applicable.\n *\n * @example\n * ```ts\n * const context = getBranchingNestedListAtCursor(editor.state, 'listItem', [\n * 'bulletList',\n * 'orderedList',\n * ])\n *\n * if (context) {\n * // cursor is at the end of Item 1 before a branching nested sublist\n * }\n * ```\n */\nexport const getBranchingNestedListAtCursor = (\n state: EditorState,\n itemName: string,\n wrapperNames: string[],\n): BranchingNestedListAtCursor | null => {\n const { selection } = state\n\n if (!selection.empty) {\n return null\n }\n\n const { $from } = selection\n\n if (!$from.parent.isTextblock) {\n return null\n }\n\n if ($from.parentOffset !== $from.parent.content.size) {\n return null\n }\n\n let listItemDepth = -1\n\n for (let depth = $from.depth; depth > 0; depth -= 1) {\n if ($from.node(depth).type.name === itemName) {\n listItemDepth = depth\n break\n }\n }\n\n if (listItemDepth < 0) {\n return null\n }\n\n const listItem = $from.node(listItemDepth)\n const indexInListItem = $from.index(listItemDepth)\n\n if (indexInListItem + 1 >= listItem.childCount) {\n return null\n }\n\n const nextChild = listItem.child(indexInListItem + 1)\n\n if (!wrapperNames.includes(nextChild.type.name)) {\n return null\n }\n\n const itemType = state.schema.nodes[itemName]\n let hasBranching = false\n\n nextChild.forEach(child => {\n if (child.type === itemType && child.childCount > 1) {\n hasBranching = true\n }\n })\n\n if (!hasBranching) {\n return null\n }\n\n const nodeAfter = state.doc.resolve($from.after()).nodeAfter\n\n if (!nodeAfter || !wrapperNames.includes(nodeAfter.type.name)) {\n return null\n }\n\n const items: Node[] = []\n\n nodeAfter.forEach(child => {\n items.push(child)\n })\n\n if (items.length === 0) {\n return null\n }\n\n return {\n listItemDepth,\n nestedList: nodeAfter,\n nestedListPos: $from.after(),\n insertPos: $from.after(listItemDepth),\n items,\n }\n}\n","import type { Editor } from '@tiptap/core'\n\nimport { hoistBranchingNestedList } from './hoistBranchingNestedList.js'\n\n/**\n * Handles Delete for a list item when a branching nested sublist follows the cursor.\n *\n * @param editor - The editor instance whose state should be updated.\n * @param itemName - The list item node name (for example `listItem` or `taskItem`).\n * @param wrapperNames - List wrapper node names (for example `bulletList` and `orderedList`).\n * @returns `true` when the nested list was hoisted, otherwise `false`.\n *\n * @example\n * ```ts\n * Delete: () =>\n * handleDeleteBranchingNestedList(editor, 'listItem', ['bulletList', 'orderedList']),\n * ```\n */\nexport const handleDeleteBranchingNestedList = (\n editor: Editor,\n itemName: string,\n wrapperNames: string[],\n) => {\n return hoistBranchingNestedList(editor.state, editor.view.dispatch, itemName, wrapperNames)\n}\n"],"mappings":"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","names":[]}
@@ -79,7 +79,7 @@ var getNextListDepth = (typeOrName, state) => {
79
79
  };
80
80
 
81
81
  // src/keymap/listHelpers/handleBackspace.ts
82
- var import_core4 = require("@tiptap/core");
82
+ var import_core3 = require("@tiptap/core");
83
83
 
84
84
  // src/keymap/listHelpers/hasListBefore.ts
85
85
  var hasListBefore = (editorState, name, parentListTypes) => {
@@ -92,36 +92,6 @@ var hasListBefore = (editorState, name, parentListTypes) => {
92
92
  return true;
93
93
  };
94
94
 
95
- // src/keymap/listHelpers/hasListItemBefore.ts
96
- var hasListItemBefore = (typeOrName, state) => {
97
- var _a;
98
- const { $anchor } = state.selection;
99
- const $targetPos = state.doc.resolve($anchor.pos - 2);
100
- if ($targetPos.index() === 0) {
101
- return false;
102
- }
103
- if (((_a = $targetPos.nodeBefore) == null ? void 0 : _a.type.name) !== typeOrName) {
104
- return false;
105
- }
106
- return true;
107
- };
108
-
109
- // src/keymap/listHelpers/listItemHasSubList.ts
110
- var import_core3 = require("@tiptap/core");
111
- var listItemHasSubList = (typeOrName, state, node) => {
112
- if (!node) {
113
- return false;
114
- }
115
- const nodeType = (0, import_core3.getNodeType)(typeOrName, state.schema);
116
- let hasSubList = false;
117
- node.descendants((child) => {
118
- if (child.type === nodeType) {
119
- hasSubList = true;
120
- }
121
- });
122
- return hasSubList;
123
- };
124
-
125
95
  // src/keymap/listHelpers/handleBackspace.ts
126
96
  var handleBackspace = (editor, name, parentListTypes) => {
127
97
  if (editor.commands.undoInputRule()) {
@@ -130,7 +100,7 @@ var handleBackspace = (editor, name, parentListTypes) => {
130
100
  if (editor.state.selection.from !== editor.state.selection.to) {
131
101
  return false;
132
102
  }
133
- if (!(0, import_core4.isNodeActive)(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) {
103
+ if (!(0, import_core3.isNodeActive)(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) {
134
104
  const { $anchor } = editor.state.selection;
135
105
  const $listPos = editor.state.doc.resolve($anchor.before() - 1);
136
106
  const listDescendants = [];
@@ -146,27 +116,17 @@ var handleBackspace = (editor, name, parentListTypes) => {
146
116
  const $lastItemPos = editor.state.doc.resolve($listPos.start() + lastItem.pos + 1);
147
117
  return editor.chain().cut({ from: $anchor.start() - 1, to: $anchor.end() + 1 }, $lastItemPos.end()).joinForward().run();
148
118
  }
149
- if (!(0, import_core4.isNodeActive)(editor.state, name)) {
150
- return false;
151
- }
152
- if (!(0, import_core4.isAtStartOfNode)(editor.state)) {
119
+ if (!(0, import_core3.isNodeActive)(editor.state, name)) {
153
120
  return false;
154
121
  }
155
- const listItemPos = findListItemPos(name, editor.state);
156
- if (!listItemPos) {
122
+ if (!(0, import_core3.isAtStartOfNode)(editor.state)) {
157
123
  return false;
158
124
  }
159
- const $prev = editor.state.doc.resolve(listItemPos.$pos.pos - 2);
160
- const prevNode = $prev.node(listItemPos.depth);
161
- const previousListItemHasSubList = listItemHasSubList(name, editor.state, prevNode);
162
- if (hasListItemBefore(name, editor.state) && !previousListItemHasSubList) {
163
- return editor.commands.joinItemBackward();
164
- }
165
125
  return editor.chain().liftListItem(name).run();
166
126
  };
167
127
 
168
128
  // src/keymap/listHelpers/handleDelete.ts
169
- var import_core5 = require("@tiptap/core");
129
+ var import_core4 = require("@tiptap/core");
170
130
 
171
131
  // src/keymap/listHelpers/nextListIsDeeper.ts
172
132
  var nextListIsDeeper = (typeOrName, state) => {
@@ -196,10 +156,10 @@ var nextListIsHigher = (typeOrName, state) => {
196
156
 
197
157
  // src/keymap/listHelpers/handleDelete.ts
198
158
  var handleDelete = (editor, name) => {
199
- if (!(0, import_core5.isNodeActive)(editor.state, name)) {
159
+ if (!(0, import_core4.isNodeActive)(editor.state, name)) {
200
160
  return false;
201
161
  }
202
- if (!(0, import_core5.isAtEndOfNode)(editor.state, name)) {
162
+ if (!(0, import_core4.isAtEndOfNode)(editor.state, name)) {
203
163
  return false;
204
164
  }
205
165
  const { selection } = editor.state;
@@ -230,6 +190,36 @@ var hasListItemAfter = (typeOrName, state) => {
230
190
  return true;
231
191
  };
232
192
 
193
+ // src/keymap/listHelpers/hasListItemBefore.ts
194
+ var hasListItemBefore = (typeOrName, state) => {
195
+ var _a;
196
+ const { $anchor } = state.selection;
197
+ const $targetPos = state.doc.resolve($anchor.pos - 2);
198
+ if ($targetPos.index() === 0) {
199
+ return false;
200
+ }
201
+ if (((_a = $targetPos.nodeBefore) == null ? void 0 : _a.type.name) !== typeOrName) {
202
+ return false;
203
+ }
204
+ return true;
205
+ };
206
+
207
+ // src/keymap/listHelpers/listItemHasSubList.ts
208
+ var import_core5 = require("@tiptap/core");
209
+ var listItemHasSubList = (typeOrName, state, node) => {
210
+ if (!node) {
211
+ return false;
212
+ }
213
+ const nodeType = (0, import_core5.getNodeType)(typeOrName, state.schema);
214
+ let hasSubList = false;
215
+ node.descendants((child) => {
216
+ if (child.type === nodeType) {
217
+ hasSubList = true;
218
+ }
219
+ });
220
+ return hasSubList;
221
+ };
222
+
233
223
  // src/keymap/list-keymap.ts
234
224
  var ListKeymap = import_core6.Extension.create({
235
225
  name: "listKeymap",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/keymap/index.ts","../../src/keymap/list-keymap.ts","../../src/keymap/listHelpers/index.ts","../../src/keymap/listHelpers/findListItemPos.ts","../../src/keymap/listHelpers/getNextListDepth.ts","../../src/keymap/listHelpers/handleBackspace.ts","../../src/keymap/listHelpers/hasListBefore.ts","../../src/keymap/listHelpers/hasListItemBefore.ts","../../src/keymap/listHelpers/listItemHasSubList.ts","../../src/keymap/listHelpers/handleDelete.ts","../../src/keymap/listHelpers/nextListIsDeeper.ts","../../src/keymap/listHelpers/nextListIsHigher.ts","../../src/keymap/listHelpers/hasListItemAfter.ts"],"sourcesContent":["export * from './list-keymap.js'\nexport * as listHelpers from './listHelpers/index.js'\n","import { Extension } from '@tiptap/core'\n\nimport { handleBackspace, handleDelete } from './listHelpers/index.js'\n\nexport type ListKeymapOptions = {\n /**\n * An array of list types. This is used for item and wrapper list matching.\n * @default []\n * @example [{ itemName: 'listItem', wrapperNames: ['bulletList', 'orderedList'] }]\n */\n listTypes: Array<{\n itemName: string\n wrapperNames: string[]\n }>\n}\n\n/**\n * This extension registers custom keymaps to change the behaviour of the backspace and delete keys.\n * By default Prosemirror keyhandling will always lift or sink items so paragraphs are joined into\n * the adjacent or previous list item. This extension will prevent this behaviour and instead will\n * try to join paragraphs from two list items into a single list item.\n * @see https://www.tiptap.dev/api/extensions/list-keymap\n */\nexport const ListKeymap = Extension.create<ListKeymapOptions>({\n name: 'listKeymap',\n\n addOptions() {\n return {\n listTypes: [\n {\n itemName: 'listItem',\n wrapperNames: ['bulletList', 'orderedList'],\n },\n {\n itemName: 'taskItem',\n wrapperNames: ['taskList'],\n },\n ],\n }\n },\n\n addKeyboardShortcuts() {\n return {\n Delete: ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleDelete(editor, itemName)) {\n handled = true\n }\n })\n\n return handled\n },\n 'Mod-Delete': ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleDelete(editor, itemName)) {\n handled = true\n }\n })\n\n return handled\n },\n Backspace: ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName, wrapperNames }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleBackspace(editor, itemName, wrapperNames)) {\n handled = true\n }\n })\n\n return handled\n },\n 'Mod-Backspace': ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName, wrapperNames }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleBackspace(editor, itemName, wrapperNames)) {\n handled = true\n }\n })\n\n return handled\n },\n }\n },\n})\n","export * from './findListItemPos.js'\nexport * from './getNextListDepth.js'\nexport * from './handleBackspace.js'\nexport * from './handleDelete.js'\nexport * from './hasListBefore.js'\nexport * from './hasListItemAfter.js'\nexport * from './hasListItemBefore.js'\nexport * from './listItemHasSubList.js'\nexport * from './nextListIsDeeper.js'\nexport * from './nextListIsHigher.js'\n","import { getNodeType } from '@tiptap/core'\nimport type { NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nexport const findListItemPos = (typeOrName: string | NodeType, state: EditorState) => {\n const { $from } = state.selection\n const nodeType = getNodeType(typeOrName, state.schema)\n\n let currentNode = null\n let currentDepth = $from.depth\n let currentPos = $from.pos\n let targetDepth: number | null = null\n\n while (currentDepth > 0 && targetDepth === null) {\n currentNode = $from.node(currentDepth)\n\n if (currentNode.type === nodeType) {\n targetDepth = currentDepth\n } else {\n currentDepth -= 1\n currentPos -= 1\n }\n }\n\n if (targetDepth === null) {\n return null\n }\n\n return { $pos: state.doc.resolve(currentPos), depth: targetDepth }\n}\n","import { getNodeAtPosition } from '@tiptap/core'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\n\nexport const getNextListDepth = (typeOrName: string, state: EditorState) => {\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos) {\n return false\n }\n\n const [, depth] = getNodeAtPosition(state, typeOrName, listItemPos.$pos.pos + 4)\n\n return depth\n}\n","import type { Editor } from '@tiptap/core'\nimport { isAtStartOfNode, isNodeActive } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\n\nimport { findListItemPos } from './findListItemPos.js'\nimport { hasListBefore } from './hasListBefore.js'\nimport { hasListItemBefore } from './hasListItemBefore.js'\nimport { listItemHasSubList } from './listItemHasSubList.js'\n\nexport const handleBackspace = (editor: Editor, name: string, parentListTypes: string[]) => {\n // this is required to still handle the undo handling\n if (editor.commands.undoInputRule()) {\n return true\n }\n\n // if the selection is not collapsed\n // we can rely on the default backspace behavior\n if (editor.state.selection.from !== editor.state.selection.to) {\n return false\n }\n\n // if the current item is NOT inside a list item &\n // the previous item is a list (orderedList or bulletList)\n // move the cursor into the list and delete the current item\n if (!isNodeActive(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) {\n const { $anchor } = editor.state.selection\n\n const $listPos = editor.state.doc.resolve($anchor.before() - 1)\n\n const listDescendants: Array<{ node: Node; pos: number }> = []\n\n $listPos.node().descendants((node, pos) => {\n if (node.type.name === name) {\n listDescendants.push({ node, pos })\n }\n })\n\n const lastItem = listDescendants.at(-1)\n\n if (!lastItem) {\n return false\n }\n\n const $lastItemPos = editor.state.doc.resolve($listPos.start() + lastItem.pos + 1)\n\n return editor\n .chain()\n .cut({ from: $anchor.start() - 1, to: $anchor.end() + 1 }, $lastItemPos.end())\n .joinForward()\n .run()\n }\n\n // if the cursor is not inside the current node type\n // do nothing and proceed\n if (!isNodeActive(editor.state, name)) {\n return false\n }\n\n // if the cursor is not at the start of a node\n // do nothing and proceed\n if (!isAtStartOfNode(editor.state)) {\n return false\n }\n\n const listItemPos = findListItemPos(name, editor.state)\n\n if (!listItemPos) {\n return false\n }\n\n const $prev = editor.state.doc.resolve(listItemPos.$pos.pos - 2)\n const prevNode = $prev.node(listItemPos.depth)\n\n const previousListItemHasSubList = listItemHasSubList(name, editor.state, prevNode)\n\n // if the previous item is a list item and doesn't have a sublist, join the list items\n if (hasListItemBefore(name, editor.state) && !previousListItemHasSubList) {\n return editor.commands.joinItemBackward()\n }\n\n // otherwise in the end, a backspace should\n // always just lift the list item if\n // joining / merging is not possible\n return editor.chain().liftListItem(name).run()\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListBefore = (\n editorState: EditorState,\n name: string,\n parentListTypes: string[],\n) => {\n const { $anchor } = editorState.selection\n\n const previousNodePos = Math.max(0, $anchor.pos - 2)\n\n const previousNode = editorState.doc.resolve(previousNodePos).node()\n\n if (!previousNode || !parentListTypes.includes(previousNode.type.name)) {\n return false\n }\n\n return true\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListItemBefore = (typeOrName: string, state: EditorState): boolean => {\n const { $anchor } = state.selection\n\n const $targetPos = state.doc.resolve($anchor.pos - 2)\n\n if ($targetPos.index() === 0) {\n return false\n }\n\n if ($targetPos.nodeBefore?.type.name !== typeOrName) {\n return false\n }\n\n return true\n}\n","import { getNodeType } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nexport const listItemHasSubList = (typeOrName: string, state: EditorState, node?: Node) => {\n if (!node) {\n return false\n }\n\n const nodeType = getNodeType(typeOrName, state.schema)\n\n let hasSubList = false\n\n node.descendants(child => {\n if (child.type === nodeType) {\n hasSubList = true\n }\n })\n\n return hasSubList\n}\n","import type { Editor } from '@tiptap/core'\nimport { isAtEndOfNode, isNodeActive } from '@tiptap/core'\n\nimport { nextListIsDeeper } from './nextListIsDeeper.js'\nimport { nextListIsHigher } from './nextListIsHigher.js'\n\nexport const handleDelete = (editor: Editor, name: string) => {\n // if the cursor is not inside the current node type\n // do nothing and proceed\n if (!isNodeActive(editor.state, name)) {\n return false\n }\n\n // if the cursor is not at the end of a node\n // do nothing and proceed\n if (!isAtEndOfNode(editor.state, name)) {\n return false\n }\n\n // if the selection is not collapsed, or not within a single node\n // do nothing and proceed\n const { selection } = editor.state\n const { $from, $to } = selection\n\n if (!selection.empty && $from.sameParent($to)) {\n return false\n }\n\n // check if the next node is a list with a deeper depth\n if (nextListIsDeeper(name, editor.state)) {\n return editor\n .chain()\n .focus(editor.state.selection.from + 4)\n .lift(name)\n .joinBackward()\n .run()\n }\n\n if (nextListIsHigher(name, editor.state)) {\n return editor.chain().joinForward().joinBackward().run()\n }\n\n return editor.commands.joinItemForward()\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\nimport { getNextListDepth } from './getNextListDepth.js'\n\nexport const nextListIsDeeper = (typeOrName: string, state: EditorState) => {\n const listDepth = getNextListDepth(typeOrName, state)\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos || !listDepth) {\n return false\n }\n\n if (listDepth > listItemPos.depth) {\n return true\n }\n\n return false\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\nimport { getNextListDepth } from './getNextListDepth.js'\n\nexport const nextListIsHigher = (typeOrName: string, state: EditorState) => {\n const listDepth = getNextListDepth(typeOrName, state)\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos || !listDepth) {\n return false\n }\n\n if (listDepth < listItemPos.depth) {\n return true\n }\n\n return false\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListItemAfter = (typeOrName: string, state: EditorState): boolean => {\n const { $anchor } = state.selection\n\n const $targetPos = state.doc.resolve($anchor.pos - $anchor.parentOffset - 2)\n\n if ($targetPos.index() === $targetPos.parent.childCount - 1) {\n return false\n }\n\n if ($targetPos.nodeAfter?.type.name !== typeOrName) {\n return false\n }\n\n return true\n}\n"],"mappings":"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","names":["import_core","import_core","import_core","import_core","import_core"]}
1
+ {"version":3,"sources":["../../src/keymap/index.ts","../../src/keymap/list-keymap.ts","../../src/keymap/listHelpers/index.ts","../../src/keymap/listHelpers/findListItemPos.ts","../../src/keymap/listHelpers/getNextListDepth.ts","../../src/keymap/listHelpers/handleBackspace.ts","../../src/keymap/listHelpers/hasListBefore.ts","../../src/keymap/listHelpers/handleDelete.ts","../../src/keymap/listHelpers/nextListIsDeeper.ts","../../src/keymap/listHelpers/nextListIsHigher.ts","../../src/keymap/listHelpers/hasListItemAfter.ts","../../src/keymap/listHelpers/hasListItemBefore.ts","../../src/keymap/listHelpers/listItemHasSubList.ts"],"sourcesContent":["export * from './list-keymap.js'\nexport * as listHelpers from './listHelpers/index.js'\n","import { Extension } from '@tiptap/core'\n\nimport { handleBackspace, handleDelete } from './listHelpers/index.js'\n\nexport type ListKeymapOptions = {\n /**\n * An array of list types. This is used for item and wrapper list matching.\n * @default []\n * @example [{ itemName: 'listItem', wrapperNames: ['bulletList', 'orderedList'] }]\n */\n listTypes: Array<{\n itemName: string\n wrapperNames: string[]\n }>\n}\n\n/**\n * This extension registers custom keymaps to change the behaviour of the backspace and delete keys.\n * By default Prosemirror keyhandling will always lift or sink items so paragraphs are joined into\n * the adjacent or previous list item. This extension will prevent this behaviour and instead will\n * try to join paragraphs from two list items into a single list item.\n * @see https://www.tiptap.dev/api/extensions/list-keymap\n */\nexport const ListKeymap = Extension.create<ListKeymapOptions>({\n name: 'listKeymap',\n\n addOptions() {\n return {\n listTypes: [\n {\n itemName: 'listItem',\n wrapperNames: ['bulletList', 'orderedList'],\n },\n {\n itemName: 'taskItem',\n wrapperNames: ['taskList'],\n },\n ],\n }\n },\n\n addKeyboardShortcuts() {\n return {\n Delete: ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleDelete(editor, itemName)) {\n handled = true\n }\n })\n\n return handled\n },\n 'Mod-Delete': ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleDelete(editor, itemName)) {\n handled = true\n }\n })\n\n return handled\n },\n Backspace: ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName, wrapperNames }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleBackspace(editor, itemName, wrapperNames)) {\n handled = true\n }\n })\n\n return handled\n },\n 'Mod-Backspace': ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName, wrapperNames }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleBackspace(editor, itemName, wrapperNames)) {\n handled = true\n }\n })\n\n return handled\n },\n }\n },\n})\n","export * from './findListItemPos.js'\nexport * from './getNextListDepth.js'\nexport * from './handleBackspace.js'\nexport * from './handleDelete.js'\nexport * from './hasListBefore.js'\nexport * from './hasListItemAfter.js'\nexport * from './hasListItemBefore.js'\nexport * from './listItemHasSubList.js'\nexport * from './nextListIsDeeper.js'\nexport * from './nextListIsHigher.js'\n","import { getNodeType } from '@tiptap/core'\nimport type { NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nexport const findListItemPos = (typeOrName: string | NodeType, state: EditorState) => {\n const { $from } = state.selection\n const nodeType = getNodeType(typeOrName, state.schema)\n\n let currentNode = null\n let currentDepth = $from.depth\n let currentPos = $from.pos\n let targetDepth: number | null = null\n\n while (currentDepth > 0 && targetDepth === null) {\n currentNode = $from.node(currentDepth)\n\n if (currentNode.type === nodeType) {\n targetDepth = currentDepth\n } else {\n currentDepth -= 1\n currentPos -= 1\n }\n }\n\n if (targetDepth === null) {\n return null\n }\n\n return { $pos: state.doc.resolve(currentPos), depth: targetDepth }\n}\n","import { getNodeAtPosition } from '@tiptap/core'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\n\nexport const getNextListDepth = (typeOrName: string, state: EditorState) => {\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos) {\n return false\n }\n\n const [, depth] = getNodeAtPosition(state, typeOrName, listItemPos.$pos.pos + 4)\n\n return depth\n}\n","import type { Editor } from '@tiptap/core'\nimport { isAtStartOfNode, isNodeActive } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\n\nimport { hasListBefore } from './hasListBefore.js'\n\nexport const handleBackspace = (editor: Editor, name: string, parentListTypes: string[]) => {\n // this is required to still handle the undo handling\n if (editor.commands.undoInputRule()) {\n return true\n }\n\n // if the selection is not collapsed\n // we can rely on the default backspace behavior\n if (editor.state.selection.from !== editor.state.selection.to) {\n return false\n }\n\n // if the current item is NOT inside a list item &\n // the previous item is a list (orderedList or bulletList)\n // move the cursor into the list and delete the current item\n if (!isNodeActive(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) {\n const { $anchor } = editor.state.selection\n\n const $listPos = editor.state.doc.resolve($anchor.before() - 1)\n\n const listDescendants: Array<{ node: Node; pos: number }> = []\n\n $listPos.node().descendants((node, pos) => {\n if (node.type.name === name) {\n listDescendants.push({ node, pos })\n }\n })\n\n const lastItem = listDescendants.at(-1)\n\n if (!lastItem) {\n return false\n }\n\n const $lastItemPos = editor.state.doc.resolve($listPos.start() + lastItem.pos + 1)\n\n return editor\n .chain()\n .cut({ from: $anchor.start() - 1, to: $anchor.end() + 1 }, $lastItemPos.end())\n .joinForward()\n .run()\n }\n\n // if the cursor is not inside the current node type\n // do nothing and proceed\n if (!isNodeActive(editor.state, name)) {\n return false\n }\n\n // if the cursor is not at the start of a node\n // do nothing and proceed\n if (!isAtStartOfNode(editor.state)) {\n return false\n }\n\n // At the start of a list item, lift it out. Top-level items split the\n // wrapping list around them; nested items get promoted into the outer\n // list. A second backspace then falls through to the merge branch above.\n return editor.chain().liftListItem(name).run()\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListBefore = (\n editorState: EditorState,\n name: string,\n parentListTypes: string[],\n) => {\n const { $anchor } = editorState.selection\n\n const previousNodePos = Math.max(0, $anchor.pos - 2)\n\n const previousNode = editorState.doc.resolve(previousNodePos).node()\n\n if (!previousNode || !parentListTypes.includes(previousNode.type.name)) {\n return false\n }\n\n return true\n}\n","import type { Editor } from '@tiptap/core'\nimport { isAtEndOfNode, isNodeActive } from '@tiptap/core'\n\nimport { nextListIsDeeper } from './nextListIsDeeper.js'\nimport { nextListIsHigher } from './nextListIsHigher.js'\n\nexport const handleDelete = (editor: Editor, name: string) => {\n // if the cursor is not inside the current node type\n // do nothing and proceed\n if (!isNodeActive(editor.state, name)) {\n return false\n }\n\n // if the cursor is not at the end of a node\n // do nothing and proceed\n if (!isAtEndOfNode(editor.state, name)) {\n return false\n }\n\n // if the selection is not collapsed, or not within a single node\n // do nothing and proceed\n const { selection } = editor.state\n const { $from, $to } = selection\n\n if (!selection.empty && $from.sameParent($to)) {\n return false\n }\n\n // check if the next node is a list with a deeper depth\n if (nextListIsDeeper(name, editor.state)) {\n return editor\n .chain()\n .focus(editor.state.selection.from + 4)\n .lift(name)\n .joinBackward()\n .run()\n }\n\n if (nextListIsHigher(name, editor.state)) {\n return editor.chain().joinForward().joinBackward().run()\n }\n\n return editor.commands.joinItemForward()\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\nimport { getNextListDepth } from './getNextListDepth.js'\n\nexport const nextListIsDeeper = (typeOrName: string, state: EditorState) => {\n const listDepth = getNextListDepth(typeOrName, state)\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos || !listDepth) {\n return false\n }\n\n if (listDepth > listItemPos.depth) {\n return true\n }\n\n return false\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\nimport { getNextListDepth } from './getNextListDepth.js'\n\nexport const nextListIsHigher = (typeOrName: string, state: EditorState) => {\n const listDepth = getNextListDepth(typeOrName, state)\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos || !listDepth) {\n return false\n }\n\n if (listDepth < listItemPos.depth) {\n return true\n }\n\n return false\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListItemAfter = (typeOrName: string, state: EditorState): boolean => {\n const { $anchor } = state.selection\n\n const $targetPos = state.doc.resolve($anchor.pos - $anchor.parentOffset - 2)\n\n if ($targetPos.index() === $targetPos.parent.childCount - 1) {\n return false\n }\n\n if ($targetPos.nodeAfter?.type.name !== typeOrName) {\n return false\n }\n\n return true\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListItemBefore = (typeOrName: string, state: EditorState): boolean => {\n const { $anchor } = state.selection\n\n const $targetPos = state.doc.resolve($anchor.pos - 2)\n\n if ($targetPos.index() === 0) {\n return false\n }\n\n if ($targetPos.nodeBefore?.type.name !== typeOrName) {\n return false\n }\n\n return true\n}\n","import { getNodeType } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nexport const listItemHasSubList = (typeOrName: string, state: EditorState, node?: Node) => {\n if (!node) {\n return false\n }\n\n const nodeType = getNodeType(typeOrName, state.schema)\n\n let hasSubList = false\n\n node.descendants(child => {\n if (child.type === nodeType) {\n hasSubList = true\n }\n })\n\n return hasSubList\n}\n"],"mappings":"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","names":["import_core","import_core","import_core","import_core","import_core"]}
@@ -71,36 +71,6 @@ var hasListBefore = (editorState, name, parentListTypes) => {
71
71
  return true;
72
72
  };
73
73
 
74
- // src/keymap/listHelpers/hasListItemBefore.ts
75
- var hasListItemBefore = (typeOrName, state) => {
76
- var _a;
77
- const { $anchor } = state.selection;
78
- const $targetPos = state.doc.resolve($anchor.pos - 2);
79
- if ($targetPos.index() === 0) {
80
- return false;
81
- }
82
- if (((_a = $targetPos.nodeBefore) == null ? void 0 : _a.type.name) !== typeOrName) {
83
- return false;
84
- }
85
- return true;
86
- };
87
-
88
- // src/keymap/listHelpers/listItemHasSubList.ts
89
- import { getNodeType as getNodeType2 } from "@tiptap/core";
90
- var listItemHasSubList = (typeOrName, state, node) => {
91
- if (!node) {
92
- return false;
93
- }
94
- const nodeType = getNodeType2(typeOrName, state.schema);
95
- let hasSubList = false;
96
- node.descendants((child) => {
97
- if (child.type === nodeType) {
98
- hasSubList = true;
99
- }
100
- });
101
- return hasSubList;
102
- };
103
-
104
74
  // src/keymap/listHelpers/handleBackspace.ts
105
75
  var handleBackspace = (editor, name, parentListTypes) => {
106
76
  if (editor.commands.undoInputRule()) {
@@ -131,16 +101,6 @@ var handleBackspace = (editor, name, parentListTypes) => {
131
101
  if (!isAtStartOfNode(editor.state)) {
132
102
  return false;
133
103
  }
134
- const listItemPos = findListItemPos(name, editor.state);
135
- if (!listItemPos) {
136
- return false;
137
- }
138
- const $prev = editor.state.doc.resolve(listItemPos.$pos.pos - 2);
139
- const prevNode = $prev.node(listItemPos.depth);
140
- const previousListItemHasSubList = listItemHasSubList(name, editor.state, prevNode);
141
- if (hasListItemBefore(name, editor.state) && !previousListItemHasSubList) {
142
- return editor.commands.joinItemBackward();
143
- }
144
104
  return editor.chain().liftListItem(name).run();
145
105
  };
146
106
 
@@ -209,6 +169,36 @@ var hasListItemAfter = (typeOrName, state) => {
209
169
  return true;
210
170
  };
211
171
 
172
+ // src/keymap/listHelpers/hasListItemBefore.ts
173
+ var hasListItemBefore = (typeOrName, state) => {
174
+ var _a;
175
+ const { $anchor } = state.selection;
176
+ const $targetPos = state.doc.resolve($anchor.pos - 2);
177
+ if ($targetPos.index() === 0) {
178
+ return false;
179
+ }
180
+ if (((_a = $targetPos.nodeBefore) == null ? void 0 : _a.type.name) !== typeOrName) {
181
+ return false;
182
+ }
183
+ return true;
184
+ };
185
+
186
+ // src/keymap/listHelpers/listItemHasSubList.ts
187
+ import { getNodeType as getNodeType2 } from "@tiptap/core";
188
+ var listItemHasSubList = (typeOrName, state, node) => {
189
+ if (!node) {
190
+ return false;
191
+ }
192
+ const nodeType = getNodeType2(typeOrName, state.schema);
193
+ let hasSubList = false;
194
+ node.descendants((child) => {
195
+ if (child.type === nodeType) {
196
+ hasSubList = true;
197
+ }
198
+ });
199
+ return hasSubList;
200
+ };
201
+
212
202
  // src/keymap/list-keymap.ts
213
203
  var ListKeymap = Extension.create({
214
204
  name: "listKeymap",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/keymap/list-keymap.ts","../../src/keymap/listHelpers/index.ts","../../src/keymap/listHelpers/findListItemPos.ts","../../src/keymap/listHelpers/getNextListDepth.ts","../../src/keymap/listHelpers/handleBackspace.ts","../../src/keymap/listHelpers/hasListBefore.ts","../../src/keymap/listHelpers/hasListItemBefore.ts","../../src/keymap/listHelpers/listItemHasSubList.ts","../../src/keymap/listHelpers/handleDelete.ts","../../src/keymap/listHelpers/nextListIsDeeper.ts","../../src/keymap/listHelpers/nextListIsHigher.ts","../../src/keymap/listHelpers/hasListItemAfter.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\n\nimport { handleBackspace, handleDelete } from './listHelpers/index.js'\n\nexport type ListKeymapOptions = {\n /**\n * An array of list types. This is used for item and wrapper list matching.\n * @default []\n * @example [{ itemName: 'listItem', wrapperNames: ['bulletList', 'orderedList'] }]\n */\n listTypes: Array<{\n itemName: string\n wrapperNames: string[]\n }>\n}\n\n/**\n * This extension registers custom keymaps to change the behaviour of the backspace and delete keys.\n * By default Prosemirror keyhandling will always lift or sink items so paragraphs are joined into\n * the adjacent or previous list item. This extension will prevent this behaviour and instead will\n * try to join paragraphs from two list items into a single list item.\n * @see https://www.tiptap.dev/api/extensions/list-keymap\n */\nexport const ListKeymap = Extension.create<ListKeymapOptions>({\n name: 'listKeymap',\n\n addOptions() {\n return {\n listTypes: [\n {\n itemName: 'listItem',\n wrapperNames: ['bulletList', 'orderedList'],\n },\n {\n itemName: 'taskItem',\n wrapperNames: ['taskList'],\n },\n ],\n }\n },\n\n addKeyboardShortcuts() {\n return {\n Delete: ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleDelete(editor, itemName)) {\n handled = true\n }\n })\n\n return handled\n },\n 'Mod-Delete': ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleDelete(editor, itemName)) {\n handled = true\n }\n })\n\n return handled\n },\n Backspace: ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName, wrapperNames }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleBackspace(editor, itemName, wrapperNames)) {\n handled = true\n }\n })\n\n return handled\n },\n 'Mod-Backspace': ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName, wrapperNames }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleBackspace(editor, itemName, wrapperNames)) {\n handled = true\n }\n })\n\n return handled\n },\n }\n },\n})\n","export * from './findListItemPos.js'\nexport * from './getNextListDepth.js'\nexport * from './handleBackspace.js'\nexport * from './handleDelete.js'\nexport * from './hasListBefore.js'\nexport * from './hasListItemAfter.js'\nexport * from './hasListItemBefore.js'\nexport * from './listItemHasSubList.js'\nexport * from './nextListIsDeeper.js'\nexport * from './nextListIsHigher.js'\n","import { getNodeType } from '@tiptap/core'\nimport type { NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nexport const findListItemPos = (typeOrName: string | NodeType, state: EditorState) => {\n const { $from } = state.selection\n const nodeType = getNodeType(typeOrName, state.schema)\n\n let currentNode = null\n let currentDepth = $from.depth\n let currentPos = $from.pos\n let targetDepth: number | null = null\n\n while (currentDepth > 0 && targetDepth === null) {\n currentNode = $from.node(currentDepth)\n\n if (currentNode.type === nodeType) {\n targetDepth = currentDepth\n } else {\n currentDepth -= 1\n currentPos -= 1\n }\n }\n\n if (targetDepth === null) {\n return null\n }\n\n return { $pos: state.doc.resolve(currentPos), depth: targetDepth }\n}\n","import { getNodeAtPosition } from '@tiptap/core'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\n\nexport const getNextListDepth = (typeOrName: string, state: EditorState) => {\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos) {\n return false\n }\n\n const [, depth] = getNodeAtPosition(state, typeOrName, listItemPos.$pos.pos + 4)\n\n return depth\n}\n","import type { Editor } from '@tiptap/core'\nimport { isAtStartOfNode, isNodeActive } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\n\nimport { findListItemPos } from './findListItemPos.js'\nimport { hasListBefore } from './hasListBefore.js'\nimport { hasListItemBefore } from './hasListItemBefore.js'\nimport { listItemHasSubList } from './listItemHasSubList.js'\n\nexport const handleBackspace = (editor: Editor, name: string, parentListTypes: string[]) => {\n // this is required to still handle the undo handling\n if (editor.commands.undoInputRule()) {\n return true\n }\n\n // if the selection is not collapsed\n // we can rely on the default backspace behavior\n if (editor.state.selection.from !== editor.state.selection.to) {\n return false\n }\n\n // if the current item is NOT inside a list item &\n // the previous item is a list (orderedList or bulletList)\n // move the cursor into the list and delete the current item\n if (!isNodeActive(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) {\n const { $anchor } = editor.state.selection\n\n const $listPos = editor.state.doc.resolve($anchor.before() - 1)\n\n const listDescendants: Array<{ node: Node; pos: number }> = []\n\n $listPos.node().descendants((node, pos) => {\n if (node.type.name === name) {\n listDescendants.push({ node, pos })\n }\n })\n\n const lastItem = listDescendants.at(-1)\n\n if (!lastItem) {\n return false\n }\n\n const $lastItemPos = editor.state.doc.resolve($listPos.start() + lastItem.pos + 1)\n\n return editor\n .chain()\n .cut({ from: $anchor.start() - 1, to: $anchor.end() + 1 }, $lastItemPos.end())\n .joinForward()\n .run()\n }\n\n // if the cursor is not inside the current node type\n // do nothing and proceed\n if (!isNodeActive(editor.state, name)) {\n return false\n }\n\n // if the cursor is not at the start of a node\n // do nothing and proceed\n if (!isAtStartOfNode(editor.state)) {\n return false\n }\n\n const listItemPos = findListItemPos(name, editor.state)\n\n if (!listItemPos) {\n return false\n }\n\n const $prev = editor.state.doc.resolve(listItemPos.$pos.pos - 2)\n const prevNode = $prev.node(listItemPos.depth)\n\n const previousListItemHasSubList = listItemHasSubList(name, editor.state, prevNode)\n\n // if the previous item is a list item and doesn't have a sublist, join the list items\n if (hasListItemBefore(name, editor.state) && !previousListItemHasSubList) {\n return editor.commands.joinItemBackward()\n }\n\n // otherwise in the end, a backspace should\n // always just lift the list item if\n // joining / merging is not possible\n return editor.chain().liftListItem(name).run()\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListBefore = (\n editorState: EditorState,\n name: string,\n parentListTypes: string[],\n) => {\n const { $anchor } = editorState.selection\n\n const previousNodePos = Math.max(0, $anchor.pos - 2)\n\n const previousNode = editorState.doc.resolve(previousNodePos).node()\n\n if (!previousNode || !parentListTypes.includes(previousNode.type.name)) {\n return false\n }\n\n return true\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListItemBefore = (typeOrName: string, state: EditorState): boolean => {\n const { $anchor } = state.selection\n\n const $targetPos = state.doc.resolve($anchor.pos - 2)\n\n if ($targetPos.index() === 0) {\n return false\n }\n\n if ($targetPos.nodeBefore?.type.name !== typeOrName) {\n return false\n }\n\n return true\n}\n","import { getNodeType } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nexport const listItemHasSubList = (typeOrName: string, state: EditorState, node?: Node) => {\n if (!node) {\n return false\n }\n\n const nodeType = getNodeType(typeOrName, state.schema)\n\n let hasSubList = false\n\n node.descendants(child => {\n if (child.type === nodeType) {\n hasSubList = true\n }\n })\n\n return hasSubList\n}\n","import type { Editor } from '@tiptap/core'\nimport { isAtEndOfNode, isNodeActive } from '@tiptap/core'\n\nimport { nextListIsDeeper } from './nextListIsDeeper.js'\nimport { nextListIsHigher } from './nextListIsHigher.js'\n\nexport const handleDelete = (editor: Editor, name: string) => {\n // if the cursor is not inside the current node type\n // do nothing and proceed\n if (!isNodeActive(editor.state, name)) {\n return false\n }\n\n // if the cursor is not at the end of a node\n // do nothing and proceed\n if (!isAtEndOfNode(editor.state, name)) {\n return false\n }\n\n // if the selection is not collapsed, or not within a single node\n // do nothing and proceed\n const { selection } = editor.state\n const { $from, $to } = selection\n\n if (!selection.empty && $from.sameParent($to)) {\n return false\n }\n\n // check if the next node is a list with a deeper depth\n if (nextListIsDeeper(name, editor.state)) {\n return editor\n .chain()\n .focus(editor.state.selection.from + 4)\n .lift(name)\n .joinBackward()\n .run()\n }\n\n if (nextListIsHigher(name, editor.state)) {\n return editor.chain().joinForward().joinBackward().run()\n }\n\n return editor.commands.joinItemForward()\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\nimport { getNextListDepth } from './getNextListDepth.js'\n\nexport const nextListIsDeeper = (typeOrName: string, state: EditorState) => {\n const listDepth = getNextListDepth(typeOrName, state)\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos || !listDepth) {\n return false\n }\n\n if (listDepth > listItemPos.depth) {\n return true\n }\n\n return false\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\nimport { getNextListDepth } from './getNextListDepth.js'\n\nexport const nextListIsHigher = (typeOrName: string, state: EditorState) => {\n const listDepth = getNextListDepth(typeOrName, state)\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos || !listDepth) {\n return false\n }\n\n if (listDepth < listItemPos.depth) {\n return true\n }\n\n return false\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListItemAfter = (typeOrName: string, state: EditorState): boolean => {\n const { $anchor } = state.selection\n\n const $targetPos = state.doc.resolve($anchor.pos - $anchor.parentOffset - 2)\n\n if ($targetPos.index() === $targetPos.parent.childCount - 1) {\n return false\n }\n\n if ($targetPos.nodeAfter?.type.name !== typeOrName) {\n return false\n }\n\n return true\n}\n"],"mappings":"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","names":["getNodeType","isNodeActive","isNodeActive"]}
1
+ {"version":3,"sources":["../../src/keymap/list-keymap.ts","../../src/keymap/listHelpers/index.ts","../../src/keymap/listHelpers/findListItemPos.ts","../../src/keymap/listHelpers/getNextListDepth.ts","../../src/keymap/listHelpers/handleBackspace.ts","../../src/keymap/listHelpers/hasListBefore.ts","../../src/keymap/listHelpers/handleDelete.ts","../../src/keymap/listHelpers/nextListIsDeeper.ts","../../src/keymap/listHelpers/nextListIsHigher.ts","../../src/keymap/listHelpers/hasListItemAfter.ts","../../src/keymap/listHelpers/hasListItemBefore.ts","../../src/keymap/listHelpers/listItemHasSubList.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\n\nimport { handleBackspace, handleDelete } from './listHelpers/index.js'\n\nexport type ListKeymapOptions = {\n /**\n * An array of list types. This is used for item and wrapper list matching.\n * @default []\n * @example [{ itemName: 'listItem', wrapperNames: ['bulletList', 'orderedList'] }]\n */\n listTypes: Array<{\n itemName: string\n wrapperNames: string[]\n }>\n}\n\n/**\n * This extension registers custom keymaps to change the behaviour of the backspace and delete keys.\n * By default Prosemirror keyhandling will always lift or sink items so paragraphs are joined into\n * the adjacent or previous list item. This extension will prevent this behaviour and instead will\n * try to join paragraphs from two list items into a single list item.\n * @see https://www.tiptap.dev/api/extensions/list-keymap\n */\nexport const ListKeymap = Extension.create<ListKeymapOptions>({\n name: 'listKeymap',\n\n addOptions() {\n return {\n listTypes: [\n {\n itemName: 'listItem',\n wrapperNames: ['bulletList', 'orderedList'],\n },\n {\n itemName: 'taskItem',\n wrapperNames: ['taskList'],\n },\n ],\n }\n },\n\n addKeyboardShortcuts() {\n return {\n Delete: ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleDelete(editor, itemName)) {\n handled = true\n }\n })\n\n return handled\n },\n 'Mod-Delete': ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleDelete(editor, itemName)) {\n handled = true\n }\n })\n\n return handled\n },\n Backspace: ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName, wrapperNames }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleBackspace(editor, itemName, wrapperNames)) {\n handled = true\n }\n })\n\n return handled\n },\n 'Mod-Backspace': ({ editor }) => {\n let handled = false\n\n this.options.listTypes.forEach(({ itemName, wrapperNames }) => {\n if (editor.state.schema.nodes[itemName] === undefined) {\n return\n }\n\n if (handleBackspace(editor, itemName, wrapperNames)) {\n handled = true\n }\n })\n\n return handled\n },\n }\n },\n})\n","export * from './findListItemPos.js'\nexport * from './getNextListDepth.js'\nexport * from './handleBackspace.js'\nexport * from './handleDelete.js'\nexport * from './hasListBefore.js'\nexport * from './hasListItemAfter.js'\nexport * from './hasListItemBefore.js'\nexport * from './listItemHasSubList.js'\nexport * from './nextListIsDeeper.js'\nexport * from './nextListIsHigher.js'\n","import { getNodeType } from '@tiptap/core'\nimport type { NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nexport const findListItemPos = (typeOrName: string | NodeType, state: EditorState) => {\n const { $from } = state.selection\n const nodeType = getNodeType(typeOrName, state.schema)\n\n let currentNode = null\n let currentDepth = $from.depth\n let currentPos = $from.pos\n let targetDepth: number | null = null\n\n while (currentDepth > 0 && targetDepth === null) {\n currentNode = $from.node(currentDepth)\n\n if (currentNode.type === nodeType) {\n targetDepth = currentDepth\n } else {\n currentDepth -= 1\n currentPos -= 1\n }\n }\n\n if (targetDepth === null) {\n return null\n }\n\n return { $pos: state.doc.resolve(currentPos), depth: targetDepth }\n}\n","import { getNodeAtPosition } from '@tiptap/core'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\n\nexport const getNextListDepth = (typeOrName: string, state: EditorState) => {\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos) {\n return false\n }\n\n const [, depth] = getNodeAtPosition(state, typeOrName, listItemPos.$pos.pos + 4)\n\n return depth\n}\n","import type { Editor } from '@tiptap/core'\nimport { isAtStartOfNode, isNodeActive } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\n\nimport { hasListBefore } from './hasListBefore.js'\n\nexport const handleBackspace = (editor: Editor, name: string, parentListTypes: string[]) => {\n // this is required to still handle the undo handling\n if (editor.commands.undoInputRule()) {\n return true\n }\n\n // if the selection is not collapsed\n // we can rely on the default backspace behavior\n if (editor.state.selection.from !== editor.state.selection.to) {\n return false\n }\n\n // if the current item is NOT inside a list item &\n // the previous item is a list (orderedList or bulletList)\n // move the cursor into the list and delete the current item\n if (!isNodeActive(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) {\n const { $anchor } = editor.state.selection\n\n const $listPos = editor.state.doc.resolve($anchor.before() - 1)\n\n const listDescendants: Array<{ node: Node; pos: number }> = []\n\n $listPos.node().descendants((node, pos) => {\n if (node.type.name === name) {\n listDescendants.push({ node, pos })\n }\n })\n\n const lastItem = listDescendants.at(-1)\n\n if (!lastItem) {\n return false\n }\n\n const $lastItemPos = editor.state.doc.resolve($listPos.start() + lastItem.pos + 1)\n\n return editor\n .chain()\n .cut({ from: $anchor.start() - 1, to: $anchor.end() + 1 }, $lastItemPos.end())\n .joinForward()\n .run()\n }\n\n // if the cursor is not inside the current node type\n // do nothing and proceed\n if (!isNodeActive(editor.state, name)) {\n return false\n }\n\n // if the cursor is not at the start of a node\n // do nothing and proceed\n if (!isAtStartOfNode(editor.state)) {\n return false\n }\n\n // At the start of a list item, lift it out. Top-level items split the\n // wrapping list around them; nested items get promoted into the outer\n // list. A second backspace then falls through to the merge branch above.\n return editor.chain().liftListItem(name).run()\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListBefore = (\n editorState: EditorState,\n name: string,\n parentListTypes: string[],\n) => {\n const { $anchor } = editorState.selection\n\n const previousNodePos = Math.max(0, $anchor.pos - 2)\n\n const previousNode = editorState.doc.resolve(previousNodePos).node()\n\n if (!previousNode || !parentListTypes.includes(previousNode.type.name)) {\n return false\n }\n\n return true\n}\n","import type { Editor } from '@tiptap/core'\nimport { isAtEndOfNode, isNodeActive } from '@tiptap/core'\n\nimport { nextListIsDeeper } from './nextListIsDeeper.js'\nimport { nextListIsHigher } from './nextListIsHigher.js'\n\nexport const handleDelete = (editor: Editor, name: string) => {\n // if the cursor is not inside the current node type\n // do nothing and proceed\n if (!isNodeActive(editor.state, name)) {\n return false\n }\n\n // if the cursor is not at the end of a node\n // do nothing and proceed\n if (!isAtEndOfNode(editor.state, name)) {\n return false\n }\n\n // if the selection is not collapsed, or not within a single node\n // do nothing and proceed\n const { selection } = editor.state\n const { $from, $to } = selection\n\n if (!selection.empty && $from.sameParent($to)) {\n return false\n }\n\n // check if the next node is a list with a deeper depth\n if (nextListIsDeeper(name, editor.state)) {\n return editor\n .chain()\n .focus(editor.state.selection.from + 4)\n .lift(name)\n .joinBackward()\n .run()\n }\n\n if (nextListIsHigher(name, editor.state)) {\n return editor.chain().joinForward().joinBackward().run()\n }\n\n return editor.commands.joinItemForward()\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\nimport { getNextListDepth } from './getNextListDepth.js'\n\nexport const nextListIsDeeper = (typeOrName: string, state: EditorState) => {\n const listDepth = getNextListDepth(typeOrName, state)\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos || !listDepth) {\n return false\n }\n\n if (listDepth > listItemPos.depth) {\n return true\n }\n\n return false\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { findListItemPos } from './findListItemPos.js'\nimport { getNextListDepth } from './getNextListDepth.js'\n\nexport const nextListIsHigher = (typeOrName: string, state: EditorState) => {\n const listDepth = getNextListDepth(typeOrName, state)\n const listItemPos = findListItemPos(typeOrName, state)\n\n if (!listItemPos || !listDepth) {\n return false\n }\n\n if (listDepth < listItemPos.depth) {\n return true\n }\n\n return false\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListItemAfter = (typeOrName: string, state: EditorState): boolean => {\n const { $anchor } = state.selection\n\n const $targetPos = state.doc.resolve($anchor.pos - $anchor.parentOffset - 2)\n\n if ($targetPos.index() === $targetPos.parent.childCount - 1) {\n return false\n }\n\n if ($targetPos.nodeAfter?.type.name !== typeOrName) {\n return false\n }\n\n return true\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const hasListItemBefore = (typeOrName: string, state: EditorState): boolean => {\n const { $anchor } = state.selection\n\n const $targetPos = state.doc.resolve($anchor.pos - 2)\n\n if ($targetPos.index() === 0) {\n return false\n }\n\n if ($targetPos.nodeBefore?.type.name !== typeOrName) {\n return false\n }\n\n return true\n}\n","import { getNodeType } from '@tiptap/core'\nimport type { Node } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nexport const listItemHasSubList = (typeOrName: string, state: EditorState, node?: Node) => {\n if (!node) {\n return false\n }\n\n const nodeType = getNodeType(typeOrName, state.schema)\n\n let hasSubList = false\n\n node.descendants(child => {\n if (child.type === nodeType) {\n hasSubList = true\n }\n })\n\n return hasSubList\n}\n"],"mappings":"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","names":["isNodeActive","isNodeActive","getNodeType"]}