@domternal/extension-details 0.2.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,656 @@
1
+ 'use strict';
2
+
3
+ var core = require('@domternal/core');
4
+ var state = require('@domternal/pm/state');
5
+ var gapcursor = require('@domternal/pm/gapcursor');
6
+
7
+ // src/Details.ts
8
+
9
+ // src/helpers/isNodeVisible.ts
10
+ var isNodeVisible = (position, editor) => {
11
+ let node = editor.view.domAtPos(position).node;
12
+ while (node && !(node instanceof HTMLElement)) {
13
+ node = node.parentNode;
14
+ }
15
+ if (!node) return false;
16
+ return node.offsetParent !== null;
17
+ };
18
+
19
+ // src/helpers/findClosestVisibleNode.ts
20
+ var findClosestVisibleNode = ($pos, predicate, editor) => {
21
+ for (let i = $pos.depth; i > 0; i--) {
22
+ const node = $pos.node(i);
23
+ const match = predicate(node);
24
+ const visible = isNodeVisible($pos.start(i), editor);
25
+ if (match && visible) {
26
+ return {
27
+ pos: i > 0 ? $pos.before(i) : 0,
28
+ start: $pos.start(i),
29
+ depth: i,
30
+ node
31
+ };
32
+ }
33
+ }
34
+ return void 0;
35
+ };
36
+ var setGapCursor = (editor, direction) => {
37
+ const { state, view } = editor;
38
+ const { schema, selection } = state;
39
+ const { empty, $anchor } = selection;
40
+ const summaryType = schema.nodes["detailsSummary"];
41
+ const detailsType = schema.nodes["details"];
42
+ const contentType = schema.nodes["detailsContent"];
43
+ const hasGapCursor = editor.extensionManager?.extensions.some(
44
+ (ext) => ext.name === "gapcursor" || ext.name === "gapCursor"
45
+ );
46
+ if (!empty || $anchor.parent.type !== summaryType || !hasGapCursor) {
47
+ return false;
48
+ }
49
+ if (direction === "right" && $anchor.parentOffset !== $anchor.parent.nodeSize - 2) {
50
+ return false;
51
+ }
52
+ const details = core.findParentNode((node) => node.type === detailsType)(selection);
53
+ if (!details) return false;
54
+ const detailsContent = core.findChildren(details.node, (node) => node.type === contentType);
55
+ if (!detailsContent.length) return false;
56
+ const firstContent = detailsContent[0];
57
+ if (!firstContent) return false;
58
+ const isOpen = isNodeVisible(details.start + firstContent.pos + 1, editor);
59
+ if (isOpen) return false;
60
+ const $position = state.doc.resolve(details.pos + details.node.nodeSize);
61
+ const found = gapcursor.GapCursor.findFrom($position, 1, false);
62
+ if (!found) return false;
63
+ const { tr } = state;
64
+ const gapCursorSelection = new gapcursor.GapCursor(found.$from);
65
+ tr.setSelection(gapCursorSelection);
66
+ tr.scrollIntoView();
67
+ view.dispatch(tr);
68
+ return true;
69
+ };
70
+ var DetailsSummary = core.Node.create({
71
+ name: "detailsSummary",
72
+ content: "inline*",
73
+ defining: true,
74
+ selectable: false,
75
+ isolating: true,
76
+ addOptions() {
77
+ return {
78
+ HTMLAttributes: {}
79
+ };
80
+ },
81
+ parseHTML() {
82
+ return [{ tag: "summary" }];
83
+ },
84
+ renderHTML({ HTMLAttributes }) {
85
+ return ["summary", { ...this.options.HTMLAttributes, ...HTMLAttributes }, 0];
86
+ }
87
+ });
88
+ var DetailsContent = core.Node.create({
89
+ name: "detailsContent",
90
+ content: "block+",
91
+ defining: true,
92
+ selectable: false,
93
+ addOptions() {
94
+ return {
95
+ HTMLAttributes: {}
96
+ };
97
+ },
98
+ parseHTML() {
99
+ return [
100
+ { tag: "div[data-details-content]" },
101
+ { tag: 'div[data-type="detailsContent"]' }
102
+ ];
103
+ },
104
+ renderHTML({ HTMLAttributes }) {
105
+ return [
106
+ "div",
107
+ { ...this.options.HTMLAttributes, ...HTMLAttributes, "data-details-content": "" },
108
+ 0
109
+ ];
110
+ },
111
+ addNodeView() {
112
+ const options = this.options;
113
+ return () => {
114
+ const dom = document.createElement("div");
115
+ dom.setAttribute("data-details-content", "");
116
+ dom.setAttribute("hidden", "hidden");
117
+ for (const [key, value] of Object.entries(options.HTMLAttributes)) {
118
+ if (value !== null && value !== void 0) {
119
+ dom.setAttribute(key, typeof value === "string" ? value : JSON.stringify(value));
120
+ }
121
+ }
122
+ dom.addEventListener("toggleDetailsContent", () => {
123
+ dom.toggleAttribute("hidden");
124
+ });
125
+ return {
126
+ dom,
127
+ contentDOM: dom,
128
+ ignoreMutation(mutation) {
129
+ if (mutation.type === "selection") {
130
+ return false;
131
+ }
132
+ return !dom.contains(mutation.target) || dom === mutation.target;
133
+ },
134
+ update: (updatedNode) => {
135
+ if (updatedNode.type.name !== "detailsContent") {
136
+ return false;
137
+ }
138
+ return true;
139
+ }
140
+ };
141
+ };
142
+ },
143
+ addKeyboardShortcuts() {
144
+ return {
145
+ // Double-Enter escape: when pressing Enter on the last empty block
146
+ // inside details content, escape out and create a new block after details
147
+ Enter: () => {
148
+ const editor = this.editor;
149
+ if (!editor) return false;
150
+ const { state: state$1, view } = editor;
151
+ const { selection } = state$1;
152
+ const { $from, empty } = selection;
153
+ const detailsContent = core.findParentNode(
154
+ (node2) => node2.type === state$1.schema.nodes["detailsContent"]
155
+ )(selection);
156
+ if (!empty || !detailsContent?.node.childCount) {
157
+ return false;
158
+ }
159
+ const fromIndex = $from.index(detailsContent.depth);
160
+ const { childCount } = detailsContent.node;
161
+ const isAtEnd = childCount === fromIndex + 1;
162
+ if (!isAtEnd) {
163
+ return false;
164
+ }
165
+ const defaultChildType = detailsContent.node.type.contentMatch.defaultType;
166
+ const defaultChildNode = defaultChildType?.createAndFill();
167
+ if (!defaultChildNode) {
168
+ return false;
169
+ }
170
+ const $childPos = state$1.doc.resolve(detailsContent.pos + 1);
171
+ const lastChildIndex = childCount - 1;
172
+ const lastChildNode = detailsContent.node.child(lastChildIndex);
173
+ const lastChildPos = $childPos.posAtIndex(lastChildIndex, detailsContent.depth);
174
+ const lastChildNodeIsEmpty = lastChildNode.eq(defaultChildNode);
175
+ if (!lastChildNodeIsEmpty) {
176
+ return false;
177
+ }
178
+ const above = $from.node(-3);
179
+ const after = $from.indexAfter(-3);
180
+ const type = core.defaultBlockAt(above.contentMatchAt(after));
181
+ if (!type || !above.canReplaceWith(after, after, type)) {
182
+ return false;
183
+ }
184
+ const node = type.createAndFill();
185
+ if (!node) {
186
+ return false;
187
+ }
188
+ const { tr } = state$1;
189
+ const pos = $from.after(-2);
190
+ tr.replaceWith(pos, pos, node);
191
+ const $pos = tr.doc.resolve(pos);
192
+ const newSelection = state.Selection.near($pos, 1);
193
+ tr.setSelection(newSelection);
194
+ const deleteFrom = lastChildPos;
195
+ const deleteTo = lastChildPos + lastChildNode.nodeSize;
196
+ tr.delete(deleteFrom, deleteTo);
197
+ tr.scrollIntoView();
198
+ view.dispatch(tr);
199
+ return true;
200
+ }
201
+ };
202
+ }
203
+ });
204
+
205
+ // src/Details.ts
206
+ var detailsSelectionPluginKey = new state.PluginKey("detailsSelection");
207
+ var Details = core.Node.create({
208
+ name: "details",
209
+ group: "block",
210
+ content: "detailsSummary detailsContent",
211
+ defining: true,
212
+ isolating: true,
213
+ allowGapCursor: false,
214
+ addOptions() {
215
+ return {
216
+ persist: false,
217
+ openClassName: "is-open",
218
+ HTMLAttributes: {}
219
+ };
220
+ },
221
+ addAttributes() {
222
+ if (!this.options.persist) {
223
+ return {};
224
+ }
225
+ return {
226
+ open: {
227
+ default: false,
228
+ parseHTML: (element) => element.hasAttribute("open"),
229
+ renderHTML: (attrs) => {
230
+ if (!attrs["open"]) {
231
+ return {};
232
+ }
233
+ return { open: "" };
234
+ }
235
+ }
236
+ };
237
+ },
238
+ parseHTML() {
239
+ return [
240
+ { tag: "details" },
241
+ { tag: 'div[data-type="details"]' }
242
+ ];
243
+ },
244
+ renderHTML({ HTMLAttributes }) {
245
+ return ["details", { ...this.options.HTMLAttributes, ...HTMLAttributes }, 0];
246
+ },
247
+ addNodeView() {
248
+ const options = this.options;
249
+ const editor = this.editor;
250
+ const nodeType = this.nodeType;
251
+ return (node, _view, getPos) => {
252
+ const dom = document.createElement("div");
253
+ dom.setAttribute("data-type", "details");
254
+ for (const [key, value] of Object.entries(options.HTMLAttributes)) {
255
+ if (value !== null && value !== void 0) {
256
+ dom.setAttribute(key, typeof value === "string" ? value : JSON.stringify(value));
257
+ }
258
+ }
259
+ const toggle = document.createElement("button");
260
+ toggle.type = "button";
261
+ toggle.addEventListener("mousedown", (e) => {
262
+ e.preventDefault();
263
+ });
264
+ dom.append(toggle);
265
+ const content = document.createElement("div");
266
+ dom.append(content);
267
+ const toggleDetailsContent = (setToValue) => {
268
+ const isOpen = dom.classList.contains(options.openClassName);
269
+ if (setToValue !== void 0 && setToValue === isOpen) return;
270
+ dom.classList.toggle(options.openClassName, setToValue);
271
+ content.querySelector(":scope > div[data-details-content]")?.dispatchEvent(new Event("toggleDetailsContent"));
272
+ };
273
+ if (node.attrs["open"]) {
274
+ setTimeout(() => {
275
+ toggleDetailsContent();
276
+ });
277
+ }
278
+ toggle.addEventListener("click", () => {
279
+ toggleDetailsContent();
280
+ if (!options.persist) {
281
+ if (editor) {
282
+ editor.commands["focus"]?.();
283
+ }
284
+ return;
285
+ }
286
+ if (editor && typeof getPos === "function") {
287
+ const pos = getPos();
288
+ if (pos === void 0) return;
289
+ const { state: state$1, view } = editor;
290
+ const { from, to } = state$1.selection;
291
+ const currentNode = state$1.doc.nodeAt(pos);
292
+ if (currentNode?.type !== nodeType) return;
293
+ const tr = state$1.tr.setNodeMarkup(pos, void 0, {
294
+ open: !currentNode.attrs["open"]
295
+ });
296
+ tr.setSelection(state.TextSelection.create(tr.doc, from, to));
297
+ view.dispatch(tr);
298
+ editor.commands["focus"]?.();
299
+ }
300
+ });
301
+ return {
302
+ dom,
303
+ contentDOM: content,
304
+ ignoreMutation(mutation) {
305
+ if (mutation.type === "selection") {
306
+ return false;
307
+ }
308
+ return !dom.contains(mutation.target) || dom === mutation.target;
309
+ },
310
+ update: (updatedNode) => {
311
+ if (updatedNode.type !== nodeType) {
312
+ return false;
313
+ }
314
+ if (updatedNode.attrs["open"] !== void 0) {
315
+ toggleDetailsContent(updatedNode.attrs["open"]);
316
+ }
317
+ return true;
318
+ }
319
+ };
320
+ };
321
+ },
322
+ addToolbarItems() {
323
+ return [
324
+ {
325
+ type: "button",
326
+ name: "details",
327
+ command: "toggleDetails",
328
+ isActive: "details",
329
+ icon: "caretCircleRight",
330
+ label: "Toggle Details",
331
+ group: "insert",
332
+ priority: 100
333
+ }
334
+ ];
335
+ },
336
+ addExtensions() {
337
+ return [DetailsSummary, DetailsContent];
338
+ },
339
+ addCommands() {
340
+ return {
341
+ setDetails: () => ({ state: state$1, tr, dispatch }) => {
342
+ const { schema } = state$1;
343
+ const { $from, $to } = tr.selection;
344
+ const range = $from.blockRange($to);
345
+ if (!range) return false;
346
+ const detailsType = schema.nodes["details"];
347
+ const summaryType = schema.nodes["detailsSummary"];
348
+ const contentType = schema.nodes["detailsContent"];
349
+ if (!detailsType || !summaryType || !contentType) return false;
350
+ for (let d = $from.depth; d > 0; d--) {
351
+ if ($from.node(d).type === detailsType) return false;
352
+ }
353
+ if (!dispatch) return true;
354
+ const selectedContent = [];
355
+ for (let i = range.startIndex; i < range.endIndex; i++) {
356
+ selectedContent.push(range.parent.child(i));
357
+ }
358
+ if (selectedContent.length === 0) return false;
359
+ const summary = summaryType.create(null);
360
+ const content = contentType.create(null, selectedContent);
361
+ const details = detailsType.create(null, [summary, content]);
362
+ tr.replaceWith(range.start, range.end, details);
363
+ tr.setSelection(state.TextSelection.create(tr.doc, range.start + 2));
364
+ dispatch(tr.scrollIntoView());
365
+ return true;
366
+ },
367
+ unsetDetails: () => ({ state: state$1, tr, dispatch }) => {
368
+ const { schema } = state$1;
369
+ const detailsType = schema.nodes["details"];
370
+ if (!detailsType) return false;
371
+ const details = core.findParentNode(
372
+ (node) => node.type === detailsType
373
+ )(tr.selection);
374
+ if (!details) return false;
375
+ if (!dispatch) return true;
376
+ const summaries = core.findChildren(
377
+ details.node,
378
+ (node) => node.type === schema.nodes["detailsSummary"]
379
+ );
380
+ const contents = core.findChildren(
381
+ details.node,
382
+ (node) => node.type === schema.nodes["detailsContent"]
383
+ );
384
+ if (!summaries.length || !contents.length) return false;
385
+ const detailsSummary = summaries[0];
386
+ const detailsContent = contents[0];
387
+ if (!detailsSummary || !detailsContent) return false;
388
+ const from = details.pos;
389
+ const $from = state$1.doc.resolve(from);
390
+ const to = from + details.node.nodeSize;
391
+ const defaultType = $from.parent.type.contentMatch.defaultType;
392
+ const blocks = [];
393
+ if (defaultType && detailsSummary.node.content.size > 0) {
394
+ blocks.push(defaultType.create(null, detailsSummary.node.content));
395
+ }
396
+ detailsContent.node.forEach((child) => blocks.push(child));
397
+ tr.replaceWith(from, to, blocks);
398
+ tr.setSelection(state.TextSelection.create(tr.doc, from + 1));
399
+ dispatch(tr.scrollIntoView());
400
+ return true;
401
+ },
402
+ toggleDetails: () => ({ state: state$1, tr, dispatch }) => {
403
+ const { schema } = state$1;
404
+ const detailsType = schema.nodes["details"];
405
+ const summaryType = schema.nodes["detailsSummary"];
406
+ const contentType = schema.nodes["detailsContent"];
407
+ if (!detailsType || !summaryType || !contentType) return false;
408
+ const wrapInDetails = (t, $from, $to) => {
409
+ const range = $from.blockRange($to);
410
+ if (!range) return -1;
411
+ for (let d = $from.depth; d > 0; d--) {
412
+ if ($from.node(d).type === detailsType) return -1;
413
+ }
414
+ const blocks = [];
415
+ for (let i = range.startIndex; i < range.endIndex; i++) {
416
+ blocks.push(range.parent.child(i));
417
+ }
418
+ if (blocks.length === 0) return -1;
419
+ const summary = summaryType.create(null);
420
+ const content = contentType.create(null, blocks);
421
+ t.replaceWith(range.start, range.end, detailsType.create(null, [summary, content]));
422
+ return range.start;
423
+ };
424
+ const unwrapDetails = (t, details) => {
425
+ const sums = core.findChildren(details.node, (n) => n.type === summaryType);
426
+ const conts = core.findChildren(details.node, (n) => n.type === contentType);
427
+ if (!sums.length || !conts.length) return -1;
428
+ const detailsSummary = sums[0];
429
+ const detailsContent = conts[0];
430
+ if (!detailsSummary || !detailsContent) return -1;
431
+ const from = details.pos;
432
+ const $f = t.doc.resolve(from);
433
+ const to = from + details.node.nodeSize;
434
+ const defaultType = $f.parent.type.contentMatch.defaultType;
435
+ const result = [];
436
+ if (defaultType && detailsSummary.node.content.size > 0) {
437
+ result.push(defaultType.create(null, detailsSummary.node.content));
438
+ }
439
+ detailsContent.node.forEach((child) => result.push(child));
440
+ t.replaceWith(from, to, result);
441
+ return from;
442
+ };
443
+ const { ranges } = state$1.selection;
444
+ if (ranges.length <= 1) {
445
+ const { $from } = state$1.selection;
446
+ for (let d = $from.depth; d > 0; d--) {
447
+ if ($from.node(d).type === detailsType) {
448
+ if (!dispatch) return true;
449
+ const details = core.findParentNode((n) => n.type === detailsType)(tr.selection);
450
+ if (!details) return false;
451
+ const pos2 = unwrapDetails(tr, details);
452
+ if (pos2 < 0) return false;
453
+ tr.setSelection(state.TextSelection.create(tr.doc, pos2 + 1));
454
+ dispatch(tr.scrollIntoView());
455
+ return true;
456
+ }
457
+ }
458
+ if (!dispatch) return true;
459
+ const pos = wrapInDetails(tr, tr.selection.$from, tr.selection.$to);
460
+ if (pos < 0) return false;
461
+ tr.setSelection(state.TextSelection.create(tr.doc, pos + 2));
462
+ dispatch(tr.scrollIntoView());
463
+ return true;
464
+ }
465
+ const cellHasDetails = (doc, from) => {
466
+ const $f = doc.resolve(from);
467
+ const cell = $f.parent;
468
+ return cell.childCount === 1 && cell.firstChild?.type === detailsType;
469
+ };
470
+ const cells = ranges.map((r) => ({ from: r.$from.pos, to: r.$to.pos })).sort((a, b) => b.from - a.from);
471
+ const allInDetails = cells.every((c) => cellHasDetails(state$1.doc, c.from));
472
+ if (!dispatch) return true;
473
+ for (const cell of cells) {
474
+ const from = tr.mapping.map(cell.from);
475
+ const to = tr.mapping.map(cell.to);
476
+ if (allInDetails) {
477
+ const detailsNode = tr.doc.nodeAt(from);
478
+ if (detailsNode?.type === detailsType) {
479
+ unwrapDetails(tr, { pos: from, node: detailsNode });
480
+ }
481
+ } else {
482
+ if (cellHasDetails(tr.doc, from)) continue;
483
+ const $f = tr.doc.resolve(from);
484
+ const $to2 = tr.doc.resolve(to);
485
+ wrapInDetails(tr, $f, $to2);
486
+ }
487
+ }
488
+ dispatch(tr.scrollIntoView());
489
+ return true;
490
+ },
491
+ openDetails: () => ({ commands }) => commands.setDetailsOpen(true),
492
+ closeDetails: () => ({ commands }) => commands.setDetailsOpen(false),
493
+ setDetailsOpen: (open) => ({ state, tr, dispatch }) => {
494
+ if (!this.options.persist) return false;
495
+ const detailsType = state.schema.nodes["details"];
496
+ if (!detailsType) return false;
497
+ const details = core.findParentNode(
498
+ (node) => node.type === detailsType
499
+ )(state.selection);
500
+ if (!details) return false;
501
+ if (!!details.node.attrs["open"] === open) return false;
502
+ if (dispatch) {
503
+ tr.setNodeMarkup(details.pos, void 0, {
504
+ ...details.node.attrs,
505
+ open
506
+ });
507
+ dispatch(tr);
508
+ }
509
+ return true;
510
+ }
511
+ };
512
+ },
513
+ addKeyboardShortcuts() {
514
+ return {
515
+ Backspace: () => {
516
+ const editor = this.editor;
517
+ if (!editor) return false;
518
+ const { schema, selection } = editor.state;
519
+ const { empty, $anchor } = selection;
520
+ const summaryType = schema.nodes["detailsSummary"];
521
+ if (!empty || !summaryType || $anchor.parent.type !== summaryType) {
522
+ return false;
523
+ }
524
+ if ($anchor.parentOffset !== 0) {
525
+ const { tr } = editor.state;
526
+ tr.delete($anchor.pos - 1, $anchor.pos);
527
+ editor.view.dispatch(tr);
528
+ return true;
529
+ }
530
+ return editor.commands["unsetDetails"]?.() ?? false;
531
+ },
532
+ Enter: () => {
533
+ const editor = this.editor;
534
+ if (!editor) return false;
535
+ const { state: state$1, view } = editor;
536
+ const { schema, selection } = state$1;
537
+ const { $head } = selection;
538
+ const summaryType = schema.nodes["detailsSummary"];
539
+ if (!summaryType || $head.parent.type !== summaryType) {
540
+ return false;
541
+ }
542
+ const isVisible = isNodeVisible($head.after() + 1, editor);
543
+ if (!isVisible) {
544
+ const detailsPos = $head.before(-1);
545
+ const detailsDom = view.nodeDOM(detailsPos);
546
+ if (detailsDom instanceof HTMLElement) {
547
+ detailsDom.classList.add(this.options.openClassName);
548
+ const contentEl = detailsDom.querySelector("[data-details-content]");
549
+ if (contentEl) {
550
+ contentEl.dispatchEvent(new Event("toggleDetailsContent"));
551
+ }
552
+ }
553
+ if (this.options.persist) {
554
+ const detailsNode = state$1.doc.nodeAt(detailsPos);
555
+ if (detailsNode) {
556
+ const { tr: tr2 } = state$1;
557
+ tr2.setNodeMarkup(detailsPos, void 0, {
558
+ ...detailsNode.attrs,
559
+ open: true
560
+ });
561
+ const contentStartPos = $head.after() + 1;
562
+ tr2.setSelection(state.Selection.near(tr2.doc.resolve(contentStartPos), 1));
563
+ tr2.scrollIntoView();
564
+ view.dispatch(tr2);
565
+ return true;
566
+ }
567
+ }
568
+ }
569
+ const above = state$1.doc.nodeAt($head.after());
570
+ if (!above) return false;
571
+ const type = core.defaultBlockAt(above.contentMatchAt(0));
572
+ if (!type || !above.canReplaceWith(0, 0, type)) {
573
+ return false;
574
+ }
575
+ const node = type.createAndFill();
576
+ if (!node) return false;
577
+ const pos = $head.after() + 1;
578
+ const tr = state$1.tr.replaceWith(pos, pos, node);
579
+ const $pos = tr.doc.resolve(pos);
580
+ const newSelection = state.Selection.near($pos, 1);
581
+ tr.setSelection(newSelection);
582
+ tr.setMeta("detailsEnterOpen", true);
583
+ tr.scrollIntoView();
584
+ view.dispatch(tr);
585
+ return true;
586
+ },
587
+ ArrowRight: () => {
588
+ const editor = this.editor;
589
+ if (!editor) return false;
590
+ return setGapCursor(editor, "right");
591
+ },
592
+ ArrowDown: () => {
593
+ const editor = this.editor;
594
+ if (!editor) return false;
595
+ return setGapCursor(editor, "down");
596
+ }
597
+ };
598
+ },
599
+ addProseMirrorPlugins() {
600
+ const editor = this.editor;
601
+ const nodeType = this.nodeType;
602
+ return [
603
+ // Prevent text selections within hidden content
604
+ new state.Plugin({
605
+ key: detailsSelectionPluginKey,
606
+ appendTransaction: (transactions, oldState, newState) => {
607
+ if (!editor) return;
608
+ const isComposing = editor.view.composing;
609
+ if (isComposing) return;
610
+ const selectionSet = transactions.some((t) => t.selectionSet);
611
+ if (!selectionSet || !oldState.selection.empty || !newState.selection.empty) {
612
+ return;
613
+ }
614
+ if (transactions.some((t) => t.getMeta("detailsEnterOpen"))) {
615
+ return;
616
+ }
617
+ if (!nodeType) return;
618
+ const { $from } = newState.selection;
619
+ let inDetails = false;
620
+ for (let d = $from.depth; d > 0; d--) {
621
+ if ($from.node(d).type === nodeType) {
622
+ inDetails = true;
623
+ break;
624
+ }
625
+ }
626
+ if (!inDetails) return;
627
+ const visible = isNodeVisible($from.pos, editor);
628
+ if (visible) return;
629
+ const details = findClosestVisibleNode(
630
+ $from,
631
+ (node) => node.type === nodeType,
632
+ editor
633
+ );
634
+ if (!details) return;
635
+ const detailsSummaries = core.findChildren(
636
+ details.node,
637
+ (node) => node.type === newState.schema.nodes["detailsSummary"]
638
+ );
639
+ if (!detailsSummaries.length) return;
640
+ const detailsSummary = detailsSummaries[0];
641
+ if (!detailsSummary) return;
642
+ const selectionDirection = oldState.selection.from < newState.selection.from ? "forward" : "backward";
643
+ const correctedPosition = selectionDirection === "forward" ? details.start + detailsSummary.pos : details.pos + detailsSummary.pos + detailsSummary.node.nodeSize;
644
+ const correctedSelection = state.TextSelection.create(newState.doc, correctedPosition);
645
+ return newState.tr.setSelection(correctedSelection);
646
+ }
647
+ })
648
+ ];
649
+ }
650
+ });
651
+
652
+ exports.Details = Details;
653
+ exports.DetailsContent = DetailsContent;
654
+ exports.DetailsSummary = DetailsSummary;
655
+ //# sourceMappingURL=index.cjs.map
656
+ //# sourceMappingURL=index.cjs.map