@tiptap/extension-drag-handle 3.20.3 → 3.20.5

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,907 @@
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
+ DragHandle: () => DragHandle,
24
+ DragHandlePlugin: () => DragHandlePlugin,
25
+ default: () => index_default,
26
+ defaultComputePositionConfig: () => defaultComputePositionConfig,
27
+ defaultRules: () => defaultRules,
28
+ dragHandlePluginDefaultKey: () => dragHandlePluginDefaultKey,
29
+ normalizeNestedOptions: () => normalizeNestedOptions
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/drag-handle.ts
34
+ var import_core2 = require("@tiptap/core");
35
+
36
+ // src/drag-handle-plugin.ts
37
+ var import_dom = require("@floating-ui/dom");
38
+ var import_core = require("@tiptap/core");
39
+ var import_extension_collaboration = require("@tiptap/extension-collaboration");
40
+ var import_state2 = require("@tiptap/pm/state");
41
+ var import_y_tiptap = require("@tiptap/y-tiptap");
42
+
43
+ // src/helpers/dragHandler.ts
44
+ var import_extension_node_range = require("@tiptap/extension-node-range");
45
+ var import_state = require("@tiptap/pm/state");
46
+
47
+ // src/helpers/cloneElement.ts
48
+ function getCSSText(element) {
49
+ let value = "";
50
+ const style = getComputedStyle(element);
51
+ for (let i = 0; i < style.length; i += 1) {
52
+ value += `${style[i]}:${style.getPropertyValue(style[i])};`;
53
+ }
54
+ return value;
55
+ }
56
+ function cloneElement(node) {
57
+ const clonedNode = node.cloneNode(true);
58
+ const sourceElements = [node, ...Array.from(node.getElementsByTagName("*"))];
59
+ const targetElements = [clonedNode, ...Array.from(clonedNode.getElementsByTagName("*"))];
60
+ sourceElements.forEach((sourceElement, index) => {
61
+ targetElements[index].style.cssText = getCSSText(sourceElement);
62
+ });
63
+ return clonedNode;
64
+ }
65
+
66
+ // src/helpers/defaultRules.ts
67
+ var listItemFirstChild = {
68
+ id: "listItemFirstChild",
69
+ evaluate: ({ parent, isFirst }) => {
70
+ if (!isFirst) {
71
+ return 0;
72
+ }
73
+ const listItemTypes = ["listItem", "taskItem"];
74
+ if (parent && listItemTypes.includes(parent.type.name)) {
75
+ return 1e3;
76
+ }
77
+ return 0;
78
+ }
79
+ };
80
+ var listWrapperDeprioritize = {
81
+ id: "listWrapperDeprioritize",
82
+ evaluate: ({ node }) => {
83
+ const listItemTypes = ["listItem", "taskItem"];
84
+ const firstChild = node.firstChild;
85
+ if (firstChild && listItemTypes.includes(firstChild.type.name)) {
86
+ return 1e3;
87
+ }
88
+ return 0;
89
+ }
90
+ };
91
+ var tableStructure = {
92
+ id: "tableStructure",
93
+ evaluate: ({ node, parent }) => {
94
+ const tableStructureTypes = ["tableRow", "tableCell", "tableHeader"];
95
+ if (tableStructureTypes.includes(node.type.name)) {
96
+ return 1e3;
97
+ }
98
+ if (parent && parent.type.name === "tableHeader") {
99
+ return 1e3;
100
+ }
101
+ return 0;
102
+ }
103
+ };
104
+ var inlineContent = {
105
+ id: "inlineContent",
106
+ evaluate: ({ node }) => {
107
+ if (node.isInline || node.isText) {
108
+ return 1e3;
109
+ }
110
+ return 0;
111
+ }
112
+ };
113
+ var defaultRules = [
114
+ listItemFirstChild,
115
+ listWrapperDeprioritize,
116
+ tableStructure,
117
+ inlineContent
118
+ ];
119
+
120
+ // src/helpers/edgeDetection.ts
121
+ var DEFAULT_EDGE_CONFIG = {
122
+ edges: ["left", "top"],
123
+ threshold: 12,
124
+ strength: 500
125
+ };
126
+ function normalizeEdgeDetection(input) {
127
+ if (input === void 0 || input === "left") {
128
+ return { ...DEFAULT_EDGE_CONFIG };
129
+ }
130
+ if (input === "right") {
131
+ return { edges: ["right", "top"], threshold: 12, strength: 500 };
132
+ }
133
+ if (input === "both") {
134
+ return { edges: ["left", "right", "top"], threshold: 12, strength: 500 };
135
+ }
136
+ if (input === "none") {
137
+ return { edges: [], threshold: 0, strength: 0 };
138
+ }
139
+ return { ...DEFAULT_EDGE_CONFIG, ...input };
140
+ }
141
+ function isNearEdge(coords, element, config) {
142
+ if (config.edges.length === 0) {
143
+ return false;
144
+ }
145
+ const rect = element.getBoundingClientRect();
146
+ const { threshold, edges } = config;
147
+ return edges.some((edge) => {
148
+ if (edge === "left") {
149
+ return coords.x - rect.left < threshold;
150
+ }
151
+ if (edge === "right") {
152
+ return rect.right - coords.x < threshold;
153
+ }
154
+ if (edge === "top") {
155
+ return coords.y - rect.top < threshold;
156
+ }
157
+ if (edge === "bottom") {
158
+ return rect.bottom - coords.y < threshold;
159
+ }
160
+ return false;
161
+ });
162
+ }
163
+ function calculateEdgeDeduction(coords, element, config, depth) {
164
+ if (!element || config.edges.length === 0) {
165
+ return 0;
166
+ }
167
+ if (isNearEdge(coords, element, config)) {
168
+ return config.strength * depth;
169
+ }
170
+ return 0;
171
+ }
172
+
173
+ // src/helpers/scoring.ts
174
+ var BASE_SCORE = 1e3;
175
+ function calculateScore(context, rules, edgeConfig, coords) {
176
+ let score = BASE_SCORE;
177
+ let excluded = false;
178
+ rules.every((rule) => {
179
+ const deduction = rule.evaluate(context);
180
+ score -= deduction;
181
+ if (score <= 0) {
182
+ excluded = true;
183
+ return false;
184
+ }
185
+ return true;
186
+ });
187
+ if (excluded) {
188
+ return -1;
189
+ }
190
+ const dom = context.view.nodeDOM(context.pos);
191
+ score -= calculateEdgeDeduction(coords, dom, edgeConfig, context.depth);
192
+ if (score <= 0) {
193
+ return -1;
194
+ }
195
+ return score;
196
+ }
197
+
198
+ // src/helpers/findBestDragTarget.ts
199
+ function hasAncestorOfType($pos, depth, allowedTypes) {
200
+ const ancestorDepths = Array.from({ length: depth }, (_, i) => depth - 1 - i);
201
+ return ancestorDepths.some((d) => allowedTypes.includes($pos.node(d).type.name));
202
+ }
203
+ function findBestDragTarget(view, coords, options) {
204
+ if (!Number.isFinite(coords.x) || !Number.isFinite(coords.y)) {
205
+ return null;
206
+ }
207
+ const posInfo = view.posAtCoords({ left: coords.x, top: coords.y });
208
+ if (!posInfo) {
209
+ return null;
210
+ }
211
+ const { doc } = view.state;
212
+ const $pos = doc.resolve(posInfo.pos);
213
+ const rules = [];
214
+ if (options.defaultRules) {
215
+ rules.push(...defaultRules);
216
+ }
217
+ rules.push(...options.rules);
218
+ const depthLevels = Array.from({ length: $pos.depth }, (_, i) => $pos.depth - i);
219
+ const candidates = depthLevels.map((depth) => {
220
+ const node = $pos.node(depth);
221
+ const nodePos = $pos.before(depth);
222
+ if (options.allowedContainers && depth > 0) {
223
+ const inAllowedContainer = hasAncestorOfType($pos, depth, options.allowedContainers);
224
+ if (!inAllowedContainer) {
225
+ return null;
226
+ }
227
+ }
228
+ const parent = depth > 0 ? $pos.node(depth - 1) : null;
229
+ const index = depth > 0 ? $pos.index(depth - 1) : 0;
230
+ const siblingCount = parent ? parent.childCount : 1;
231
+ const context = {
232
+ node,
233
+ pos: nodePos,
234
+ depth,
235
+ parent,
236
+ index,
237
+ isFirst: index === 0,
238
+ isLast: index === siblingCount - 1,
239
+ $pos,
240
+ view
241
+ };
242
+ const score = calculateScore(context, rules, options.edgeDetection, coords);
243
+ if (score < 0) {
244
+ return null;
245
+ }
246
+ const dom = view.nodeDOM(nodePos);
247
+ return { node, pos: nodePos, depth, score, dom };
248
+ }).filter((candidate) => candidate !== null);
249
+ const nodeAfter = $pos.nodeAfter;
250
+ if (nodeAfter && nodeAfter.isAtom && !nodeAfter.isInline) {
251
+ const nodePos = posInfo.pos;
252
+ const depth = $pos.depth + 1;
253
+ const parent = $pos.parent;
254
+ const index = $pos.index();
255
+ const siblingCount = parent.childCount;
256
+ let inAllowedContainer = true;
257
+ if (options.allowedContainers) {
258
+ inAllowedContainer = hasAncestorOfType($pos, depth, options.allowedContainers);
259
+ }
260
+ if (inAllowedContainer) {
261
+ const context = {
262
+ node: nodeAfter,
263
+ pos: nodePos,
264
+ depth,
265
+ parent,
266
+ index,
267
+ isFirst: index === 0,
268
+ isLast: index === siblingCount - 1,
269
+ $pos,
270
+ view
271
+ };
272
+ const score = calculateScore(context, rules, options.edgeDetection, coords);
273
+ if (score >= 0) {
274
+ const dom = view.nodeDOM(nodePos);
275
+ if (dom) {
276
+ candidates.push({ node: nodeAfter, pos: nodePos, depth, score, dom });
277
+ }
278
+ }
279
+ }
280
+ }
281
+ if (candidates.length === 0) {
282
+ return null;
283
+ }
284
+ candidates.sort((a, b) => {
285
+ if (b.score !== a.score) {
286
+ return b.score - a.score;
287
+ }
288
+ return b.depth - a.depth;
289
+ });
290
+ const winner = candidates[0];
291
+ if (!winner.dom) {
292
+ return null;
293
+ }
294
+ return {
295
+ node: winner.node,
296
+ pos: winner.pos,
297
+ dom: winner.dom
298
+ };
299
+ }
300
+
301
+ // src/helpers/findNextElementFromCursor.ts
302
+ function findClosestTopLevelBlock(element, view) {
303
+ let current = element;
304
+ while ((current == null ? void 0 : current.parentElement) && current.parentElement !== view.dom) {
305
+ current = current.parentElement;
306
+ }
307
+ return (current == null ? void 0 : current.parentElement) === view.dom ? current : void 0;
308
+ }
309
+ function isValidRect(rect) {
310
+ return Number.isFinite(rect.top) && Number.isFinite(rect.bottom) && Number.isFinite(rect.left) && Number.isFinite(rect.right) && rect.width > 0 && rect.height > 0;
311
+ }
312
+ function clampToContent(view, x, y, inset = 5) {
313
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
314
+ return null;
315
+ }
316
+ const container = view.dom;
317
+ const firstBlock = container.firstElementChild;
318
+ const lastBlock = container.lastElementChild;
319
+ if (!firstBlock || !lastBlock) {
320
+ return null;
321
+ }
322
+ const topRect = firstBlock.getBoundingClientRect();
323
+ const botRect = lastBlock.getBoundingClientRect();
324
+ if (!isValidRect(topRect) || !isValidRect(botRect)) {
325
+ return null;
326
+ }
327
+ const clampedY = Math.min(Math.max(topRect.top + inset, y), botRect.bottom - inset);
328
+ const epsilon = 0.5;
329
+ const sameLeft = Math.abs(topRect.left - botRect.left) < epsilon;
330
+ const sameRight = Math.abs(topRect.right - botRect.right) < epsilon;
331
+ let rowRect = topRect;
332
+ if (sameLeft && sameRight) {
333
+ rowRect = topRect;
334
+ } else {
335
+ }
336
+ const clampedX = Math.min(Math.max(rowRect.left + inset, x), rowRect.right - inset);
337
+ if (!Number.isFinite(clampedX) || !Number.isFinite(clampedY)) {
338
+ return null;
339
+ }
340
+ return { x: clampedX, y: clampedY };
341
+ }
342
+ var findElementNextToCoords = (options) => {
343
+ const { x, y, editor, nestedOptions } = options;
344
+ const { view, state } = editor;
345
+ const clamped = clampToContent(view, x, y, 5);
346
+ if (!clamped) {
347
+ return { resultElement: null, resultNode: null, pos: null };
348
+ }
349
+ const { x: clampedX, y: clampedY } = clamped;
350
+ if (nestedOptions == null ? void 0 : nestedOptions.enabled) {
351
+ const target = findBestDragTarget(view, { x: clampedX, y: clampedY }, nestedOptions);
352
+ if (!target) {
353
+ return { resultElement: null, resultNode: null, pos: null };
354
+ }
355
+ return {
356
+ resultElement: target.dom,
357
+ resultNode: target.node,
358
+ pos: target.pos
359
+ };
360
+ }
361
+ const elements = view.root.elementsFromPoint(clampedX, clampedY);
362
+ let block;
363
+ Array.prototype.some.call(elements, (el) => {
364
+ if (!view.dom.contains(el)) {
365
+ return false;
366
+ }
367
+ const candidate = findClosestTopLevelBlock(el, view);
368
+ if (candidate) {
369
+ block = candidate;
370
+ return true;
371
+ }
372
+ return false;
373
+ });
374
+ if (!block) {
375
+ return { resultElement: null, resultNode: null, pos: null };
376
+ }
377
+ let pos;
378
+ try {
379
+ pos = view.posAtDOM(block, 0);
380
+ } catch {
381
+ return { resultElement: null, resultNode: null, pos: null };
382
+ }
383
+ const node = state.doc.nodeAt(pos);
384
+ if (!node) {
385
+ const resolvedPos = state.doc.resolve(pos);
386
+ const parent = resolvedPos.parent;
387
+ return {
388
+ resultElement: block,
389
+ resultNode: parent,
390
+ pos: resolvedPos.start()
391
+ };
392
+ }
393
+ return {
394
+ resultElement: block,
395
+ resultNode: node,
396
+ pos
397
+ };
398
+ };
399
+
400
+ // src/helpers/removeNode.ts
401
+ function removeNode(node) {
402
+ var _a;
403
+ (_a = node.parentNode) == null ? void 0 : _a.removeChild(node);
404
+ }
405
+
406
+ // src/helpers/dragHandler.ts
407
+ function getDragHandleRanges(event, editor, nestedOptions, dragContext) {
408
+ const { doc } = editor.view.state;
409
+ if ((nestedOptions == null ? void 0 : nestedOptions.enabled) && (dragContext == null ? void 0 : dragContext.node) && dragContext.pos >= 0) {
410
+ const nodeStart = dragContext.pos;
411
+ const nodeEnd = dragContext.pos + dragContext.node.nodeSize;
412
+ return [
413
+ {
414
+ $from: doc.resolve(nodeStart),
415
+ $to: doc.resolve(nodeEnd)
416
+ }
417
+ ];
418
+ }
419
+ const result = findElementNextToCoords({
420
+ editor,
421
+ x: event.clientX,
422
+ y: event.clientY,
423
+ direction: "right",
424
+ nestedOptions
425
+ });
426
+ if (!result.resultNode || result.pos === null) {
427
+ return [];
428
+ }
429
+ const offset = result.resultNode.isText || result.resultNode.isAtom ? 0 : -1;
430
+ const $from = doc.resolve(result.pos);
431
+ const $to = doc.resolve(result.pos + result.resultNode.nodeSize + offset);
432
+ return (0, import_extension_node_range.getSelectionRanges)($from, $to, 0);
433
+ }
434
+ function dragHandler(event, editor, nestedOptions, dragContext) {
435
+ const { view } = editor;
436
+ if (!event.dataTransfer) {
437
+ return;
438
+ }
439
+ const { empty, $from, $to } = view.state.selection;
440
+ const dragHandleRanges = getDragHandleRanges(event, editor, nestedOptions, dragContext);
441
+ const selectionRanges = (0, import_extension_node_range.getSelectionRanges)($from, $to, 0);
442
+ const isDragHandleWithinSelection = selectionRanges.some((range) => {
443
+ return dragHandleRanges.find((dragHandleRange) => {
444
+ return dragHandleRange.$from === range.$from && dragHandleRange.$to === range.$to;
445
+ });
446
+ });
447
+ const ranges = empty || !isDragHandleWithinSelection ? dragHandleRanges : selectionRanges;
448
+ if (!ranges.length) {
449
+ return;
450
+ }
451
+ const { tr } = view.state;
452
+ const wrapper = document.createElement("div");
453
+ const from = ranges[0].$from.pos;
454
+ const to = ranges[ranges.length - 1].$to.pos;
455
+ const isNestedDrag = (nestedOptions == null ? void 0 : nestedOptions.enabled) && (dragContext == null ? void 0 : dragContext.node);
456
+ let slice;
457
+ let selection;
458
+ if (isNestedDrag) {
459
+ slice = view.state.doc.slice(from, to);
460
+ selection = import_state.NodeSelection.create(view.state.doc, from);
461
+ } else {
462
+ selection = import_extension_node_range.NodeRangeSelection.create(view.state.doc, from, to);
463
+ slice = selection.content();
464
+ }
465
+ ranges.forEach((range) => {
466
+ const element = view.nodeDOM(range.$from.pos);
467
+ const clonedElement = cloneElement(element);
468
+ wrapper.append(clonedElement);
469
+ });
470
+ wrapper.style.position = "absolute";
471
+ wrapper.style.top = "-10000px";
472
+ document.body.append(wrapper);
473
+ event.dataTransfer.clearData();
474
+ event.dataTransfer.setDragImage(wrapper, 0, 0);
475
+ const nodeSelection = selection instanceof import_state.NodeSelection ? selection : void 0;
476
+ view.dragging = { slice, move: true, node: nodeSelection };
477
+ tr.setSelection(selection);
478
+ view.dispatch(tr);
479
+ document.addEventListener("drop", () => removeNode(wrapper), { once: true });
480
+ }
481
+
482
+ // src/helpers/getOuterNode.ts
483
+ var getOuterNodePos = (doc, pos) => {
484
+ const resolvedPos = doc.resolve(pos);
485
+ const { depth } = resolvedPos;
486
+ if (depth === 0) {
487
+ return pos;
488
+ }
489
+ const a = resolvedPos.pos - resolvedPos.parentOffset;
490
+ return a - 1;
491
+ };
492
+ var getOuterNode = (doc, pos) => {
493
+ const node = doc.nodeAt(pos);
494
+ const resolvedPos = doc.resolve(pos);
495
+ let { depth } = resolvedPos;
496
+ let parent = node;
497
+ while (depth > 0) {
498
+ const currentNode = resolvedPos.node(depth);
499
+ depth -= 1;
500
+ if (depth === 0) {
501
+ parent = currentNode;
502
+ }
503
+ }
504
+ return parent;
505
+ };
506
+
507
+ // src/drag-handle-plugin.ts
508
+ var getRelativePos = (state, absolutePos) => {
509
+ const ystate = import_y_tiptap.ySyncPluginKey.getState(state);
510
+ if (!ystate) {
511
+ return null;
512
+ }
513
+ return (0, import_y_tiptap.absolutePositionToRelativePosition)(absolutePos, ystate.type, ystate.binding.mapping);
514
+ };
515
+ var getAbsolutePos = (state, relativePos) => {
516
+ const ystate = import_y_tiptap.ySyncPluginKey.getState(state);
517
+ if (!ystate) {
518
+ return -1;
519
+ }
520
+ return (0, import_y_tiptap.relativePositionToAbsolutePosition)(ystate.doc, ystate.type, relativePos, ystate.binding.mapping) || 0;
521
+ };
522
+ var getOuterDomNode = (view, domNode) => {
523
+ let tmpDomNode = domNode;
524
+ while (tmpDomNode == null ? void 0 : tmpDomNode.parentNode) {
525
+ if (tmpDomNode.parentNode === view.dom) {
526
+ break;
527
+ }
528
+ tmpDomNode = tmpDomNode.parentNode;
529
+ }
530
+ return tmpDomNode;
531
+ };
532
+ var dragHandlePluginDefaultKey = new import_state2.PluginKey("dragHandle");
533
+ var DragHandlePlugin = ({
534
+ pluginKey = dragHandlePluginDefaultKey,
535
+ element,
536
+ editor,
537
+ computePositionConfig,
538
+ getReferencedVirtualElement,
539
+ onNodeChange,
540
+ onElementDragStart,
541
+ onElementDragEnd,
542
+ nestedOptions
543
+ }) => {
544
+ const wrapper = document.createElement("div");
545
+ let locked = false;
546
+ let currentNode = null;
547
+ let currentNodePos = -1;
548
+ let currentNodeRelPos;
549
+ let rafId = null;
550
+ let pendingMouseCoords = null;
551
+ function hideHandle() {
552
+ if (!element) {
553
+ return;
554
+ }
555
+ element.style.visibility = "hidden";
556
+ element.style.pointerEvents = "none";
557
+ }
558
+ function showHandle() {
559
+ if (!element) {
560
+ return;
561
+ }
562
+ if (!editor.isEditable) {
563
+ hideHandle();
564
+ return;
565
+ }
566
+ element.style.visibility = "";
567
+ element.style.pointerEvents = "auto";
568
+ }
569
+ function repositionDragHandle(dom) {
570
+ const virtualElement = (getReferencedVirtualElement == null ? void 0 : getReferencedVirtualElement()) || {
571
+ getBoundingClientRect: () => dom.getBoundingClientRect()
572
+ };
573
+ (0, import_dom.computePosition)(virtualElement, element, computePositionConfig).then((val) => {
574
+ Object.assign(element.style, {
575
+ position: val.strategy,
576
+ left: `${val.x}px`,
577
+ top: `${val.y}px`
578
+ });
579
+ });
580
+ }
581
+ function onDragStart(e) {
582
+ onElementDragStart == null ? void 0 : onElementDragStart(e);
583
+ dragHandler(e, editor, nestedOptions, { node: currentNode, pos: currentNodePos });
584
+ if (element) {
585
+ element.dataset.dragging = "true";
586
+ }
587
+ setTimeout(() => {
588
+ if (element) {
589
+ element.style.pointerEvents = "none";
590
+ }
591
+ }, 0);
592
+ }
593
+ function onDragEnd(e) {
594
+ onElementDragEnd == null ? void 0 : onElementDragEnd(e);
595
+ hideHandle();
596
+ if (element) {
597
+ element.style.pointerEvents = "auto";
598
+ element.dataset.dragging = "false";
599
+ }
600
+ }
601
+ function onDrop() {
602
+ if ((0, import_core.isFirefox)()) {
603
+ const editorElement = editor.view.dom;
604
+ requestAnimationFrame(() => {
605
+ if (editorElement.isContentEditable) {
606
+ editorElement.contentEditable = "false";
607
+ editorElement.contentEditable = "true";
608
+ }
609
+ });
610
+ }
611
+ }
612
+ wrapper.appendChild(element);
613
+ return {
614
+ unbind() {
615
+ element.removeEventListener("dragstart", onDragStart);
616
+ element.removeEventListener("dragend", onDragEnd);
617
+ document.removeEventListener("drop", onDrop);
618
+ if (rafId) {
619
+ cancelAnimationFrame(rafId);
620
+ rafId = null;
621
+ pendingMouseCoords = null;
622
+ }
623
+ },
624
+ plugin: new import_state2.Plugin({
625
+ key: typeof pluginKey === "string" ? new import_state2.PluginKey(pluginKey) : pluginKey,
626
+ state: {
627
+ init() {
628
+ return { locked: false };
629
+ },
630
+ apply(tr, value, _oldState, state) {
631
+ const isLocked = tr.getMeta("lockDragHandle");
632
+ const hideDragHandle = tr.getMeta("hideDragHandle");
633
+ if (isLocked !== void 0) {
634
+ locked = isLocked;
635
+ }
636
+ if (hideDragHandle) {
637
+ hideHandle();
638
+ locked = false;
639
+ currentNode = null;
640
+ currentNodePos = -1;
641
+ onNodeChange == null ? void 0 : onNodeChange({ editor, node: null, pos: -1 });
642
+ return value;
643
+ }
644
+ if (tr.docChanged && currentNodePos !== -1 && element) {
645
+ if ((0, import_extension_collaboration.isChangeOrigin)(tr)) {
646
+ const newPos = getAbsolutePos(state, currentNodeRelPos);
647
+ if (newPos !== currentNodePos) {
648
+ currentNodePos = newPos;
649
+ }
650
+ } else {
651
+ const newPos = tr.mapping.map(currentNodePos);
652
+ if (newPos !== currentNodePos) {
653
+ currentNodePos = newPos;
654
+ currentNodeRelPos = getRelativePos(state, currentNodePos);
655
+ }
656
+ }
657
+ }
658
+ return value;
659
+ }
660
+ },
661
+ view: (view) => {
662
+ var _a;
663
+ element.draggable = true;
664
+ element.style.pointerEvents = "auto";
665
+ element.dataset.dragging = "false";
666
+ (_a = editor.view.dom.parentElement) == null ? void 0 : _a.appendChild(wrapper);
667
+ wrapper.style.pointerEvents = "none";
668
+ wrapper.style.position = "absolute";
669
+ wrapper.style.top = "0";
670
+ wrapper.style.left = "0";
671
+ element.addEventListener("dragstart", onDragStart);
672
+ element.addEventListener("dragend", onDragEnd);
673
+ document.addEventListener("drop", onDrop);
674
+ return {
675
+ update(_, oldState) {
676
+ if (!element) {
677
+ return;
678
+ }
679
+ if (!editor.isEditable) {
680
+ hideHandle();
681
+ return;
682
+ }
683
+ if (locked) {
684
+ element.draggable = false;
685
+ } else {
686
+ element.draggable = true;
687
+ }
688
+ if (view.state.doc.eq(oldState.doc) || currentNodePos === -1) {
689
+ return;
690
+ }
691
+ let domNode = view.nodeDOM(currentNodePos);
692
+ domNode = getOuterDomNode(view, domNode);
693
+ if (domNode === view.dom) {
694
+ return;
695
+ }
696
+ if ((domNode == null ? void 0 : domNode.nodeType) !== 1) {
697
+ return;
698
+ }
699
+ const domNodePos = view.posAtDOM(domNode, 0);
700
+ const outerNode = getOuterNode(editor.state.doc, domNodePos);
701
+ const outerNodePos = getOuterNodePos(editor.state.doc, domNodePos);
702
+ currentNode = outerNode;
703
+ currentNodePos = outerNodePos;
704
+ currentNodeRelPos = getRelativePos(view.state, currentNodePos);
705
+ onNodeChange == null ? void 0 : onNodeChange({ editor, node: currentNode, pos: currentNodePos });
706
+ repositionDragHandle(domNode);
707
+ },
708
+ // TODO: Kills even on hot reload
709
+ destroy() {
710
+ element.removeEventListener("dragstart", onDragStart);
711
+ element.removeEventListener("dragend", onDragEnd);
712
+ document.removeEventListener("drop", onDrop);
713
+ if (rafId) {
714
+ cancelAnimationFrame(rafId);
715
+ rafId = null;
716
+ pendingMouseCoords = null;
717
+ }
718
+ if (element) {
719
+ removeNode(wrapper);
720
+ }
721
+ }
722
+ };
723
+ },
724
+ props: {
725
+ handleDOMEvents: {
726
+ keydown(view) {
727
+ if (!element || locked) {
728
+ return false;
729
+ }
730
+ if (view.hasFocus()) {
731
+ hideHandle();
732
+ currentNode = null;
733
+ currentNodePos = -1;
734
+ onNodeChange == null ? void 0 : onNodeChange({ editor, node: null, pos: -1 });
735
+ return false;
736
+ }
737
+ return false;
738
+ },
739
+ mouseleave(_view, e) {
740
+ if (locked) {
741
+ return false;
742
+ }
743
+ if (e.target && !wrapper.contains(e.relatedTarget)) {
744
+ hideHandle();
745
+ currentNode = null;
746
+ currentNodePos = -1;
747
+ onNodeChange == null ? void 0 : onNodeChange({ editor, node: null, pos: -1 });
748
+ }
749
+ return false;
750
+ },
751
+ mousemove(view, e) {
752
+ if (!element || locked) {
753
+ return false;
754
+ }
755
+ pendingMouseCoords = { x: e.clientX, y: e.clientY };
756
+ if (rafId) {
757
+ return false;
758
+ }
759
+ rafId = requestAnimationFrame(() => {
760
+ rafId = null;
761
+ if (!pendingMouseCoords) {
762
+ return;
763
+ }
764
+ const { x, y } = pendingMouseCoords;
765
+ pendingMouseCoords = null;
766
+ const nodeData = findElementNextToCoords({
767
+ x,
768
+ y,
769
+ direction: "right",
770
+ editor,
771
+ nestedOptions
772
+ });
773
+ if (!nodeData.resultElement) {
774
+ return;
775
+ }
776
+ let domNode = nodeData.resultElement;
777
+ let targetNode = nodeData.resultNode;
778
+ let targetPos = nodeData.pos;
779
+ if (!(nestedOptions == null ? void 0 : nestedOptions.enabled)) {
780
+ domNode = getOuterDomNode(view, domNode);
781
+ if (domNode === view.dom) {
782
+ return;
783
+ }
784
+ if ((domNode == null ? void 0 : domNode.nodeType) !== 1) {
785
+ return;
786
+ }
787
+ const domNodePos = view.posAtDOM(domNode, 0);
788
+ targetNode = getOuterNode(editor.state.doc, domNodePos);
789
+ targetPos = getOuterNodePos(editor.state.doc, domNodePos);
790
+ }
791
+ if (targetNode !== currentNode) {
792
+ currentNode = targetNode;
793
+ currentNodePos = targetPos != null ? targetPos : -1;
794
+ currentNodeRelPos = getRelativePos(view.state, currentNodePos);
795
+ onNodeChange == null ? void 0 : onNodeChange({ editor, node: currentNode, pos: currentNodePos });
796
+ repositionDragHandle(domNode);
797
+ showHandle();
798
+ }
799
+ });
800
+ return false;
801
+ }
802
+ }
803
+ }
804
+ })
805
+ };
806
+ };
807
+
808
+ // src/helpers/normalizeOptions.ts
809
+ function normalizeNestedOptions(input) {
810
+ var _a, _b;
811
+ if (input === false || input === void 0) {
812
+ return {
813
+ enabled: false,
814
+ rules: [],
815
+ defaultRules: true,
816
+ allowedContainers: void 0,
817
+ edgeDetection: normalizeEdgeDetection("none")
818
+ };
819
+ }
820
+ if (input === true) {
821
+ return {
822
+ enabled: true,
823
+ rules: [],
824
+ defaultRules: true,
825
+ allowedContainers: void 0,
826
+ edgeDetection: normalizeEdgeDetection("left")
827
+ };
828
+ }
829
+ return {
830
+ enabled: true,
831
+ rules: (_a = input.rules) != null ? _a : [],
832
+ defaultRules: (_b = input.defaultRules) != null ? _b : true,
833
+ allowedContainers: input.allowedContainers,
834
+ edgeDetection: normalizeEdgeDetection(input.edgeDetection)
835
+ };
836
+ }
837
+
838
+ // src/drag-handle.ts
839
+ var defaultComputePositionConfig = {
840
+ placement: "left-start",
841
+ strategy: "absolute"
842
+ };
843
+ var DragHandle = import_core2.Extension.create({
844
+ name: "dragHandle",
845
+ addOptions() {
846
+ return {
847
+ render() {
848
+ const element = document.createElement("div");
849
+ element.classList.add("drag-handle");
850
+ return element;
851
+ },
852
+ computePositionConfig: {},
853
+ locked: false,
854
+ onNodeChange: () => {
855
+ return null;
856
+ },
857
+ onElementDragStart: void 0,
858
+ onElementDragEnd: void 0,
859
+ nested: false
860
+ };
861
+ },
862
+ addCommands() {
863
+ return {
864
+ lockDragHandle: () => ({ editor }) => {
865
+ this.options.locked = true;
866
+ return editor.commands.setMeta("lockDragHandle", this.options.locked);
867
+ },
868
+ unlockDragHandle: () => ({ editor }) => {
869
+ this.options.locked = false;
870
+ return editor.commands.setMeta("lockDragHandle", this.options.locked);
871
+ },
872
+ toggleDragHandle: () => ({ editor }) => {
873
+ this.options.locked = !this.options.locked;
874
+ return editor.commands.setMeta("lockDragHandle", this.options.locked);
875
+ }
876
+ };
877
+ },
878
+ addProseMirrorPlugins() {
879
+ const element = this.options.render();
880
+ const nestedOptions = normalizeNestedOptions(this.options.nested);
881
+ return [
882
+ DragHandlePlugin({
883
+ computePositionConfig: { ...defaultComputePositionConfig, ...this.options.computePositionConfig },
884
+ getReferencedVirtualElement: this.options.getReferencedVirtualElement,
885
+ element,
886
+ editor: this.editor,
887
+ onNodeChange: this.options.onNodeChange,
888
+ onElementDragStart: this.options.onElementDragStart,
889
+ onElementDragEnd: this.options.onElementDragEnd,
890
+ nestedOptions
891
+ }).plugin
892
+ ];
893
+ }
894
+ });
895
+
896
+ // src/index.ts
897
+ var index_default = DragHandle;
898
+ // Annotate the CommonJS export names for ESM import in node:
899
+ 0 && (module.exports = {
900
+ DragHandle,
901
+ DragHandlePlugin,
902
+ defaultComputePositionConfig,
903
+ defaultRules,
904
+ dragHandlePluginDefaultKey,
905
+ normalizeNestedOptions
906
+ });
907
+ //# sourceMappingURL=index.cjs.map