@tiptap/extension-details 3.20.1 → 3.20.3

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/extension-details",
3
3
  "description": "details extension for tiptap",
4
- "version": "3.20.1",
4
+ "version": "3.20.3",
5
5
  "homepage": "https://tiptap.dev/api/nodes/details",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -31,14 +31,14 @@
31
31
  "dist"
32
32
  ],
33
33
  "devDependencies": {
34
- "@tiptap/core": "^3.20.1",
35
- "@tiptap/extension-text-style": "^3.20.1",
36
- "@tiptap/pm": "^3.20.1"
34
+ "@tiptap/core": "^3.20.3",
35
+ "@tiptap/extension-text-style": "^3.20.3",
36
+ "@tiptap/pm": "^3.20.3"
37
37
  },
38
38
  "peerDependencies": {
39
- "@tiptap/core": "^3.20.1",
40
- "@tiptap/extension-text-style": "^3.20.1",
41
- "@tiptap/pm": "^3.20.1"
39
+ "@tiptap/core": "^3.20.3",
40
+ "@tiptap/extension-text-style": "^3.20.3",
41
+ "@tiptap/pm": "^3.20.3"
42
42
  },
43
43
  "repository": {
44
44
  "type": "git",
package/dist/index.cjs DELETED
@@ -1,544 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- Details: () => Details,
24
- DetailsContent: () => DetailsContent,
25
- DetailsSummary: () => DetailsSummary,
26
- default: () => index_default
27
- });
28
- module.exports = __toCommonJS(index_exports);
29
-
30
- // src/details.ts
31
- var import_core2 = require("@tiptap/core");
32
- var import_state = require("@tiptap/pm/state");
33
-
34
- // src/helpers/isNodeVisible.ts
35
- var isNodeVisible = (position, editor) => {
36
- const node = editor.view.domAtPos(position).node;
37
- const isOpen = node.offsetParent !== null;
38
- return isOpen;
39
- };
40
-
41
- // src/helpers/findClosestVisibleNode.ts
42
- var findClosestVisibleNode = ($pos, predicate, editor) => {
43
- for (let i = $pos.depth; i > 0; i -= 1) {
44
- const node = $pos.node(i);
45
- const match = predicate(node);
46
- const isVisible = isNodeVisible($pos.start(i), editor);
47
- if (match && isVisible) {
48
- return {
49
- pos: i > 0 ? $pos.before(i) : 0,
50
- start: $pos.start(i),
51
- depth: i,
52
- node
53
- };
54
- }
55
- }
56
- };
57
-
58
- // src/helpers/setGapCursor.ts
59
- var import_core = require("@tiptap/core");
60
- var import_gapcursor = require("@tiptap/pm/gapcursor");
61
- var setGapCursor = (editor, direction) => {
62
- const { state, view, extensionManager } = editor;
63
- const { schema, selection } = state;
64
- const { empty, $anchor } = selection;
65
- const hasGapCursorExtension = !!extensionManager.extensions.find((extension) => extension.name === "gapCursor");
66
- if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary || !hasGapCursorExtension) {
67
- return false;
68
- }
69
- if (direction === "right" && $anchor.parentOffset !== $anchor.parent.nodeSize - 2) {
70
- return false;
71
- }
72
- const details = (0, import_core.findParentNode)((node) => node.type === schema.nodes.details)(selection);
73
- if (!details) {
74
- return false;
75
- }
76
- const detailsContent = (0, import_core.findChildren)(details.node, (node) => node.type === schema.nodes.detailsContent);
77
- if (!detailsContent.length) {
78
- return false;
79
- }
80
- const isOpen = isNodeVisible(details.start + detailsContent[0].pos + 1, editor);
81
- if (isOpen) {
82
- return false;
83
- }
84
- const $position = state.doc.resolve(details.pos + details.node.nodeSize);
85
- const $validPosition = import_gapcursor.GapCursor.findFrom($position, 1, false);
86
- if (!$validPosition) {
87
- return false;
88
- }
89
- const { tr } = state;
90
- const gapCursorSelection = new import_gapcursor.GapCursor($validPosition);
91
- tr.setSelection(gapCursorSelection);
92
- tr.scrollIntoView();
93
- view.dispatch(tr);
94
- return true;
95
- };
96
-
97
- // src/details.ts
98
- var Details = import_core2.Node.create({
99
- name: "details",
100
- content: "detailsSummary detailsContent",
101
- group: "block",
102
- defining: true,
103
- isolating: true,
104
- // @ts-ignore reason: `allowGapCursor` is not a valid property by default, but the `GapCursor` extension adds it to the Nodeconfig type
105
- allowGapCursor: false,
106
- addOptions() {
107
- return {
108
- persist: false,
109
- openClassName: "is-open",
110
- HTMLAttributes: {}
111
- };
112
- },
113
- addAttributes() {
114
- if (!this.options.persist) {
115
- return [];
116
- }
117
- return {
118
- open: {
119
- default: false,
120
- parseHTML: (element) => element.hasAttribute("open"),
121
- renderHTML: ({ open }) => {
122
- if (!open) {
123
- return {};
124
- }
125
- return { open: "" };
126
- }
127
- }
128
- };
129
- },
130
- parseHTML() {
131
- return [
132
- {
133
- tag: "details"
134
- }
135
- ];
136
- },
137
- renderHTML({ HTMLAttributes }) {
138
- return ["details", (0, import_core2.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes), 0];
139
- },
140
- ...(0, import_core2.createBlockMarkdownSpec)({
141
- nodeName: "details",
142
- content: "block"
143
- }),
144
- addNodeView() {
145
- return ({ editor, getPos, node, HTMLAttributes }) => {
146
- const dom = document.createElement("div");
147
- const attributes = (0, import_core2.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, {
148
- "data-type": this.name
149
- });
150
- Object.entries(attributes).forEach(([key, value]) => dom.setAttribute(key, value));
151
- const toggle = document.createElement("button");
152
- toggle.type = "button";
153
- dom.append(toggle);
154
- const content = document.createElement("div");
155
- dom.append(content);
156
- const toggleDetailsContent = (setToValue) => {
157
- if (setToValue !== void 0) {
158
- if (setToValue) {
159
- if (dom.classList.contains(this.options.openClassName)) {
160
- return;
161
- }
162
- dom.classList.add(this.options.openClassName);
163
- } else {
164
- if (!dom.classList.contains(this.options.openClassName)) {
165
- return;
166
- }
167
- dom.classList.remove(this.options.openClassName);
168
- }
169
- } else {
170
- dom.classList.toggle(this.options.openClassName);
171
- }
172
- const event = new Event("toggleDetailsContent");
173
- const detailsContent = content.querySelector(':scope > div[data-type="detailsContent"]');
174
- detailsContent == null ? void 0 : detailsContent.dispatchEvent(event);
175
- };
176
- if (node.attrs.open) {
177
- setTimeout(() => toggleDetailsContent());
178
- }
179
- toggle.addEventListener("click", () => {
180
- toggleDetailsContent();
181
- if (!this.options.persist) {
182
- editor.commands.focus(void 0, { scrollIntoView: false });
183
- return;
184
- }
185
- if (editor.isEditable && typeof getPos === "function") {
186
- const { from, to } = editor.state.selection;
187
- editor.chain().command(({ tr }) => {
188
- const pos = getPos();
189
- if (!pos) {
190
- return false;
191
- }
192
- const currentNode = tr.doc.nodeAt(pos);
193
- if ((currentNode == null ? void 0 : currentNode.type) !== this.type) {
194
- return false;
195
- }
196
- tr.setNodeMarkup(pos, void 0, {
197
- open: !currentNode.attrs.open
198
- });
199
- return true;
200
- }).setTextSelection({
201
- from,
202
- to
203
- }).focus(void 0, { scrollIntoView: false }).run();
204
- }
205
- });
206
- return {
207
- dom,
208
- contentDOM: content,
209
- ignoreMutation(mutation) {
210
- if (mutation.type === "selection") {
211
- return false;
212
- }
213
- return !dom.contains(mutation.target) || dom === mutation.target;
214
- },
215
- update: (updatedNode) => {
216
- if (updatedNode.type !== this.type) {
217
- return false;
218
- }
219
- if (updatedNode.attrs.open !== void 0) {
220
- toggleDetailsContent(updatedNode.attrs.open);
221
- }
222
- return true;
223
- }
224
- };
225
- };
226
- },
227
- addCommands() {
228
- return {
229
- setDetails: () => ({ state, chain }) => {
230
- var _a;
231
- const { schema, selection } = state;
232
- const { $from, $to } = selection;
233
- const range = $from.blockRange($to);
234
- if (!range) {
235
- return false;
236
- }
237
- const slice = state.doc.slice(range.start, range.end);
238
- const match = schema.nodes.detailsContent.contentMatch.matchFragment(slice.content);
239
- if (!match) {
240
- return false;
241
- }
242
- const content = ((_a = slice.toJSON()) == null ? void 0 : _a.content) || [];
243
- return chain().insertContentAt(
244
- { from: range.start, to: range.end },
245
- {
246
- type: this.name,
247
- content: [
248
- {
249
- type: "detailsSummary"
250
- },
251
- {
252
- type: "detailsContent",
253
- content
254
- }
255
- ]
256
- }
257
- ).setTextSelection(range.start + 2).run();
258
- },
259
- unsetDetails: () => ({ state, chain }) => {
260
- const { selection, schema } = state;
261
- const details = (0, import_core2.findParentNode)((node) => node.type === this.type)(selection);
262
- if (!details) {
263
- return false;
264
- }
265
- const detailsSummaries = (0, import_core2.findChildren)(details.node, (node) => node.type === schema.nodes.detailsSummary);
266
- const detailsContents = (0, import_core2.findChildren)(details.node, (node) => node.type === schema.nodes.detailsContent);
267
- if (!detailsSummaries.length || !detailsContents.length) {
268
- return false;
269
- }
270
- const detailsSummary = detailsSummaries[0];
271
- const detailsContent = detailsContents[0];
272
- const from = details.pos;
273
- const $from = state.doc.resolve(from);
274
- const to = from + details.node.nodeSize;
275
- const range = { from, to };
276
- const content = detailsContent.node.content.toJSON() || [];
277
- const defaultTypeForSummary = $from.parent.type.contentMatch.defaultType;
278
- const summaryContent = defaultTypeForSummary == null ? void 0 : defaultTypeForSummary.create(null, detailsSummary.node.content).toJSON();
279
- const mergedContent = [summaryContent, ...content];
280
- return chain().insertContentAt(range, mergedContent).setTextSelection(from + 1).run();
281
- }
282
- };
283
- },
284
- addKeyboardShortcuts() {
285
- return {
286
- Backspace: () => {
287
- const { schema, selection } = this.editor.state;
288
- const { empty, $anchor } = selection;
289
- if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary) {
290
- return false;
291
- }
292
- if ($anchor.parentOffset !== 0) {
293
- return this.editor.commands.command(({ tr }) => {
294
- const from = $anchor.pos - 1;
295
- const to = $anchor.pos;
296
- tr.delete(from, to);
297
- return true;
298
- });
299
- }
300
- return this.editor.commands.unsetDetails();
301
- },
302
- // Creates a new node below it if it is closed.
303
- // Otherwise inside `DetailsContent`.
304
- Enter: ({ editor }) => {
305
- const { state, view } = editor;
306
- const { schema, selection } = state;
307
- const { $head } = selection;
308
- if ($head.parent.type !== schema.nodes.detailsSummary) {
309
- return false;
310
- }
311
- const isVisible = isNodeVisible($head.after() + 1, editor);
312
- const above = isVisible ? state.doc.nodeAt($head.after()) : $head.node(-2);
313
- if (!above) {
314
- return false;
315
- }
316
- const after = isVisible ? 0 : $head.indexAfter(-1);
317
- const type = (0, import_core2.defaultBlockAt)(above.contentMatchAt(after));
318
- if (!type || !above.canReplaceWith(after, after, type)) {
319
- return false;
320
- }
321
- const node = type.createAndFill();
322
- if (!node) {
323
- return false;
324
- }
325
- const pos = isVisible ? $head.after() + 1 : $head.after(-1);
326
- const tr = state.tr.replaceWith(pos, pos, node);
327
- const $pos = tr.doc.resolve(pos);
328
- const newSelection = import_state.Selection.near($pos, 1);
329
- tr.setSelection(newSelection);
330
- tr.scrollIntoView();
331
- view.dispatch(tr);
332
- return true;
333
- },
334
- // The default gapcursor implementation can’t handle hidden content, so we need to fix this.
335
- ArrowRight: ({ editor }) => {
336
- return setGapCursor(editor, "right");
337
- },
338
- // The default gapcursor implementation can’t handle hidden content, so we need to fix this.
339
- ArrowDown: ({ editor }) => {
340
- return setGapCursor(editor, "down");
341
- }
342
- };
343
- },
344
- addProseMirrorPlugins() {
345
- return [
346
- // This plugin prevents text selections within the hidden content in `DetailsContent`.
347
- // The cursor is moved to the next visible position.
348
- new import_state.Plugin({
349
- key: new import_state.PluginKey("detailsSelection"),
350
- appendTransaction: (transactions, oldState, newState) => {
351
- const { editor, type } = this;
352
- const isComposing = editor.view.composing;
353
- if (isComposing) {
354
- return;
355
- }
356
- const selectionSet = transactions.some((transaction2) => transaction2.selectionSet);
357
- if (!selectionSet || !oldState.selection.empty || !newState.selection.empty) {
358
- return;
359
- }
360
- const detailsIsActive = (0, import_core2.isActive)(newState, type.name);
361
- if (!detailsIsActive) {
362
- return;
363
- }
364
- const { $from } = newState.selection;
365
- const isVisible = isNodeVisible($from.pos, editor);
366
- if (isVisible) {
367
- return;
368
- }
369
- const details = findClosestVisibleNode($from, (node) => node.type === type, editor);
370
- if (!details) {
371
- return;
372
- }
373
- const detailsSummaries = (0, import_core2.findChildren)(
374
- details.node,
375
- (node) => node.type === newState.schema.nodes.detailsSummary
376
- );
377
- if (!detailsSummaries.length) {
378
- return;
379
- }
380
- const detailsSummary = detailsSummaries[0];
381
- const selectionDirection = oldState.selection.from < newState.selection.from ? "forward" : "backward";
382
- const correctedPosition = selectionDirection === "forward" ? details.start + detailsSummary.pos : details.pos + detailsSummary.pos + detailsSummary.node.nodeSize;
383
- const selection = import_state.TextSelection.create(newState.doc, correctedPosition);
384
- const transaction = newState.tr.setSelection(selection);
385
- return transaction;
386
- }
387
- })
388
- ];
389
- }
390
- });
391
-
392
- // src/content/details-content.ts
393
- var import_core3 = require("@tiptap/core");
394
- var import_state2 = require("@tiptap/pm/state");
395
- var DetailsContent = import_core3.Node.create({
396
- name: "detailsContent",
397
- content: "block+",
398
- defining: true,
399
- selectable: false,
400
- addOptions() {
401
- return {
402
- HTMLAttributes: {}
403
- };
404
- },
405
- parseHTML() {
406
- return [
407
- {
408
- tag: `div[data-type="${this.name}"]`
409
- }
410
- ];
411
- },
412
- renderHTML({ HTMLAttributes }) {
413
- return ["div", (0, import_core3.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, { "data-type": this.name }), 0];
414
- },
415
- addNodeView() {
416
- return ({ HTMLAttributes }) => {
417
- const dom = document.createElement("div");
418
- const attributes = (0, import_core3.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, {
419
- "data-type": this.name,
420
- hidden: "hidden"
421
- });
422
- Object.entries(attributes).forEach(([key, value]) => dom.setAttribute(key, value));
423
- dom.addEventListener("toggleDetailsContent", () => {
424
- dom.toggleAttribute("hidden");
425
- });
426
- return {
427
- dom,
428
- contentDOM: dom,
429
- ignoreMutation(mutation) {
430
- if (mutation.type === "selection") {
431
- return false;
432
- }
433
- return !dom.contains(mutation.target) || dom === mutation.target;
434
- },
435
- update: (updatedNode) => {
436
- if (updatedNode.type !== this.type) {
437
- return false;
438
- }
439
- return true;
440
- }
441
- };
442
- };
443
- },
444
- addKeyboardShortcuts() {
445
- return {
446
- // Escape node on double enter
447
- Enter: ({ editor }) => {
448
- const { state, view } = editor;
449
- const { selection } = state;
450
- const { $from, empty } = selection;
451
- const detailsContent = (0, import_core3.findParentNode)((node2) => node2.type === this.type)(selection);
452
- if (!empty || !detailsContent || !detailsContent.node.childCount) {
453
- return false;
454
- }
455
- const fromIndex = $from.index(detailsContent.depth);
456
- const { childCount } = detailsContent.node;
457
- const isAtEnd = childCount === fromIndex + 1;
458
- if (!isAtEnd) {
459
- return false;
460
- }
461
- const defaultChildType = detailsContent.node.type.contentMatch.defaultType;
462
- const defaultChildNode = defaultChildType == null ? void 0 : defaultChildType.createAndFill();
463
- if (!defaultChildNode) {
464
- return false;
465
- }
466
- const $childPos = state.doc.resolve(detailsContent.pos + 1);
467
- const lastChildIndex = childCount - 1;
468
- const lastChildNode = detailsContent.node.child(lastChildIndex);
469
- const lastChildPos = $childPos.posAtIndex(lastChildIndex, detailsContent.depth);
470
- const lastChildNodeIsEmpty = lastChildNode.eq(defaultChildNode);
471
- if (!lastChildNodeIsEmpty) {
472
- return false;
473
- }
474
- const above = $from.node(-3);
475
- if (!above) {
476
- return false;
477
- }
478
- const after = $from.indexAfter(-3);
479
- const type = (0, import_core3.defaultBlockAt)(above.contentMatchAt(after));
480
- if (!type || !above.canReplaceWith(after, after, type)) {
481
- return false;
482
- }
483
- const node = type.createAndFill();
484
- if (!node) {
485
- return false;
486
- }
487
- const { tr } = state;
488
- const pos = $from.after(-2);
489
- tr.replaceWith(pos, pos, node);
490
- const $pos = tr.doc.resolve(pos);
491
- const newSelection = import_state2.Selection.near($pos, 1);
492
- tr.setSelection(newSelection);
493
- const deleteFrom = lastChildPos;
494
- const deleteTo = lastChildPos + lastChildNode.nodeSize;
495
- tr.delete(deleteFrom, deleteTo);
496
- tr.scrollIntoView();
497
- view.dispatch(tr);
498
- return true;
499
- }
500
- };
501
- },
502
- ...(0, import_core3.createBlockMarkdownSpec)({
503
- nodeName: "detailsContent"
504
- })
505
- });
506
-
507
- // src/summary/details-summary.ts
508
- var import_core4 = require("@tiptap/core");
509
- var DetailsSummary = import_core4.Node.create({
510
- name: "detailsSummary",
511
- content: "text*",
512
- defining: true,
513
- selectable: false,
514
- isolating: true,
515
- addOptions() {
516
- return {
517
- HTMLAttributes: {}
518
- };
519
- },
520
- parseHTML() {
521
- return [
522
- {
523
- tag: "summary"
524
- }
525
- ];
526
- },
527
- renderHTML({ HTMLAttributes }) {
528
- return ["summary", (0, import_core4.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes), 0];
529
- },
530
- ...(0, import_core4.createBlockMarkdownSpec)({
531
- nodeName: "detailsSummary",
532
- content: "inline"
533
- })
534
- });
535
-
536
- // src/index.ts
537
- var index_default = Details;
538
- // Annotate the CommonJS export names for ESM import in node:
539
- 0 && (module.exports = {
540
- Details,
541
- DetailsContent,
542
- DetailsSummary
543
- });
544
- //# sourceMappingURL=index.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts","../src/details.ts","../src/helpers/isNodeVisible.ts","../src/helpers/findClosestVisibleNode.ts","../src/helpers/setGapCursor.ts","../src/content/details-content.ts","../src/summary/details-summary.ts"],"sourcesContent":["import { Details } from './details.js'\n\nexport * from './content/index.js'\nexport * from './details.js'\nexport * from './summary/index.js'\n\nexport default Details\n","import {\n createBlockMarkdownSpec,\n defaultBlockAt,\n findChildren,\n findParentNode,\n isActive,\n mergeAttributes,\n Node,\n} from '@tiptap/core'\nimport { Plugin, PluginKey, Selection, TextSelection } from '@tiptap/pm/state'\nimport type { ViewMutationRecord } from '@tiptap/pm/view'\n\nimport { findClosestVisibleNode } from './helpers/findClosestVisibleNode.js'\nimport { isNodeVisible } from './helpers/isNodeVisible.js'\nimport { setGapCursor } from './helpers/setGapCursor.js'\n\nexport interface DetailsOptions {\n /**\n * Specify if the open status should be saved in the document. Defaults to `false`.\n */\n persist: boolean\n /**\n * Specifies a CSS class that is set when toggling the content. Defaults to `is-open`.\n */\n openClassName: string\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n */\n HTMLAttributes: {\n [key: string]: any\n }\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n details: {\n /**\n * Set a details node\n */\n setDetails: () => ReturnType\n /**\n * Unset a details node\n */\n unsetDetails: () => ReturnType\n }\n }\n}\n\nexport const Details = Node.create<DetailsOptions>({\n name: 'details',\n\n content: 'detailsSummary detailsContent',\n\n group: 'block',\n\n defining: true,\n\n isolating: true,\n\n // @ts-ignore reason: `allowGapCursor` is not a valid property by default, but the `GapCursor` extension adds it to the Nodeconfig type\n allowGapCursor: false,\n\n addOptions() {\n return {\n persist: false,\n openClassName: 'is-open',\n HTMLAttributes: {},\n }\n },\n\n addAttributes() {\n if (!this.options.persist) {\n return []\n }\n\n return {\n open: {\n default: false,\n parseHTML: element => element.hasAttribute('open'),\n renderHTML: ({ open }) => {\n if (!open) {\n return {}\n }\n\n return { open: '' }\n },\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'details',\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['details', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n ...createBlockMarkdownSpec({\n nodeName: 'details',\n content: 'block',\n }),\n\n addNodeView() {\n return ({ editor, getPos, node, HTMLAttributes }) => {\n const dom = document.createElement('div')\n const attributes = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n 'data-type': this.name,\n })\n\n Object.entries(attributes).forEach(([key, value]) => dom.setAttribute(key, value))\n\n const toggle = document.createElement('button')\n\n toggle.type = 'button'\n\n dom.append(toggle)\n\n const content = document.createElement('div')\n\n dom.append(content)\n\n const toggleDetailsContent = (setToValue?: boolean) => {\n if (setToValue !== undefined) {\n if (setToValue) {\n if (dom.classList.contains(this.options.openClassName)) {\n return\n }\n dom.classList.add(this.options.openClassName)\n } else {\n if (!dom.classList.contains(this.options.openClassName)) {\n return\n }\n dom.classList.remove(this.options.openClassName)\n }\n } else {\n dom.classList.toggle(this.options.openClassName)\n }\n\n const event = new Event('toggleDetailsContent')\n const detailsContent = content.querySelector(':scope > div[data-type=\"detailsContent\"]')\n\n detailsContent?.dispatchEvent(event)\n }\n\n if (node.attrs.open) {\n setTimeout(() => toggleDetailsContent())\n }\n\n toggle.addEventListener('click', () => {\n toggleDetailsContent()\n\n if (!this.options.persist) {\n editor.commands.focus(undefined, { scrollIntoView: false })\n\n return\n }\n\n if (editor.isEditable && typeof getPos === 'function') {\n const { from, to } = editor.state.selection\n\n editor\n .chain()\n .command(({ tr }) => {\n const pos = getPos()\n\n if (!pos) {\n return false\n }\n\n const currentNode = tr.doc.nodeAt(pos)\n\n if (currentNode?.type !== this.type) {\n return false\n }\n\n tr.setNodeMarkup(pos, undefined, {\n open: !currentNode.attrs.open,\n })\n\n return true\n })\n .setTextSelection({\n from,\n to,\n })\n .focus(undefined, { scrollIntoView: false })\n .run()\n }\n })\n\n return {\n dom,\n contentDOM: content,\n ignoreMutation(mutation: ViewMutationRecord) {\n if (mutation.type === 'selection') {\n return false\n }\n\n return !dom.contains(mutation.target) || dom === mutation.target\n },\n update: updatedNode => {\n if (updatedNode.type !== this.type) {\n return false\n }\n\n // Only update the open state if set\n if (updatedNode.attrs.open !== undefined) {\n toggleDetailsContent(updatedNode.attrs.open)\n }\n\n return true\n },\n }\n }\n },\n\n addCommands() {\n return {\n setDetails:\n () =>\n ({ state, chain }) => {\n const { schema, selection } = state\n const { $from, $to } = selection\n const range = $from.blockRange($to)\n\n if (!range) {\n return false\n }\n\n const slice = state.doc.slice(range.start, range.end)\n const match = schema.nodes.detailsContent.contentMatch.matchFragment(slice.content)\n\n if (!match) {\n return false\n }\n\n const content = slice.toJSON()?.content || []\n\n return chain()\n .insertContentAt(\n { from: range.start, to: range.end },\n {\n type: this.name,\n content: [\n {\n type: 'detailsSummary',\n },\n {\n type: 'detailsContent',\n content,\n },\n ],\n },\n )\n .setTextSelection(range.start + 2)\n .run()\n },\n\n unsetDetails:\n () =>\n ({ state, chain }) => {\n const { selection, schema } = state\n const details = findParentNode(node => node.type === this.type)(selection)\n\n if (!details) {\n return false\n }\n\n const detailsSummaries = findChildren(details.node, node => node.type === schema.nodes.detailsSummary)\n const detailsContents = findChildren(details.node, node => node.type === schema.nodes.detailsContent)\n\n if (!detailsSummaries.length || !detailsContents.length) {\n return false\n }\n\n const detailsSummary = detailsSummaries[0]\n const detailsContent = detailsContents[0]\n const from = details.pos\n const $from = state.doc.resolve(from)\n const to = from + details.node.nodeSize\n const range = { from, to }\n const content = (detailsContent.node.content.toJSON() as []) || []\n const defaultTypeForSummary = $from.parent.type.contentMatch.defaultType\n\n // TODO: this may break for some custom schemas\n const summaryContent = defaultTypeForSummary?.create(null, detailsSummary.node.content).toJSON()\n const mergedContent = [summaryContent, ...content]\n\n return chain()\n .insertContentAt(range, mergedContent)\n .setTextSelection(from + 1)\n .run()\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => {\n const { schema, selection } = this.editor.state\n const { empty, $anchor } = selection\n\n if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary) {\n return false\n }\n\n // for some reason safari removes the whole text content within a `<summary>`tag on backspace\n // so we have to remove the text manually\n // see: https://discuss.prosemirror.net/t/safari-backspace-bug-with-details-tag/4223\n if ($anchor.parentOffset !== 0) {\n return this.editor.commands.command(({ tr }) => {\n const from = $anchor.pos - 1\n const to = $anchor.pos\n\n tr.delete(from, to)\n\n return true\n })\n }\n\n return this.editor.commands.unsetDetails()\n },\n\n // Creates a new node below it if it is closed.\n // Otherwise inside `DetailsContent`.\n Enter: ({ editor }) => {\n const { state, view } = editor\n const { schema, selection } = state\n const { $head } = selection\n\n if ($head.parent.type !== schema.nodes.detailsSummary) {\n return false\n }\n\n const isVisible = isNodeVisible($head.after() + 1, editor)\n const above = isVisible ? state.doc.nodeAt($head.after()) : $head.node(-2)\n\n if (!above) {\n return false\n }\n\n const after = isVisible ? 0 : $head.indexAfter(-1)\n const type = defaultBlockAt(above.contentMatchAt(after))\n\n if (!type || !above.canReplaceWith(after, after, type)) {\n return false\n }\n\n const node = type.createAndFill()\n\n if (!node) {\n return false\n }\n\n const pos = isVisible ? $head.after() + 1 : $head.after(-1)\n const tr = state.tr.replaceWith(pos, pos, node)\n const $pos = tr.doc.resolve(pos)\n const newSelection = Selection.near($pos, 1)\n\n tr.setSelection(newSelection)\n tr.scrollIntoView()\n view.dispatch(tr)\n\n return true\n },\n\n // The default gapcursor implementation can’t handle hidden content, so we need to fix this.\n ArrowRight: ({ editor }) => {\n return setGapCursor(editor, 'right')\n },\n\n // The default gapcursor implementation can’t handle hidden content, so we need to fix this.\n ArrowDown: ({ editor }) => {\n return setGapCursor(editor, 'down')\n },\n }\n },\n\n addProseMirrorPlugins() {\n return [\n // This plugin prevents text selections within the hidden content in `DetailsContent`.\n // The cursor is moved to the next visible position.\n new Plugin({\n key: new PluginKey('detailsSelection'),\n appendTransaction: (transactions, oldState, newState) => {\n const { editor, type } = this\n const isComposing = editor.view.composing\n\n if (isComposing) {\n return\n }\n\n const selectionSet = transactions.some(transaction => transaction.selectionSet)\n\n if (!selectionSet || !oldState.selection.empty || !newState.selection.empty) {\n return\n }\n\n const detailsIsActive = isActive(newState, type.name)\n\n if (!detailsIsActive) {\n return\n }\n\n const { $from } = newState.selection\n const isVisible = isNodeVisible($from.pos, editor)\n\n if (isVisible) {\n return\n }\n\n const details = findClosestVisibleNode($from, node => node.type === type, editor)\n\n if (!details) {\n return\n }\n\n const detailsSummaries = findChildren(\n details.node,\n node => node.type === newState.schema.nodes.detailsSummary,\n )\n\n if (!detailsSummaries.length) {\n return\n }\n\n const detailsSummary = detailsSummaries[0]\n const selectionDirection = oldState.selection.from < newState.selection.from ? 'forward' : 'backward'\n const correctedPosition =\n selectionDirection === 'forward'\n ? details.start + detailsSummary.pos\n : details.pos + detailsSummary.pos + detailsSummary.node.nodeSize\n const selection = TextSelection.create(newState.doc, correctedPosition)\n const transaction = newState.tr.setSelection(selection)\n\n return transaction\n },\n }),\n ]\n },\n})\n","import type { Editor } from '@tiptap/core'\n\nexport const isNodeVisible = (position: number, editor: Editor): boolean => {\n const node = editor.view.domAtPos(position).node as HTMLElement\n const isOpen = node.offsetParent !== null\n\n return isOpen\n}\n","import type { Editor, Predicate } from '@tiptap/core'\nimport type { Node as ProseMirrorNode, ResolvedPos } from '@tiptap/pm/model'\n\nimport { isNodeVisible } from './isNodeVisible.js'\n\nexport const findClosestVisibleNode = (\n $pos: ResolvedPos,\n predicate: Predicate,\n editor: Editor,\n):\n | {\n pos: number\n start: number\n depth: number\n node: ProseMirrorNode\n }\n | undefined => {\n for (let i = $pos.depth; i > 0; i -= 1) {\n const node = $pos.node(i)\n const match = predicate(node)\n const isVisible = isNodeVisible($pos.start(i), editor)\n\n if (match && isVisible) {\n return {\n pos: i > 0 ? $pos.before(i) : 0,\n start: $pos.start(i),\n depth: i,\n node,\n }\n }\n }\n}\n","import type { Editor } from '@tiptap/core'\nimport { findChildren, findParentNode } from '@tiptap/core'\nimport { GapCursor } from '@tiptap/pm/gapcursor'\nimport type { ResolvedPos } from '@tiptap/pm/model'\nimport type { Selection } from '@tiptap/pm/state'\n\nimport { isNodeVisible } from './isNodeVisible.js'\n\nexport const setGapCursor = (editor: Editor, direction: 'down' | 'right') => {\n const { state, view, extensionManager } = editor\n const { schema, selection } = state\n const { empty, $anchor } = selection\n const hasGapCursorExtension = !!extensionManager.extensions.find(extension => extension.name === 'gapCursor')\n\n if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary || !hasGapCursorExtension) {\n return false\n }\n\n if (direction === 'right' && $anchor.parentOffset !== $anchor.parent.nodeSize - 2) {\n return false\n }\n\n const details = findParentNode(node => node.type === schema.nodes.details)(selection)\n\n if (!details) {\n return false\n }\n\n const detailsContent = findChildren(details.node, node => node.type === schema.nodes.detailsContent)\n\n if (!detailsContent.length) {\n return false\n }\n\n const isOpen = isNodeVisible(details.start + detailsContent[0].pos + 1, editor)\n\n if (isOpen) {\n return false\n }\n\n const $position = state.doc.resolve(details.pos + details.node.nodeSize)\n const $validPosition = GapCursor.findFrom($position, 1, false) as unknown as null | ResolvedPos\n\n if (!$validPosition) {\n return false\n }\n\n const { tr } = state\n const gapCursorSelection = new GapCursor($validPosition) as Selection\n\n tr.setSelection(gapCursorSelection)\n tr.scrollIntoView()\n view.dispatch(tr)\n\n return true\n}\n","import { createBlockMarkdownSpec, defaultBlockAt, findParentNode, mergeAttributes, Node } from '@tiptap/core'\nimport { Selection } from '@tiptap/pm/state'\nimport type { ViewMutationRecord } from '@tiptap/pm/view'\n\nexport interface DetailsContentOptions {\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n */\n HTMLAttributes: {\n [key: string]: any\n }\n}\n\nexport const DetailsContent = Node.create<DetailsContentOptions>({\n name: 'detailsContent',\n\n content: 'block+',\n\n defining: true,\n\n selectable: false,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n }\n },\n\n parseHTML() {\n return [\n {\n tag: `div[data-type=\"${this.name}\"]`,\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { 'data-type': this.name }), 0]\n },\n\n addNodeView() {\n return ({ HTMLAttributes }) => {\n const dom = document.createElement('div')\n const attributes = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n 'data-type': this.name,\n hidden: 'hidden',\n })\n\n Object.entries(attributes).forEach(([key, value]) => dom.setAttribute(key, value))\n\n dom.addEventListener('toggleDetailsContent', () => {\n dom.toggleAttribute('hidden')\n })\n\n return {\n dom,\n contentDOM: dom,\n ignoreMutation(mutation: ViewMutationRecord) {\n if (mutation.type === 'selection') {\n return false\n }\n\n return !dom.contains(mutation.target) || dom === mutation.target\n },\n update: updatedNode => {\n if (updatedNode.type !== this.type) {\n return false\n }\n\n return true\n },\n }\n }\n },\n\n addKeyboardShortcuts() {\n return {\n // Escape node on double enter\n Enter: ({ editor }) => {\n const { state, view } = editor\n const { selection } = state\n const { $from, empty } = selection\n const detailsContent = findParentNode(node => node.type === this.type)(selection)\n\n if (!empty || !detailsContent || !detailsContent.node.childCount) {\n return false\n }\n\n const fromIndex = $from.index(detailsContent.depth)\n const { childCount } = detailsContent.node\n const isAtEnd = childCount === fromIndex + 1\n\n if (!isAtEnd) {\n return false\n }\n\n const defaultChildType = detailsContent.node.type.contentMatch.defaultType\n const defaultChildNode = defaultChildType?.createAndFill()\n\n if (!defaultChildNode) {\n return false\n }\n\n const $childPos = state.doc.resolve(detailsContent.pos + 1)\n const lastChildIndex = childCount - 1\n const lastChildNode = detailsContent.node.child(lastChildIndex)\n const lastChildPos = $childPos.posAtIndex(lastChildIndex, detailsContent.depth)\n const lastChildNodeIsEmpty = lastChildNode.eq(defaultChildNode)\n\n if (!lastChildNodeIsEmpty) {\n return false\n }\n\n // get parent of details node\n const above = $from.node(-3)\n\n if (!above) {\n return false\n }\n\n // get default node type after details node\n const after = $from.indexAfter(-3)\n const type = defaultBlockAt(above.contentMatchAt(after))\n\n if (!type || !above.canReplaceWith(after, after, type)) {\n return false\n }\n\n const node = type.createAndFill()\n\n if (!node) {\n return false\n }\n\n const { tr } = state\n const pos = $from.after(-2)\n\n tr.replaceWith(pos, pos, node)\n\n const $pos = tr.doc.resolve(pos)\n const newSelection = Selection.near($pos, 1)\n\n tr.setSelection(newSelection)\n\n const deleteFrom = lastChildPos\n const deleteTo = lastChildPos + lastChildNode.nodeSize\n\n tr.delete(deleteFrom, deleteTo)\n tr.scrollIntoView()\n view.dispatch(tr)\n\n return true\n },\n }\n },\n\n ...createBlockMarkdownSpec({\n nodeName: 'detailsContent',\n }),\n})\n","import { createBlockMarkdownSpec, mergeAttributes, Node } from '@tiptap/core'\n\nexport interface DetailsSummaryOptions {\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n */\n HTMLAttributes: {\n [key: string]: any\n }\n}\n\nexport const DetailsSummary = Node.create<DetailsSummaryOptions>({\n name: 'detailsSummary',\n\n content: 'text*',\n\n defining: true,\n\n selectable: false,\n\n isolating: true,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'summary',\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['summary', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n ...createBlockMarkdownSpec({\n nodeName: 'detailsSummary',\n content: 'inline',\n }),\n})\n"],"mappings":"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","names":["import_core","transaction","import_core","import_state","node","import_core"]}
package/dist/index.d.cts DELETED
@@ -1,55 +0,0 @@
1
- import { Node } from '@tiptap/core';
2
-
3
- interface DetailsOptions {
4
- /**
5
- * Specify if the open status should be saved in the document. Defaults to `false`.
6
- */
7
- persist: boolean;
8
- /**
9
- * Specifies a CSS class that is set when toggling the content. Defaults to `is-open`.
10
- */
11
- openClassName: string;
12
- /**
13
- * Custom HTML attributes that should be added to the rendered HTML tag.
14
- */
15
- HTMLAttributes: {
16
- [key: string]: any;
17
- };
18
- }
19
- declare module '@tiptap/core' {
20
- interface Commands<ReturnType> {
21
- details: {
22
- /**
23
- * Set a details node
24
- */
25
- setDetails: () => ReturnType;
26
- /**
27
- * Unset a details node
28
- */
29
- unsetDetails: () => ReturnType;
30
- };
31
- }
32
- }
33
- declare const Details: Node<DetailsOptions, any>;
34
-
35
- interface DetailsContentOptions {
36
- /**
37
- * Custom HTML attributes that should be added to the rendered HTML tag.
38
- */
39
- HTMLAttributes: {
40
- [key: string]: any;
41
- };
42
- }
43
- declare const DetailsContent: Node<DetailsContentOptions, any>;
44
-
45
- interface DetailsSummaryOptions {
46
- /**
47
- * Custom HTML attributes that should be added to the rendered HTML tag.
48
- */
49
- HTMLAttributes: {
50
- [key: string]: any;
51
- };
52
- }
53
- declare const DetailsSummary: Node<DetailsSummaryOptions, any>;
54
-
55
- export { Details, DetailsContent, type DetailsContentOptions, type DetailsOptions, DetailsSummary, type DetailsSummaryOptions, Details as default };
package/dist/index.d.ts DELETED
@@ -1,55 +0,0 @@
1
- import { Node } from '@tiptap/core';
2
-
3
- interface DetailsOptions {
4
- /**
5
- * Specify if the open status should be saved in the document. Defaults to `false`.
6
- */
7
- persist: boolean;
8
- /**
9
- * Specifies a CSS class that is set when toggling the content. Defaults to `is-open`.
10
- */
11
- openClassName: string;
12
- /**
13
- * Custom HTML attributes that should be added to the rendered HTML tag.
14
- */
15
- HTMLAttributes: {
16
- [key: string]: any;
17
- };
18
- }
19
- declare module '@tiptap/core' {
20
- interface Commands<ReturnType> {
21
- details: {
22
- /**
23
- * Set a details node
24
- */
25
- setDetails: () => ReturnType;
26
- /**
27
- * Unset a details node
28
- */
29
- unsetDetails: () => ReturnType;
30
- };
31
- }
32
- }
33
- declare const Details: Node<DetailsOptions, any>;
34
-
35
- interface DetailsContentOptions {
36
- /**
37
- * Custom HTML attributes that should be added to the rendered HTML tag.
38
- */
39
- HTMLAttributes: {
40
- [key: string]: any;
41
- };
42
- }
43
- declare const DetailsContent: Node<DetailsContentOptions, any>;
44
-
45
- interface DetailsSummaryOptions {
46
- /**
47
- * Custom HTML attributes that should be added to the rendered HTML tag.
48
- */
49
- HTMLAttributes: {
50
- [key: string]: any;
51
- };
52
- }
53
- declare const DetailsSummary: Node<DetailsSummaryOptions, any>;
54
-
55
- export { Details, DetailsContent, type DetailsContentOptions, type DetailsOptions, DetailsSummary, type DetailsSummaryOptions, Details as default };
package/dist/index.js DELETED
@@ -1,523 +0,0 @@
1
- // src/details.ts
2
- import {
3
- createBlockMarkdownSpec,
4
- defaultBlockAt,
5
- findChildren as findChildren2,
6
- findParentNode as findParentNode2,
7
- isActive,
8
- mergeAttributes,
9
- Node
10
- } from "@tiptap/core";
11
- import { Plugin, PluginKey, Selection, TextSelection } from "@tiptap/pm/state";
12
-
13
- // src/helpers/isNodeVisible.ts
14
- var isNodeVisible = (position, editor) => {
15
- const node = editor.view.domAtPos(position).node;
16
- const isOpen = node.offsetParent !== null;
17
- return isOpen;
18
- };
19
-
20
- // src/helpers/findClosestVisibleNode.ts
21
- var findClosestVisibleNode = ($pos, predicate, editor) => {
22
- for (let i = $pos.depth; i > 0; i -= 1) {
23
- const node = $pos.node(i);
24
- const match = predicate(node);
25
- const isVisible = isNodeVisible($pos.start(i), editor);
26
- if (match && isVisible) {
27
- return {
28
- pos: i > 0 ? $pos.before(i) : 0,
29
- start: $pos.start(i),
30
- depth: i,
31
- node
32
- };
33
- }
34
- }
35
- };
36
-
37
- // src/helpers/setGapCursor.ts
38
- import { findChildren, findParentNode } from "@tiptap/core";
39
- import { GapCursor } from "@tiptap/pm/gapcursor";
40
- var setGapCursor = (editor, direction) => {
41
- const { state, view, extensionManager } = editor;
42
- const { schema, selection } = state;
43
- const { empty, $anchor } = selection;
44
- const hasGapCursorExtension = !!extensionManager.extensions.find((extension) => extension.name === "gapCursor");
45
- if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary || !hasGapCursorExtension) {
46
- return false;
47
- }
48
- if (direction === "right" && $anchor.parentOffset !== $anchor.parent.nodeSize - 2) {
49
- return false;
50
- }
51
- const details = findParentNode((node) => node.type === schema.nodes.details)(selection);
52
- if (!details) {
53
- return false;
54
- }
55
- const detailsContent = findChildren(details.node, (node) => node.type === schema.nodes.detailsContent);
56
- if (!detailsContent.length) {
57
- return false;
58
- }
59
- const isOpen = isNodeVisible(details.start + detailsContent[0].pos + 1, editor);
60
- if (isOpen) {
61
- return false;
62
- }
63
- const $position = state.doc.resolve(details.pos + details.node.nodeSize);
64
- const $validPosition = GapCursor.findFrom($position, 1, false);
65
- if (!$validPosition) {
66
- return false;
67
- }
68
- const { tr } = state;
69
- const gapCursorSelection = new GapCursor($validPosition);
70
- tr.setSelection(gapCursorSelection);
71
- tr.scrollIntoView();
72
- view.dispatch(tr);
73
- return true;
74
- };
75
-
76
- // src/details.ts
77
- var Details = Node.create({
78
- name: "details",
79
- content: "detailsSummary detailsContent",
80
- group: "block",
81
- defining: true,
82
- isolating: true,
83
- // @ts-ignore reason: `allowGapCursor` is not a valid property by default, but the `GapCursor` extension adds it to the Nodeconfig type
84
- allowGapCursor: false,
85
- addOptions() {
86
- return {
87
- persist: false,
88
- openClassName: "is-open",
89
- HTMLAttributes: {}
90
- };
91
- },
92
- addAttributes() {
93
- if (!this.options.persist) {
94
- return [];
95
- }
96
- return {
97
- open: {
98
- default: false,
99
- parseHTML: (element) => element.hasAttribute("open"),
100
- renderHTML: ({ open }) => {
101
- if (!open) {
102
- return {};
103
- }
104
- return { open: "" };
105
- }
106
- }
107
- };
108
- },
109
- parseHTML() {
110
- return [
111
- {
112
- tag: "details"
113
- }
114
- ];
115
- },
116
- renderHTML({ HTMLAttributes }) {
117
- return ["details", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
118
- },
119
- ...createBlockMarkdownSpec({
120
- nodeName: "details",
121
- content: "block"
122
- }),
123
- addNodeView() {
124
- return ({ editor, getPos, node, HTMLAttributes }) => {
125
- const dom = document.createElement("div");
126
- const attributes = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
127
- "data-type": this.name
128
- });
129
- Object.entries(attributes).forEach(([key, value]) => dom.setAttribute(key, value));
130
- const toggle = document.createElement("button");
131
- toggle.type = "button";
132
- dom.append(toggle);
133
- const content = document.createElement("div");
134
- dom.append(content);
135
- const toggleDetailsContent = (setToValue) => {
136
- if (setToValue !== void 0) {
137
- if (setToValue) {
138
- if (dom.classList.contains(this.options.openClassName)) {
139
- return;
140
- }
141
- dom.classList.add(this.options.openClassName);
142
- } else {
143
- if (!dom.classList.contains(this.options.openClassName)) {
144
- return;
145
- }
146
- dom.classList.remove(this.options.openClassName);
147
- }
148
- } else {
149
- dom.classList.toggle(this.options.openClassName);
150
- }
151
- const event = new Event("toggleDetailsContent");
152
- const detailsContent = content.querySelector(':scope > div[data-type="detailsContent"]');
153
- detailsContent == null ? void 0 : detailsContent.dispatchEvent(event);
154
- };
155
- if (node.attrs.open) {
156
- setTimeout(() => toggleDetailsContent());
157
- }
158
- toggle.addEventListener("click", () => {
159
- toggleDetailsContent();
160
- if (!this.options.persist) {
161
- editor.commands.focus(void 0, { scrollIntoView: false });
162
- return;
163
- }
164
- if (editor.isEditable && typeof getPos === "function") {
165
- const { from, to } = editor.state.selection;
166
- editor.chain().command(({ tr }) => {
167
- const pos = getPos();
168
- if (!pos) {
169
- return false;
170
- }
171
- const currentNode = tr.doc.nodeAt(pos);
172
- if ((currentNode == null ? void 0 : currentNode.type) !== this.type) {
173
- return false;
174
- }
175
- tr.setNodeMarkup(pos, void 0, {
176
- open: !currentNode.attrs.open
177
- });
178
- return true;
179
- }).setTextSelection({
180
- from,
181
- to
182
- }).focus(void 0, { scrollIntoView: false }).run();
183
- }
184
- });
185
- return {
186
- dom,
187
- contentDOM: content,
188
- ignoreMutation(mutation) {
189
- if (mutation.type === "selection") {
190
- return false;
191
- }
192
- return !dom.contains(mutation.target) || dom === mutation.target;
193
- },
194
- update: (updatedNode) => {
195
- if (updatedNode.type !== this.type) {
196
- return false;
197
- }
198
- if (updatedNode.attrs.open !== void 0) {
199
- toggleDetailsContent(updatedNode.attrs.open);
200
- }
201
- return true;
202
- }
203
- };
204
- };
205
- },
206
- addCommands() {
207
- return {
208
- setDetails: () => ({ state, chain }) => {
209
- var _a;
210
- const { schema, selection } = state;
211
- const { $from, $to } = selection;
212
- const range = $from.blockRange($to);
213
- if (!range) {
214
- return false;
215
- }
216
- const slice = state.doc.slice(range.start, range.end);
217
- const match = schema.nodes.detailsContent.contentMatch.matchFragment(slice.content);
218
- if (!match) {
219
- return false;
220
- }
221
- const content = ((_a = slice.toJSON()) == null ? void 0 : _a.content) || [];
222
- return chain().insertContentAt(
223
- { from: range.start, to: range.end },
224
- {
225
- type: this.name,
226
- content: [
227
- {
228
- type: "detailsSummary"
229
- },
230
- {
231
- type: "detailsContent",
232
- content
233
- }
234
- ]
235
- }
236
- ).setTextSelection(range.start + 2).run();
237
- },
238
- unsetDetails: () => ({ state, chain }) => {
239
- const { selection, schema } = state;
240
- const details = findParentNode2((node) => node.type === this.type)(selection);
241
- if (!details) {
242
- return false;
243
- }
244
- const detailsSummaries = findChildren2(details.node, (node) => node.type === schema.nodes.detailsSummary);
245
- const detailsContents = findChildren2(details.node, (node) => node.type === schema.nodes.detailsContent);
246
- if (!detailsSummaries.length || !detailsContents.length) {
247
- return false;
248
- }
249
- const detailsSummary = detailsSummaries[0];
250
- const detailsContent = detailsContents[0];
251
- const from = details.pos;
252
- const $from = state.doc.resolve(from);
253
- const to = from + details.node.nodeSize;
254
- const range = { from, to };
255
- const content = detailsContent.node.content.toJSON() || [];
256
- const defaultTypeForSummary = $from.parent.type.contentMatch.defaultType;
257
- const summaryContent = defaultTypeForSummary == null ? void 0 : defaultTypeForSummary.create(null, detailsSummary.node.content).toJSON();
258
- const mergedContent = [summaryContent, ...content];
259
- return chain().insertContentAt(range, mergedContent).setTextSelection(from + 1).run();
260
- }
261
- };
262
- },
263
- addKeyboardShortcuts() {
264
- return {
265
- Backspace: () => {
266
- const { schema, selection } = this.editor.state;
267
- const { empty, $anchor } = selection;
268
- if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary) {
269
- return false;
270
- }
271
- if ($anchor.parentOffset !== 0) {
272
- return this.editor.commands.command(({ tr }) => {
273
- const from = $anchor.pos - 1;
274
- const to = $anchor.pos;
275
- tr.delete(from, to);
276
- return true;
277
- });
278
- }
279
- return this.editor.commands.unsetDetails();
280
- },
281
- // Creates a new node below it if it is closed.
282
- // Otherwise inside `DetailsContent`.
283
- Enter: ({ editor }) => {
284
- const { state, view } = editor;
285
- const { schema, selection } = state;
286
- const { $head } = selection;
287
- if ($head.parent.type !== schema.nodes.detailsSummary) {
288
- return false;
289
- }
290
- const isVisible = isNodeVisible($head.after() + 1, editor);
291
- const above = isVisible ? state.doc.nodeAt($head.after()) : $head.node(-2);
292
- if (!above) {
293
- return false;
294
- }
295
- const after = isVisible ? 0 : $head.indexAfter(-1);
296
- const type = defaultBlockAt(above.contentMatchAt(after));
297
- if (!type || !above.canReplaceWith(after, after, type)) {
298
- return false;
299
- }
300
- const node = type.createAndFill();
301
- if (!node) {
302
- return false;
303
- }
304
- const pos = isVisible ? $head.after() + 1 : $head.after(-1);
305
- const tr = state.tr.replaceWith(pos, pos, node);
306
- const $pos = tr.doc.resolve(pos);
307
- const newSelection = Selection.near($pos, 1);
308
- tr.setSelection(newSelection);
309
- tr.scrollIntoView();
310
- view.dispatch(tr);
311
- return true;
312
- },
313
- // The default gapcursor implementation can’t handle hidden content, so we need to fix this.
314
- ArrowRight: ({ editor }) => {
315
- return setGapCursor(editor, "right");
316
- },
317
- // The default gapcursor implementation can’t handle hidden content, so we need to fix this.
318
- ArrowDown: ({ editor }) => {
319
- return setGapCursor(editor, "down");
320
- }
321
- };
322
- },
323
- addProseMirrorPlugins() {
324
- return [
325
- // This plugin prevents text selections within the hidden content in `DetailsContent`.
326
- // The cursor is moved to the next visible position.
327
- new Plugin({
328
- key: new PluginKey("detailsSelection"),
329
- appendTransaction: (transactions, oldState, newState) => {
330
- const { editor, type } = this;
331
- const isComposing = editor.view.composing;
332
- if (isComposing) {
333
- return;
334
- }
335
- const selectionSet = transactions.some((transaction2) => transaction2.selectionSet);
336
- if (!selectionSet || !oldState.selection.empty || !newState.selection.empty) {
337
- return;
338
- }
339
- const detailsIsActive = isActive(newState, type.name);
340
- if (!detailsIsActive) {
341
- return;
342
- }
343
- const { $from } = newState.selection;
344
- const isVisible = isNodeVisible($from.pos, editor);
345
- if (isVisible) {
346
- return;
347
- }
348
- const details = findClosestVisibleNode($from, (node) => node.type === type, editor);
349
- if (!details) {
350
- return;
351
- }
352
- const detailsSummaries = findChildren2(
353
- details.node,
354
- (node) => node.type === newState.schema.nodes.detailsSummary
355
- );
356
- if (!detailsSummaries.length) {
357
- return;
358
- }
359
- const detailsSummary = detailsSummaries[0];
360
- const selectionDirection = oldState.selection.from < newState.selection.from ? "forward" : "backward";
361
- const correctedPosition = selectionDirection === "forward" ? details.start + detailsSummary.pos : details.pos + detailsSummary.pos + detailsSummary.node.nodeSize;
362
- const selection = TextSelection.create(newState.doc, correctedPosition);
363
- const transaction = newState.tr.setSelection(selection);
364
- return transaction;
365
- }
366
- })
367
- ];
368
- }
369
- });
370
-
371
- // src/content/details-content.ts
372
- import { createBlockMarkdownSpec as createBlockMarkdownSpec2, defaultBlockAt as defaultBlockAt2, findParentNode as findParentNode3, mergeAttributes as mergeAttributes2, Node as Node2 } from "@tiptap/core";
373
- import { Selection as Selection2 } from "@tiptap/pm/state";
374
- var DetailsContent = Node2.create({
375
- name: "detailsContent",
376
- content: "block+",
377
- defining: true,
378
- selectable: false,
379
- addOptions() {
380
- return {
381
- HTMLAttributes: {}
382
- };
383
- },
384
- parseHTML() {
385
- return [
386
- {
387
- tag: `div[data-type="${this.name}"]`
388
- }
389
- ];
390
- },
391
- renderHTML({ HTMLAttributes }) {
392
- return ["div", mergeAttributes2(this.options.HTMLAttributes, HTMLAttributes, { "data-type": this.name }), 0];
393
- },
394
- addNodeView() {
395
- return ({ HTMLAttributes }) => {
396
- const dom = document.createElement("div");
397
- const attributes = mergeAttributes2(this.options.HTMLAttributes, HTMLAttributes, {
398
- "data-type": this.name,
399
- hidden: "hidden"
400
- });
401
- Object.entries(attributes).forEach(([key, value]) => dom.setAttribute(key, value));
402
- dom.addEventListener("toggleDetailsContent", () => {
403
- dom.toggleAttribute("hidden");
404
- });
405
- return {
406
- dom,
407
- contentDOM: dom,
408
- ignoreMutation(mutation) {
409
- if (mutation.type === "selection") {
410
- return false;
411
- }
412
- return !dom.contains(mutation.target) || dom === mutation.target;
413
- },
414
- update: (updatedNode) => {
415
- if (updatedNode.type !== this.type) {
416
- return false;
417
- }
418
- return true;
419
- }
420
- };
421
- };
422
- },
423
- addKeyboardShortcuts() {
424
- return {
425
- // Escape node on double enter
426
- Enter: ({ editor }) => {
427
- const { state, view } = editor;
428
- const { selection } = state;
429
- const { $from, empty } = selection;
430
- const detailsContent = findParentNode3((node2) => node2.type === this.type)(selection);
431
- if (!empty || !detailsContent || !detailsContent.node.childCount) {
432
- return false;
433
- }
434
- const fromIndex = $from.index(detailsContent.depth);
435
- const { childCount } = detailsContent.node;
436
- const isAtEnd = childCount === fromIndex + 1;
437
- if (!isAtEnd) {
438
- return false;
439
- }
440
- const defaultChildType = detailsContent.node.type.contentMatch.defaultType;
441
- const defaultChildNode = defaultChildType == null ? void 0 : defaultChildType.createAndFill();
442
- if (!defaultChildNode) {
443
- return false;
444
- }
445
- const $childPos = state.doc.resolve(detailsContent.pos + 1);
446
- const lastChildIndex = childCount - 1;
447
- const lastChildNode = detailsContent.node.child(lastChildIndex);
448
- const lastChildPos = $childPos.posAtIndex(lastChildIndex, detailsContent.depth);
449
- const lastChildNodeIsEmpty = lastChildNode.eq(defaultChildNode);
450
- if (!lastChildNodeIsEmpty) {
451
- return false;
452
- }
453
- const above = $from.node(-3);
454
- if (!above) {
455
- return false;
456
- }
457
- const after = $from.indexAfter(-3);
458
- const type = defaultBlockAt2(above.contentMatchAt(after));
459
- if (!type || !above.canReplaceWith(after, after, type)) {
460
- return false;
461
- }
462
- const node = type.createAndFill();
463
- if (!node) {
464
- return false;
465
- }
466
- const { tr } = state;
467
- const pos = $from.after(-2);
468
- tr.replaceWith(pos, pos, node);
469
- const $pos = tr.doc.resolve(pos);
470
- const newSelection = Selection2.near($pos, 1);
471
- tr.setSelection(newSelection);
472
- const deleteFrom = lastChildPos;
473
- const deleteTo = lastChildPos + lastChildNode.nodeSize;
474
- tr.delete(deleteFrom, deleteTo);
475
- tr.scrollIntoView();
476
- view.dispatch(tr);
477
- return true;
478
- }
479
- };
480
- },
481
- ...createBlockMarkdownSpec2({
482
- nodeName: "detailsContent"
483
- })
484
- });
485
-
486
- // src/summary/details-summary.ts
487
- import { createBlockMarkdownSpec as createBlockMarkdownSpec3, mergeAttributes as mergeAttributes3, Node as Node3 } from "@tiptap/core";
488
- var DetailsSummary = Node3.create({
489
- name: "detailsSummary",
490
- content: "text*",
491
- defining: true,
492
- selectable: false,
493
- isolating: true,
494
- addOptions() {
495
- return {
496
- HTMLAttributes: {}
497
- };
498
- },
499
- parseHTML() {
500
- return [
501
- {
502
- tag: "summary"
503
- }
504
- ];
505
- },
506
- renderHTML({ HTMLAttributes }) {
507
- return ["summary", mergeAttributes3(this.options.HTMLAttributes, HTMLAttributes), 0];
508
- },
509
- ...createBlockMarkdownSpec3({
510
- nodeName: "detailsSummary",
511
- content: "inline"
512
- })
513
- });
514
-
515
- // src/index.ts
516
- var index_default = Details;
517
- export {
518
- Details,
519
- DetailsContent,
520
- DetailsSummary,
521
- index_default as default
522
- };
523
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/details.ts","../src/helpers/isNodeVisible.ts","../src/helpers/findClosestVisibleNode.ts","../src/helpers/setGapCursor.ts","../src/content/details-content.ts","../src/summary/details-summary.ts","../src/index.ts"],"sourcesContent":["import {\n createBlockMarkdownSpec,\n defaultBlockAt,\n findChildren,\n findParentNode,\n isActive,\n mergeAttributes,\n Node,\n} from '@tiptap/core'\nimport { Plugin, PluginKey, Selection, TextSelection } from '@tiptap/pm/state'\nimport type { ViewMutationRecord } from '@tiptap/pm/view'\n\nimport { findClosestVisibleNode } from './helpers/findClosestVisibleNode.js'\nimport { isNodeVisible } from './helpers/isNodeVisible.js'\nimport { setGapCursor } from './helpers/setGapCursor.js'\n\nexport interface DetailsOptions {\n /**\n * Specify if the open status should be saved in the document. Defaults to `false`.\n */\n persist: boolean\n /**\n * Specifies a CSS class that is set when toggling the content. Defaults to `is-open`.\n */\n openClassName: string\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n */\n HTMLAttributes: {\n [key: string]: any\n }\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n details: {\n /**\n * Set a details node\n */\n setDetails: () => ReturnType\n /**\n * Unset a details node\n */\n unsetDetails: () => ReturnType\n }\n }\n}\n\nexport const Details = Node.create<DetailsOptions>({\n name: 'details',\n\n content: 'detailsSummary detailsContent',\n\n group: 'block',\n\n defining: true,\n\n isolating: true,\n\n // @ts-ignore reason: `allowGapCursor` is not a valid property by default, but the `GapCursor` extension adds it to the Nodeconfig type\n allowGapCursor: false,\n\n addOptions() {\n return {\n persist: false,\n openClassName: 'is-open',\n HTMLAttributes: {},\n }\n },\n\n addAttributes() {\n if (!this.options.persist) {\n return []\n }\n\n return {\n open: {\n default: false,\n parseHTML: element => element.hasAttribute('open'),\n renderHTML: ({ open }) => {\n if (!open) {\n return {}\n }\n\n return { open: '' }\n },\n },\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'details',\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['details', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n ...createBlockMarkdownSpec({\n nodeName: 'details',\n content: 'block',\n }),\n\n addNodeView() {\n return ({ editor, getPos, node, HTMLAttributes }) => {\n const dom = document.createElement('div')\n const attributes = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n 'data-type': this.name,\n })\n\n Object.entries(attributes).forEach(([key, value]) => dom.setAttribute(key, value))\n\n const toggle = document.createElement('button')\n\n toggle.type = 'button'\n\n dom.append(toggle)\n\n const content = document.createElement('div')\n\n dom.append(content)\n\n const toggleDetailsContent = (setToValue?: boolean) => {\n if (setToValue !== undefined) {\n if (setToValue) {\n if (dom.classList.contains(this.options.openClassName)) {\n return\n }\n dom.classList.add(this.options.openClassName)\n } else {\n if (!dom.classList.contains(this.options.openClassName)) {\n return\n }\n dom.classList.remove(this.options.openClassName)\n }\n } else {\n dom.classList.toggle(this.options.openClassName)\n }\n\n const event = new Event('toggleDetailsContent')\n const detailsContent = content.querySelector(':scope > div[data-type=\"detailsContent\"]')\n\n detailsContent?.dispatchEvent(event)\n }\n\n if (node.attrs.open) {\n setTimeout(() => toggleDetailsContent())\n }\n\n toggle.addEventListener('click', () => {\n toggleDetailsContent()\n\n if (!this.options.persist) {\n editor.commands.focus(undefined, { scrollIntoView: false })\n\n return\n }\n\n if (editor.isEditable && typeof getPos === 'function') {\n const { from, to } = editor.state.selection\n\n editor\n .chain()\n .command(({ tr }) => {\n const pos = getPos()\n\n if (!pos) {\n return false\n }\n\n const currentNode = tr.doc.nodeAt(pos)\n\n if (currentNode?.type !== this.type) {\n return false\n }\n\n tr.setNodeMarkup(pos, undefined, {\n open: !currentNode.attrs.open,\n })\n\n return true\n })\n .setTextSelection({\n from,\n to,\n })\n .focus(undefined, { scrollIntoView: false })\n .run()\n }\n })\n\n return {\n dom,\n contentDOM: content,\n ignoreMutation(mutation: ViewMutationRecord) {\n if (mutation.type === 'selection') {\n return false\n }\n\n return !dom.contains(mutation.target) || dom === mutation.target\n },\n update: updatedNode => {\n if (updatedNode.type !== this.type) {\n return false\n }\n\n // Only update the open state if set\n if (updatedNode.attrs.open !== undefined) {\n toggleDetailsContent(updatedNode.attrs.open)\n }\n\n return true\n },\n }\n }\n },\n\n addCommands() {\n return {\n setDetails:\n () =>\n ({ state, chain }) => {\n const { schema, selection } = state\n const { $from, $to } = selection\n const range = $from.blockRange($to)\n\n if (!range) {\n return false\n }\n\n const slice = state.doc.slice(range.start, range.end)\n const match = schema.nodes.detailsContent.contentMatch.matchFragment(slice.content)\n\n if (!match) {\n return false\n }\n\n const content = slice.toJSON()?.content || []\n\n return chain()\n .insertContentAt(\n { from: range.start, to: range.end },\n {\n type: this.name,\n content: [\n {\n type: 'detailsSummary',\n },\n {\n type: 'detailsContent',\n content,\n },\n ],\n },\n )\n .setTextSelection(range.start + 2)\n .run()\n },\n\n unsetDetails:\n () =>\n ({ state, chain }) => {\n const { selection, schema } = state\n const details = findParentNode(node => node.type === this.type)(selection)\n\n if (!details) {\n return false\n }\n\n const detailsSummaries = findChildren(details.node, node => node.type === schema.nodes.detailsSummary)\n const detailsContents = findChildren(details.node, node => node.type === schema.nodes.detailsContent)\n\n if (!detailsSummaries.length || !detailsContents.length) {\n return false\n }\n\n const detailsSummary = detailsSummaries[0]\n const detailsContent = detailsContents[0]\n const from = details.pos\n const $from = state.doc.resolve(from)\n const to = from + details.node.nodeSize\n const range = { from, to }\n const content = (detailsContent.node.content.toJSON() as []) || []\n const defaultTypeForSummary = $from.parent.type.contentMatch.defaultType\n\n // TODO: this may break for some custom schemas\n const summaryContent = defaultTypeForSummary?.create(null, detailsSummary.node.content).toJSON()\n const mergedContent = [summaryContent, ...content]\n\n return chain()\n .insertContentAt(range, mergedContent)\n .setTextSelection(from + 1)\n .run()\n },\n }\n },\n\n addKeyboardShortcuts() {\n return {\n Backspace: () => {\n const { schema, selection } = this.editor.state\n const { empty, $anchor } = selection\n\n if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary) {\n return false\n }\n\n // for some reason safari removes the whole text content within a `<summary>`tag on backspace\n // so we have to remove the text manually\n // see: https://discuss.prosemirror.net/t/safari-backspace-bug-with-details-tag/4223\n if ($anchor.parentOffset !== 0) {\n return this.editor.commands.command(({ tr }) => {\n const from = $anchor.pos - 1\n const to = $anchor.pos\n\n tr.delete(from, to)\n\n return true\n })\n }\n\n return this.editor.commands.unsetDetails()\n },\n\n // Creates a new node below it if it is closed.\n // Otherwise inside `DetailsContent`.\n Enter: ({ editor }) => {\n const { state, view } = editor\n const { schema, selection } = state\n const { $head } = selection\n\n if ($head.parent.type !== schema.nodes.detailsSummary) {\n return false\n }\n\n const isVisible = isNodeVisible($head.after() + 1, editor)\n const above = isVisible ? state.doc.nodeAt($head.after()) : $head.node(-2)\n\n if (!above) {\n return false\n }\n\n const after = isVisible ? 0 : $head.indexAfter(-1)\n const type = defaultBlockAt(above.contentMatchAt(after))\n\n if (!type || !above.canReplaceWith(after, after, type)) {\n return false\n }\n\n const node = type.createAndFill()\n\n if (!node) {\n return false\n }\n\n const pos = isVisible ? $head.after() + 1 : $head.after(-1)\n const tr = state.tr.replaceWith(pos, pos, node)\n const $pos = tr.doc.resolve(pos)\n const newSelection = Selection.near($pos, 1)\n\n tr.setSelection(newSelection)\n tr.scrollIntoView()\n view.dispatch(tr)\n\n return true\n },\n\n // The default gapcursor implementation can’t handle hidden content, so we need to fix this.\n ArrowRight: ({ editor }) => {\n return setGapCursor(editor, 'right')\n },\n\n // The default gapcursor implementation can’t handle hidden content, so we need to fix this.\n ArrowDown: ({ editor }) => {\n return setGapCursor(editor, 'down')\n },\n }\n },\n\n addProseMirrorPlugins() {\n return [\n // This plugin prevents text selections within the hidden content in `DetailsContent`.\n // The cursor is moved to the next visible position.\n new Plugin({\n key: new PluginKey('detailsSelection'),\n appendTransaction: (transactions, oldState, newState) => {\n const { editor, type } = this\n const isComposing = editor.view.composing\n\n if (isComposing) {\n return\n }\n\n const selectionSet = transactions.some(transaction => transaction.selectionSet)\n\n if (!selectionSet || !oldState.selection.empty || !newState.selection.empty) {\n return\n }\n\n const detailsIsActive = isActive(newState, type.name)\n\n if (!detailsIsActive) {\n return\n }\n\n const { $from } = newState.selection\n const isVisible = isNodeVisible($from.pos, editor)\n\n if (isVisible) {\n return\n }\n\n const details = findClosestVisibleNode($from, node => node.type === type, editor)\n\n if (!details) {\n return\n }\n\n const detailsSummaries = findChildren(\n details.node,\n node => node.type === newState.schema.nodes.detailsSummary,\n )\n\n if (!detailsSummaries.length) {\n return\n }\n\n const detailsSummary = detailsSummaries[0]\n const selectionDirection = oldState.selection.from < newState.selection.from ? 'forward' : 'backward'\n const correctedPosition =\n selectionDirection === 'forward'\n ? details.start + detailsSummary.pos\n : details.pos + detailsSummary.pos + detailsSummary.node.nodeSize\n const selection = TextSelection.create(newState.doc, correctedPosition)\n const transaction = newState.tr.setSelection(selection)\n\n return transaction\n },\n }),\n ]\n },\n})\n","import type { Editor } from '@tiptap/core'\n\nexport const isNodeVisible = (position: number, editor: Editor): boolean => {\n const node = editor.view.domAtPos(position).node as HTMLElement\n const isOpen = node.offsetParent !== null\n\n return isOpen\n}\n","import type { Editor, Predicate } from '@tiptap/core'\nimport type { Node as ProseMirrorNode, ResolvedPos } from '@tiptap/pm/model'\n\nimport { isNodeVisible } from './isNodeVisible.js'\n\nexport const findClosestVisibleNode = (\n $pos: ResolvedPos,\n predicate: Predicate,\n editor: Editor,\n):\n | {\n pos: number\n start: number\n depth: number\n node: ProseMirrorNode\n }\n | undefined => {\n for (let i = $pos.depth; i > 0; i -= 1) {\n const node = $pos.node(i)\n const match = predicate(node)\n const isVisible = isNodeVisible($pos.start(i), editor)\n\n if (match && isVisible) {\n return {\n pos: i > 0 ? $pos.before(i) : 0,\n start: $pos.start(i),\n depth: i,\n node,\n }\n }\n }\n}\n","import type { Editor } from '@tiptap/core'\nimport { findChildren, findParentNode } from '@tiptap/core'\nimport { GapCursor } from '@tiptap/pm/gapcursor'\nimport type { ResolvedPos } from '@tiptap/pm/model'\nimport type { Selection } from '@tiptap/pm/state'\n\nimport { isNodeVisible } from './isNodeVisible.js'\n\nexport const setGapCursor = (editor: Editor, direction: 'down' | 'right') => {\n const { state, view, extensionManager } = editor\n const { schema, selection } = state\n const { empty, $anchor } = selection\n const hasGapCursorExtension = !!extensionManager.extensions.find(extension => extension.name === 'gapCursor')\n\n if (!empty || $anchor.parent.type !== schema.nodes.detailsSummary || !hasGapCursorExtension) {\n return false\n }\n\n if (direction === 'right' && $anchor.parentOffset !== $anchor.parent.nodeSize - 2) {\n return false\n }\n\n const details = findParentNode(node => node.type === schema.nodes.details)(selection)\n\n if (!details) {\n return false\n }\n\n const detailsContent = findChildren(details.node, node => node.type === schema.nodes.detailsContent)\n\n if (!detailsContent.length) {\n return false\n }\n\n const isOpen = isNodeVisible(details.start + detailsContent[0].pos + 1, editor)\n\n if (isOpen) {\n return false\n }\n\n const $position = state.doc.resolve(details.pos + details.node.nodeSize)\n const $validPosition = GapCursor.findFrom($position, 1, false) as unknown as null | ResolvedPos\n\n if (!$validPosition) {\n return false\n }\n\n const { tr } = state\n const gapCursorSelection = new GapCursor($validPosition) as Selection\n\n tr.setSelection(gapCursorSelection)\n tr.scrollIntoView()\n view.dispatch(tr)\n\n return true\n}\n","import { createBlockMarkdownSpec, defaultBlockAt, findParentNode, mergeAttributes, Node } from '@tiptap/core'\nimport { Selection } from '@tiptap/pm/state'\nimport type { ViewMutationRecord } from '@tiptap/pm/view'\n\nexport interface DetailsContentOptions {\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n */\n HTMLAttributes: {\n [key: string]: any\n }\n}\n\nexport const DetailsContent = Node.create<DetailsContentOptions>({\n name: 'detailsContent',\n\n content: 'block+',\n\n defining: true,\n\n selectable: false,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n }\n },\n\n parseHTML() {\n return [\n {\n tag: `div[data-type=\"${this.name}\"]`,\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['div', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { 'data-type': this.name }), 0]\n },\n\n addNodeView() {\n return ({ HTMLAttributes }) => {\n const dom = document.createElement('div')\n const attributes = mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {\n 'data-type': this.name,\n hidden: 'hidden',\n })\n\n Object.entries(attributes).forEach(([key, value]) => dom.setAttribute(key, value))\n\n dom.addEventListener('toggleDetailsContent', () => {\n dom.toggleAttribute('hidden')\n })\n\n return {\n dom,\n contentDOM: dom,\n ignoreMutation(mutation: ViewMutationRecord) {\n if (mutation.type === 'selection') {\n return false\n }\n\n return !dom.contains(mutation.target) || dom === mutation.target\n },\n update: updatedNode => {\n if (updatedNode.type !== this.type) {\n return false\n }\n\n return true\n },\n }\n }\n },\n\n addKeyboardShortcuts() {\n return {\n // Escape node on double enter\n Enter: ({ editor }) => {\n const { state, view } = editor\n const { selection } = state\n const { $from, empty } = selection\n const detailsContent = findParentNode(node => node.type === this.type)(selection)\n\n if (!empty || !detailsContent || !detailsContent.node.childCount) {\n return false\n }\n\n const fromIndex = $from.index(detailsContent.depth)\n const { childCount } = detailsContent.node\n const isAtEnd = childCount === fromIndex + 1\n\n if (!isAtEnd) {\n return false\n }\n\n const defaultChildType = detailsContent.node.type.contentMatch.defaultType\n const defaultChildNode = defaultChildType?.createAndFill()\n\n if (!defaultChildNode) {\n return false\n }\n\n const $childPos = state.doc.resolve(detailsContent.pos + 1)\n const lastChildIndex = childCount - 1\n const lastChildNode = detailsContent.node.child(lastChildIndex)\n const lastChildPos = $childPos.posAtIndex(lastChildIndex, detailsContent.depth)\n const lastChildNodeIsEmpty = lastChildNode.eq(defaultChildNode)\n\n if (!lastChildNodeIsEmpty) {\n return false\n }\n\n // get parent of details node\n const above = $from.node(-3)\n\n if (!above) {\n return false\n }\n\n // get default node type after details node\n const after = $from.indexAfter(-3)\n const type = defaultBlockAt(above.contentMatchAt(after))\n\n if (!type || !above.canReplaceWith(after, after, type)) {\n return false\n }\n\n const node = type.createAndFill()\n\n if (!node) {\n return false\n }\n\n const { tr } = state\n const pos = $from.after(-2)\n\n tr.replaceWith(pos, pos, node)\n\n const $pos = tr.doc.resolve(pos)\n const newSelection = Selection.near($pos, 1)\n\n tr.setSelection(newSelection)\n\n const deleteFrom = lastChildPos\n const deleteTo = lastChildPos + lastChildNode.nodeSize\n\n tr.delete(deleteFrom, deleteTo)\n tr.scrollIntoView()\n view.dispatch(tr)\n\n return true\n },\n }\n },\n\n ...createBlockMarkdownSpec({\n nodeName: 'detailsContent',\n }),\n})\n","import { createBlockMarkdownSpec, mergeAttributes, Node } from '@tiptap/core'\n\nexport interface DetailsSummaryOptions {\n /**\n * Custom HTML attributes that should be added to the rendered HTML tag.\n */\n HTMLAttributes: {\n [key: string]: any\n }\n}\n\nexport const DetailsSummary = Node.create<DetailsSummaryOptions>({\n name: 'detailsSummary',\n\n content: 'text*',\n\n defining: true,\n\n selectable: false,\n\n isolating: true,\n\n addOptions() {\n return {\n HTMLAttributes: {},\n }\n },\n\n parseHTML() {\n return [\n {\n tag: 'summary',\n },\n ]\n },\n\n renderHTML({ HTMLAttributes }) {\n return ['summary', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]\n },\n\n ...createBlockMarkdownSpec({\n nodeName: 'detailsSummary',\n content: 'inline',\n }),\n})\n","import { Details } from './details.js'\n\nexport * from './content/index.js'\nexport * from './details.js'\nexport * from './summary/index.js'\n\nexport default Details\n"],"mappings":"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","names":["findChildren","findParentNode","findParentNode","findChildren","transaction","createBlockMarkdownSpec","defaultBlockAt","findParentNode","mergeAttributes","Node","Selection","node","createBlockMarkdownSpec","mergeAttributes","Node"]}