@tiptap/extension-drag-handle 2.24.1 → 3.0.0-beta.10

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.
Files changed (37) hide show
  1. package/LICENSE.md +21 -0
  2. package/dist/index.cjs +463 -457
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +68 -0
  5. package/dist/index.d.ts +68 -5
  6. package/dist/index.js +441 -450
  7. package/dist/index.js.map +1 -1
  8. package/package.json +20 -18
  9. package/src/drag-handle-plugin.ts +233 -236
  10. package/src/drag-handle.ts +42 -28
  11. package/src/helpers/dragHandler.ts +8 -8
  12. package/src/helpers/findNextElementFromCursor.ts +3 -5
  13. package/src/helpers/getInnerCoords.ts +3 -7
  14. package/src/helpers/getOuterNode.ts +1 -1
  15. package/dist/drag-handle-plugin.d.ts +0 -20
  16. package/dist/drag-handle-plugin.d.ts.map +0 -1
  17. package/dist/drag-handle.d.ts +0 -44
  18. package/dist/drag-handle.d.ts.map +0 -1
  19. package/dist/helpers/cloneElement.d.ts +0 -2
  20. package/dist/helpers/cloneElement.d.ts.map +0 -1
  21. package/dist/helpers/dragHandler.d.ts +0 -3
  22. package/dist/helpers/dragHandler.d.ts.map +0 -1
  23. package/dist/helpers/findNextElementFromCursor.d.ts +0 -14
  24. package/dist/helpers/findNextElementFromCursor.d.ts.map +0 -1
  25. package/dist/helpers/getComputedStyle.d.ts +0 -2
  26. package/dist/helpers/getComputedStyle.d.ts.map +0 -1
  27. package/dist/helpers/getInnerCoords.d.ts +0 -6
  28. package/dist/helpers/getInnerCoords.d.ts.map +0 -1
  29. package/dist/helpers/getOuterNode.d.ts +0 -4
  30. package/dist/helpers/getOuterNode.d.ts.map +0 -1
  31. package/dist/helpers/minMax.d.ts +0 -2
  32. package/dist/helpers/minMax.d.ts.map +0 -1
  33. package/dist/helpers/removeNode.d.ts +0 -2
  34. package/dist/helpers/removeNode.d.ts.map +0 -1
  35. package/dist/index.d.ts.map +0 -1
  36. package/dist/index.umd.js +0 -499
  37. package/dist/index.umd.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,493 +1,484 @@
1
- import { Extension } from '@tiptap/core';
2
- import { isChangeOrigin } from '@tiptap/extension-collaboration';
3
- import { PluginKey, Plugin } from '@tiptap/pm/state';
4
- import tippy from 'tippy.js';
5
- import { ySyncPluginKey, absolutePositionToRelativePosition, relativePositionToAbsolutePosition } from 'y-prosemirror';
6
- import { getSelectionRanges, NodeRangeSelection } from '@tiptap/extension-node-range';
1
+ // src/drag-handle.ts
2
+ import { Extension } from "@tiptap/core";
7
3
 
4
+ // src/drag-handle-plugin.ts
5
+ import { computePosition } from "@floating-ui/dom";
6
+ import { isChangeOrigin } from "@tiptap/extension-collaboration";
7
+ import { Plugin, PluginKey } from "@tiptap/pm/state";
8
+ import {
9
+ absolutePositionToRelativePosition,
10
+ relativePositionToAbsolutePosition,
11
+ ySyncPluginKey
12
+ } from "@tiptap/y-tiptap";
13
+
14
+ // src/helpers/dragHandler.ts
15
+ import { getSelectionRanges, NodeRangeSelection } from "@tiptap/extension-node-range";
16
+
17
+ // src/helpers/cloneElement.ts
8
18
  function getCSSText(element) {
9
- let value = '';
10
- const style = getComputedStyle(element);
11
- for (let i = 0; i < style.length; i += 1) {
12
- value += `${style[i]}:${style.getPropertyValue(style[i])};`;
13
- }
14
- return value;
19
+ let value = "";
20
+ const style = getComputedStyle(element);
21
+ for (let i = 0; i < style.length; i += 1) {
22
+ value += `${style[i]}:${style.getPropertyValue(style[i])};`;
23
+ }
24
+ return value;
15
25
  }
16
26
  function cloneElement(node) {
17
- const clonedNode = node.cloneNode(true);
18
- const sourceElements = [node, ...Array.from(node.getElementsByTagName('*'))];
19
- const targetElements = [clonedNode, ...Array.from(clonedNode.getElementsByTagName('*'))];
20
- sourceElements.forEach((sourceElement, index) => {
21
- targetElements[index].style.cssText = getCSSText(sourceElement);
22
- });
23
- return clonedNode;
27
+ const clonedNode = node.cloneNode(true);
28
+ const sourceElements = [node, ...Array.from(node.getElementsByTagName("*"))];
29
+ const targetElements = [clonedNode, ...Array.from(clonedNode.getElementsByTagName("*"))];
30
+ sourceElements.forEach((sourceElement, index) => {
31
+ targetElements[index].style.cssText = getCSSText(sourceElement);
32
+ });
33
+ return clonedNode;
24
34
  }
25
35
 
26
- const findElementNextToCoords = (options) => {
27
- const { x, y, direction, editor, } = options;
28
- let resultElement = null;
29
- let resultNode = null;
30
- let pos = null;
31
- let currentX = x;
32
- while (resultNode === null && currentX < window.innerWidth && currentX > 0) {
33
- const allElements = document.elementsFromPoint(currentX, y);
34
- const prosemirrorIndex = allElements.findIndex(element => element.classList.contains('ProseMirror'));
35
- const filteredElements = allElements.slice(0, prosemirrorIndex);
36
- if (filteredElements.length > 0) {
37
- const target = filteredElements[0];
38
- resultElement = target;
39
- pos = editor.view.posAtDOM(target, 0);
40
- if (pos >= 0) {
41
- resultNode = editor.state.doc.nodeAt(Math.max(pos - 1, 0));
42
- if (resultNode === null || resultNode === void 0 ? void 0 : resultNode.isText) {
43
- resultNode = editor.state.doc.nodeAt(Math.max(pos - 1, 0));
44
- }
45
- if (!resultNode) {
46
- resultNode = editor.state.doc.nodeAt(Math.max(pos, 0));
47
- }
48
- break;
49
- }
36
+ // src/helpers/findNextElementFromCursor.ts
37
+ var findElementNextToCoords = (options) => {
38
+ const { x, y, direction, editor } = options;
39
+ let resultElement = null;
40
+ let resultNode = null;
41
+ let pos = null;
42
+ let currentX = x;
43
+ while (resultNode === null && currentX < window.innerWidth && currentX > 0) {
44
+ const allElements = document.elementsFromPoint(currentX, y);
45
+ const prosemirrorIndex = allElements.findIndex((element) => element.classList.contains("ProseMirror"));
46
+ const filteredElements = allElements.slice(0, prosemirrorIndex);
47
+ if (filteredElements.length > 0) {
48
+ const target = filteredElements[0];
49
+ resultElement = target;
50
+ pos = editor.view.posAtDOM(target, 0);
51
+ if (pos >= 0) {
52
+ resultNode = editor.state.doc.nodeAt(Math.max(pos - 1, 0));
53
+ if (resultNode == null ? void 0 : resultNode.isText) {
54
+ resultNode = editor.state.doc.nodeAt(Math.max(pos - 1, 0));
50
55
  }
51
- if (direction === 'left') {
52
- currentX -= 1;
53
- }
54
- else {
55
- currentX += 1;
56
+ if (!resultNode) {
57
+ resultNode = editor.state.doc.nodeAt(Math.max(pos, 0));
56
58
  }
59
+ break;
60
+ }
61
+ }
62
+ if (direction === "left") {
63
+ currentX -= 1;
64
+ } else {
65
+ currentX += 1;
57
66
  }
58
- return { resultElement, resultNode, pos: pos !== null && pos !== void 0 ? pos : null };
67
+ }
68
+ return { resultElement, resultNode, pos: pos != null ? pos : null };
59
69
  };
60
70
 
61
- function getComputedStyle$1(node, property) {
62
- const style = window.getComputedStyle(node);
63
- return style[property];
71
+ // src/helpers/getComputedStyle.ts
72
+ function getComputedStyle2(node, property) {
73
+ const style = window.getComputedStyle(node);
74
+ return style[property];
64
75
  }
65
76
 
77
+ // src/helpers/minMax.ts
66
78
  function minMax(value = 0, min = 0, max = 0) {
67
- return Math.min(Math.max(value, min), max);
79
+ return Math.min(Math.max(value, min), max);
68
80
  }
69
81
 
82
+ // src/helpers/getInnerCoords.ts
70
83
  function getInnerCoords(view, x, y) {
71
- const paddingLeft = parseInt(getComputedStyle$1(view.dom, 'paddingLeft'), 10);
72
- const paddingRight = parseInt(getComputedStyle$1(view.dom, 'paddingRight'), 10);
73
- const borderLeft = parseInt(getComputedStyle$1(view.dom, 'borderLeftWidth'), 10);
74
- const borderRight = parseInt(getComputedStyle$1(view.dom, 'borderLeftWidth'), 10);
75
- const bounds = view.dom.getBoundingClientRect();
76
- const coords = {
77
- left: minMax(x, bounds.left + paddingLeft + borderLeft, bounds.right - paddingRight - borderRight),
78
- top: y,
79
- };
80
- return coords;
84
+ const paddingLeft = parseInt(getComputedStyle2(view.dom, "paddingLeft"), 10);
85
+ const paddingRight = parseInt(getComputedStyle2(view.dom, "paddingRight"), 10);
86
+ const borderLeft = parseInt(getComputedStyle2(view.dom, "borderLeftWidth"), 10);
87
+ const borderRight = parseInt(getComputedStyle2(view.dom, "borderLeftWidth"), 10);
88
+ const bounds = view.dom.getBoundingClientRect();
89
+ const coords = {
90
+ left: minMax(x, bounds.left + paddingLeft + borderLeft, bounds.right - paddingRight - borderRight),
91
+ top: y
92
+ };
93
+ return coords;
81
94
  }
82
95
 
96
+ // src/helpers/removeNode.ts
83
97
  function removeNode(node) {
84
- var _a;
85
- (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node);
98
+ var _a;
99
+ (_a = node.parentNode) == null ? void 0 : _a.removeChild(node);
86
100
  }
87
101
 
102
+ // src/helpers/dragHandler.ts
88
103
  function getDragHandleRanges(event, editor) {
89
- const { doc } = editor.view.state;
90
- const result = findElementNextToCoords({
91
- editor, x: event.clientX, y: event.clientY, direction: 'right',
92
- });
93
- if (!result.resultNode || result.pos === null) {
94
- return [];
95
- }
96
- const x = event.clientX;
97
- // @ts-ignore
98
- const coords = getInnerCoords(editor.view, x, event.clientY);
99
- const posAtCoords = editor.view.posAtCoords(coords);
100
- if (!posAtCoords) {
101
- return [];
102
- }
103
- const { pos } = posAtCoords;
104
- const nodeAt = doc.resolve(pos).parent;
105
- if (!nodeAt) {
106
- return [];
107
- }
108
- const $from = doc.resolve(result.pos);
109
- const $to = doc.resolve(result.pos + 1);
110
- return getSelectionRanges($from, $to, 0);
104
+ const { doc } = editor.view.state;
105
+ const result = findElementNextToCoords({
106
+ editor,
107
+ x: event.clientX,
108
+ y: event.clientY,
109
+ direction: "right"
110
+ });
111
+ if (!result.resultNode || result.pos === null) {
112
+ return [];
113
+ }
114
+ const x = event.clientX;
115
+ const coords = getInnerCoords(editor.view, x, event.clientY);
116
+ const posAtCoords = editor.view.posAtCoords(coords);
117
+ if (!posAtCoords) {
118
+ return [];
119
+ }
120
+ const { pos } = posAtCoords;
121
+ const nodeAt = doc.resolve(pos).parent;
122
+ if (!nodeAt) {
123
+ return [];
124
+ }
125
+ const $from = doc.resolve(result.pos);
126
+ const $to = doc.resolve(result.pos + 1);
127
+ return getSelectionRanges($from, $to, 0);
111
128
  }
112
129
  function dragHandler(event, editor) {
113
- const { view } = editor;
114
- if (!event.dataTransfer) {
115
- return;
116
- }
117
- const { empty, $from, $to } = view.state.selection;
118
- const dragHandleRanges = getDragHandleRanges(event, editor);
119
- const selectionRanges = getSelectionRanges($from, $to, 0);
120
- const isDragHandleWithinSelection = selectionRanges.some(range => {
121
- return dragHandleRanges.find(dragHandleRange => {
122
- return dragHandleRange.$from === range.$from
123
- && dragHandleRange.$to === range.$to;
124
- });
130
+ const { view } = editor;
131
+ if (!event.dataTransfer) {
132
+ return;
133
+ }
134
+ const { empty, $from, $to } = view.state.selection;
135
+ const dragHandleRanges = getDragHandleRanges(event, editor);
136
+ const selectionRanges = getSelectionRanges($from, $to, 0);
137
+ const isDragHandleWithinSelection = selectionRanges.some((range) => {
138
+ return dragHandleRanges.find((dragHandleRange) => {
139
+ return dragHandleRange.$from === range.$from && dragHandleRange.$to === range.$to;
125
140
  });
126
- const ranges = empty || !isDragHandleWithinSelection
127
- ? dragHandleRanges
128
- : selectionRanges;
129
- if (!ranges.length) {
130
- return;
131
- }
132
- const { tr } = view.state;
133
- const wrapper = document.createElement('div');
134
- const from = ranges[0].$from.pos;
135
- const to = ranges[ranges.length - 1].$to.pos;
136
- const selection = NodeRangeSelection.create(view.state.doc, from, to);
137
- const slice = selection.content();
138
- ranges.forEach(range => {
139
- const element = view.nodeDOM(range.$from.pos);
140
- const clonedElement = cloneElement(element);
141
- wrapper.append(clonedElement);
142
- });
143
- wrapper.style.position = 'absolute';
144
- wrapper.style.top = '-10000px';
145
- document.body.append(wrapper);
146
- event.dataTransfer.clearData();
147
- event.dataTransfer.setDragImage(wrapper, 0, 0);
148
- // tell ProseMirror the dragged content
149
- view.dragging = { slice, move: true };
150
- tr.setSelection(selection);
151
- view.dispatch(tr);
152
- // clean up
153
- document.addEventListener('drop', () => removeNode(wrapper), { once: true });
141
+ });
142
+ const ranges = empty || !isDragHandleWithinSelection ? dragHandleRanges : selectionRanges;
143
+ if (!ranges.length) {
144
+ return;
145
+ }
146
+ const { tr } = view.state;
147
+ const wrapper = document.createElement("div");
148
+ const from = ranges[0].$from.pos;
149
+ const to = ranges[ranges.length - 1].$to.pos;
150
+ const selection = NodeRangeSelection.create(view.state.doc, from, to);
151
+ const slice = selection.content();
152
+ ranges.forEach((range) => {
153
+ const element = view.nodeDOM(range.$from.pos);
154
+ const clonedElement = cloneElement(element);
155
+ wrapper.append(clonedElement);
156
+ });
157
+ wrapper.style.position = "absolute";
158
+ wrapper.style.top = "-10000px";
159
+ document.body.append(wrapper);
160
+ event.dataTransfer.clearData();
161
+ event.dataTransfer.setDragImage(wrapper, 0, 0);
162
+ view.dragging = { slice, move: true };
163
+ tr.setSelection(selection);
164
+ view.dispatch(tr);
165
+ document.addEventListener("drop", () => removeNode(wrapper), { once: true });
154
166
  }
155
167
 
156
- const getOuterNodePos = (doc, pos) => {
157
- const resolvedPos = doc.resolve(pos);
158
- const { depth } = resolvedPos;
159
- if (depth === 0) {
160
- return pos;
161
- }
162
- const a = resolvedPos.pos - resolvedPos.parentOffset;
163
- return a - 1;
168
+ // src/helpers/getOuterNode.ts
169
+ var getOuterNodePos = (doc, pos) => {
170
+ const resolvedPos = doc.resolve(pos);
171
+ const { depth } = resolvedPos;
172
+ if (depth === 0) {
173
+ return pos;
174
+ }
175
+ const a = resolvedPos.pos - resolvedPos.parentOffset;
176
+ return a - 1;
164
177
  };
165
- const getOuterNode = (doc, pos) => {
166
- const node = doc.nodeAt(pos);
167
- const resolvedPos = doc.resolve(pos);
168
- let { depth } = resolvedPos;
169
- let parent = node;
170
- while (depth > 0) {
171
- const currentNode = resolvedPos.node(depth);
172
- depth -= 1;
173
- if (depth === 0) {
174
- parent = currentNode;
175
- }
178
+ var getOuterNode = (doc, pos) => {
179
+ const node = doc.nodeAt(pos);
180
+ const resolvedPos = doc.resolve(pos);
181
+ let { depth } = resolvedPos;
182
+ let parent = node;
183
+ while (depth > 0) {
184
+ const currentNode = resolvedPos.node(depth);
185
+ depth -= 1;
186
+ if (depth === 0) {
187
+ parent = currentNode;
176
188
  }
177
- return parent;
189
+ }
190
+ return parent;
178
191
  };
179
192
 
180
- const getRelativePos = (state, absolutePos) => {
181
- const ystate = ySyncPluginKey.getState(state);
182
- if (!ystate) {
183
- return null;
184
- }
185
- return absolutePositionToRelativePosition(absolutePos, ystate.type, ystate.binding.mapping);
193
+ // src/drag-handle-plugin.ts
194
+ var getRelativePos = (state, absolutePos) => {
195
+ const ystate = ySyncPluginKey.getState(state);
196
+ if (!ystate) {
197
+ return null;
198
+ }
199
+ return absolutePositionToRelativePosition(absolutePos, ystate.type, ystate.binding.mapping);
186
200
  };
187
- const getAbsolutePos = (state, relativePos) => {
188
- const ystate = ySyncPluginKey.getState(state);
189
- if (!ystate) {
190
- return -1;
191
- }
192
- return relativePositionToAbsolutePosition(ystate.doc, ystate.type, relativePos, ystate.binding.mapping) || 0;
201
+ var getAbsolutePos = (state, relativePos) => {
202
+ const ystate = ySyncPluginKey.getState(state);
203
+ if (!ystate) {
204
+ return -1;
205
+ }
206
+ return relativePositionToAbsolutePosition(ystate.doc, ystate.type, relativePos, ystate.binding.mapping) || 0;
193
207
  };
194
- const getOuterDomNode = (view, domNode) => {
195
- let tmpDomNode = domNode;
196
- // Traverse to top level node.
197
- while (tmpDomNode && tmpDomNode.parentNode) {
198
- if (tmpDomNode.parentNode === view.dom) {
199
- break;
200
- }
201
- tmpDomNode = tmpDomNode.parentNode;
208
+ var getOuterDomNode = (view, domNode) => {
209
+ let tmpDomNode = domNode;
210
+ while (tmpDomNode == null ? void 0 : tmpDomNode.parentNode) {
211
+ if (tmpDomNode.parentNode === view.dom) {
212
+ break;
202
213
  }
203
- return tmpDomNode;
214
+ tmpDomNode = tmpDomNode.parentNode;
215
+ }
216
+ return tmpDomNode;
204
217
  };
205
- const dragHandlePluginDefaultKey = new PluginKey('dragHandle');
206
- const DragHandlePlugin = ({ pluginKey = dragHandlePluginDefaultKey, element, editor, tippyOptions, onNodeChange, }) => {
207
- const wrapper = document.createElement('div');
208
- let popup = null;
209
- let locked = false;
210
- let currentNode = null;
211
- let currentNodePos = -1;
212
- let currentNodeRelPos;
213
- element.addEventListener('dragstart', e => {
214
- // Push this to the end of the event cue
215
- // Fixes bug where incorrect drag pos is returned if drag handle has position: absolute
216
- // @ts-ignore
217
- dragHandler(e, editor);
218
- setTimeout(() => {
218
+ var dragHandlePluginDefaultKey = new PluginKey("dragHandle");
219
+ var DragHandlePlugin = ({
220
+ pluginKey = dragHandlePluginDefaultKey,
221
+ element,
222
+ editor,
223
+ computePositionConfig,
224
+ onNodeChange
225
+ }) => {
226
+ const wrapper = document.createElement("div");
227
+ let locked = false;
228
+ let currentNode = null;
229
+ let currentNodePos = -1;
230
+ let currentNodeRelPos;
231
+ function hideHandle() {
232
+ if (!element) {
233
+ return;
234
+ }
235
+ element.style.visibility = "hidden";
236
+ element.style.pointerEvents = "none";
237
+ }
238
+ function showHandle() {
239
+ if (!element) {
240
+ return;
241
+ }
242
+ if (!editor.isEditable) {
243
+ hideHandle();
244
+ return;
245
+ }
246
+ element.style.visibility = "";
247
+ element.style.pointerEvents = "auto";
248
+ }
249
+ function repositionDragHandle(dom) {
250
+ const virtualElement = {
251
+ getBoundingClientRect: () => dom.getBoundingClientRect()
252
+ };
253
+ computePosition(virtualElement, element, computePositionConfig).then((val) => {
254
+ Object.assign(element.style, {
255
+ position: val.strategy,
256
+ left: `${val.x}px`,
257
+ top: `${val.y}px`
258
+ });
259
+ });
260
+ }
261
+ function onDragStart(e) {
262
+ dragHandler(e, editor);
263
+ setTimeout(() => {
264
+ if (element) {
265
+ element.style.pointerEvents = "none";
266
+ }
267
+ }, 0);
268
+ }
269
+ function onDragEnd() {
270
+ hideHandle();
271
+ if (element) {
272
+ element.style.pointerEvents = "auto";
273
+ }
274
+ }
275
+ element.addEventListener("dragstart", onDragStart);
276
+ element.addEventListener("dragend", onDragEnd);
277
+ wrapper.appendChild(element);
278
+ return {
279
+ unbind() {
280
+ element.removeEventListener("dragstart", onDragStart);
281
+ element.removeEventListener("dragend", onDragEnd);
282
+ },
283
+ plugin: new Plugin({
284
+ key: typeof pluginKey === "string" ? new PluginKey(pluginKey) : pluginKey,
285
+ state: {
286
+ init() {
287
+ return { locked: false };
288
+ },
289
+ apply(tr, value, _oldState, state) {
290
+ const isLocked = tr.getMeta("lockDragHandle");
291
+ const hideDragHandle = tr.getMeta("hideDragHandle");
292
+ if (isLocked !== void 0) {
293
+ locked = isLocked;
294
+ }
295
+ if (hideDragHandle) {
296
+ hideHandle();
297
+ locked = false;
298
+ currentNode = null;
299
+ currentNodePos = -1;
300
+ onNodeChange == null ? void 0 : onNodeChange({ editor, node: null, pos: -1 });
301
+ return value;
302
+ }
303
+ if (tr.docChanged && currentNodePos !== -1 && element) {
304
+ if (isChangeOrigin(tr)) {
305
+ const newPos = getAbsolutePos(state, currentNodeRelPos);
306
+ if (newPos !== currentNodePos) {
307
+ currentNodePos = newPos;
308
+ }
309
+ } else {
310
+ const newPos = tr.mapping.map(currentNodePos);
311
+ if (newPos !== currentNodePos) {
312
+ currentNodePos = newPos;
313
+ currentNodeRelPos = getRelativePos(state, currentNodePos);
314
+ }
315
+ }
316
+ }
317
+ return value;
318
+ }
319
+ },
320
+ view: (view) => {
321
+ var _a;
322
+ element.draggable = true;
323
+ element.style.pointerEvents = "auto";
324
+ (_a = editor.view.dom.parentElement) == null ? void 0 : _a.appendChild(wrapper);
325
+ wrapper.style.pointerEvents = "none";
326
+ wrapper.style.position = "absolute";
327
+ wrapper.style.top = "0";
328
+ wrapper.style.left = "0";
329
+ return {
330
+ update(_, oldState) {
331
+ if (!element) {
332
+ return;
333
+ }
334
+ if (!editor.isEditable) {
335
+ hideHandle();
336
+ return;
337
+ }
338
+ if (locked) {
339
+ element.draggable = false;
340
+ } else {
341
+ element.draggable = true;
342
+ }
343
+ if (view.state.doc.eq(oldState.doc) || currentNodePos === -1) {
344
+ return;
345
+ }
346
+ let domNode = view.nodeDOM(currentNodePos);
347
+ domNode = getOuterDomNode(view, domNode);
348
+ if (domNode === view.dom) {
349
+ return;
350
+ }
351
+ if ((domNode == null ? void 0 : domNode.nodeType) !== 1) {
352
+ return;
353
+ }
354
+ const domNodePos = view.posAtDOM(domNode, 0);
355
+ const outerNode = getOuterNode(editor.state.doc, domNodePos);
356
+ const outerNodePos = getOuterNodePos(editor.state.doc, domNodePos);
357
+ currentNode = outerNode;
358
+ currentNodePos = outerNodePos;
359
+ currentNodeRelPos = getRelativePos(view.state, currentNodePos);
360
+ onNodeChange == null ? void 0 : onNodeChange({ editor, node: currentNode, pos: currentNodePos });
361
+ repositionDragHandle(domNode);
362
+ },
363
+ // TODO: Kills even on hot reload
364
+ destroy() {
219
365
  if (element) {
220
- element.style.pointerEvents = 'none';
366
+ removeNode(wrapper);
221
367
  }
222
- }, 0);
223
- });
224
- element.addEventListener('dragend', () => {
225
- if (element) {
226
- element.style.pointerEvents = 'auto';
368
+ }
369
+ };
370
+ },
371
+ props: {
372
+ handleDOMEvents: {
373
+ mouseleave(_view, e) {
374
+ if (locked) {
375
+ return false;
376
+ }
377
+ if (e.target && !wrapper.contains(e.relatedTarget)) {
378
+ hideHandle();
379
+ currentNode = null;
380
+ currentNodePos = -1;
381
+ onNodeChange == null ? void 0 : onNodeChange({ editor, node: null, pos: -1 });
382
+ }
383
+ return false;
384
+ },
385
+ mousemove(view, e) {
386
+ if (!element || locked) {
387
+ return false;
388
+ }
389
+ const nodeData = findElementNextToCoords({
390
+ x: e.clientX,
391
+ y: e.clientY,
392
+ direction: "right",
393
+ editor
394
+ });
395
+ if (!nodeData.resultElement) {
396
+ return false;
397
+ }
398
+ let domNode = nodeData.resultElement;
399
+ domNode = getOuterDomNode(view, domNode);
400
+ if (domNode === view.dom) {
401
+ return false;
402
+ }
403
+ if ((domNode == null ? void 0 : domNode.nodeType) !== 1) {
404
+ return false;
405
+ }
406
+ const domNodePos = view.posAtDOM(domNode, 0);
407
+ const outerNode = getOuterNode(editor.state.doc, domNodePos);
408
+ if (outerNode !== currentNode) {
409
+ const outerNodePos = getOuterNodePos(editor.state.doc, domNodePos);
410
+ currentNode = outerNode;
411
+ currentNodePos = outerNodePos;
412
+ currentNodeRelPos = getRelativePos(view.state, currentNodePos);
413
+ onNodeChange == null ? void 0 : onNodeChange({ editor, node: currentNode, pos: currentNodePos });
414
+ repositionDragHandle(domNode);
415
+ showHandle();
416
+ }
417
+ return false;
418
+ }
227
419
  }
228
- });
229
- return new Plugin({
230
- key: typeof pluginKey === 'string' ? new PluginKey(pluginKey) : pluginKey,
231
- state: {
232
- init() {
233
- return { locked: false };
234
- },
235
- apply(tr, value, oldState, state) {
236
- const isLocked = tr.getMeta('lockDragHandle');
237
- const hideDragHandle = tr.getMeta('hideDragHandle');
238
- if (isLocked !== undefined) {
239
- locked = isLocked;
240
- }
241
- if (hideDragHandle && popup) {
242
- popup.hide();
243
- locked = false;
244
- currentNode = null;
245
- currentNodePos = -1;
246
- onNodeChange === null || onNodeChange === void 0 ? void 0 : onNodeChange({ editor, node: null, pos: -1 });
247
- return value;
248
- }
249
- // Something has changed and drag handler is visible…
250
- if (tr.docChanged && currentNodePos !== -1 && element && popup) {
251
- // Yjs replaces the entire document on every incoming change and needs a special handling.
252
- // If change comes from another user …
253
- if (isChangeOrigin(tr)) {
254
- // https://discuss.yjs.dev/t/y-prosemirror-mapping-a-single-relative-position-when-doc-changes/851/3
255
- const newPos = getAbsolutePos(state, currentNodeRelPos);
256
- if (newPos !== currentNodePos) {
257
- // Set the new position for our current node.
258
- currentNodePos = newPos;
259
- // We will get the outer node with data and position in views update method.
260
- }
261
- }
262
- else {
263
- // … otherwise use ProseMirror mapping to update the position.
264
- const newPos = tr.mapping.map(currentNodePos);
265
- if (newPos !== currentNodePos) {
266
- // TODO: Remove
267
- // console.log('Position has changed …', { old: currentNodePos, new: newPos }, tr);
268
- // Set the new position for our current node.
269
- currentNodePos = newPos;
270
- // Memorize relative position to retrieve absolute position in case of collaboration
271
- currentNodeRelPos = getRelativePos(state, currentNodePos);
272
- // We will get the outer node with data and position in views update method.
273
- }
274
- }
275
- }
276
- return value;
277
- },
278
- },
279
- view: view => {
280
- var _a;
281
- element.draggable = true;
282
- element.style.pointerEvents = 'auto';
283
- (_a = editor.view.dom.parentElement) === null || _a === void 0 ? void 0 : _a.appendChild(wrapper);
284
- wrapper.appendChild(element);
285
- wrapper.style.pointerEvents = 'none';
286
- wrapper.style.position = 'absolute';
287
- wrapper.style.top = '0';
288
- wrapper.style.left = '0';
289
- return {
290
- update(_, oldState) {
291
- if (!element) {
292
- return;
293
- }
294
- if (!editor.isEditable) {
295
- popup === null || popup === void 0 ? void 0 : popup.destroy();
296
- popup = null;
297
- return;
298
- }
299
- if (!popup) {
300
- popup = tippy(view.dom, {
301
- getReferenceClientRect: null,
302
- interactive: true,
303
- trigger: 'manual',
304
- placement: 'left-start',
305
- hideOnClick: false,
306
- duration: 100,
307
- popperOptions: {
308
- modifiers: [
309
- { name: 'flip', enabled: false },
310
- {
311
- name: 'preventOverflow',
312
- options: {
313
- rootBoundary: 'document',
314
- mainAxis: false,
315
- },
316
- },
317
- ],
318
- },
319
- ...tippyOptions,
320
- appendTo: wrapper,
321
- content: element,
322
- });
323
- }
324
- // Prevent element being draggend while being open.
325
- if (locked) {
326
- element.draggable = false;
327
- }
328
- else {
329
- element.draggable = true;
330
- }
331
- // Do not close on updates (e.g. changing padding of a section or collaboration events)
332
- // popup?.hide();
333
- // Recalculate popup position if doc has changend and drag handler is visible.
334
- if (view.state.doc.eq(oldState.doc) || currentNodePos === -1) {
335
- return;
336
- }
337
- // Get domNode from (new) position.
338
- let domNode = view.nodeDOM(currentNodePos);
339
- // Since old element could have been wrapped, we need to find
340
- // the outer node and take its position and node data.
341
- domNode = getOuterDomNode(view, domNode);
342
- // Skip if domNode is editor dom.
343
- if (domNode === view.dom) {
344
- return;
345
- }
346
- // We only want `Element`.
347
- if ((domNode === null || domNode === void 0 ? void 0 : domNode.nodeType) !== 1) {
348
- return;
349
- }
350
- const domNodePos = view.posAtDOM(domNode, 0);
351
- const outerNode = getOuterNode(editor.state.doc, domNodePos);
352
- const outerNodePos = getOuterNodePos(editor.state.doc, domNodePos); // TODO: needed?
353
- currentNode = outerNode;
354
- currentNodePos = outerNodePos;
355
- // Memorize relative position to retrieve absolute position in case of collaboration
356
- currentNodeRelPos = getRelativePos(view.state, currentNodePos);
357
- // TODO: Remove
358
- // console.log('View has updated: callback with new data and repositioning of popup …', {
359
- // domNode,
360
- // currentNodePos,
361
- // currentNode,
362
- // rect: (domNode as Element).getBoundingClientRect(),
363
- // });
364
- onNodeChange === null || onNodeChange === void 0 ? void 0 : onNodeChange({ editor, node: currentNode, pos: currentNodePos });
365
- // Update Tippys getReferenceClientRect since domNode might have changed.
366
- popup.setProps({
367
- getReferenceClientRect: () => domNode.getBoundingClientRect(),
368
- });
369
- },
370
- // TODO: Kills even on hot reload
371
- destroy() {
372
- popup === null || popup === void 0 ? void 0 : popup.destroy();
373
- if (element) {
374
- removeNode(wrapper);
375
- }
376
- },
377
- };
378
- },
379
- props: {
380
- handleDOMEvents: {
381
- mouseleave(_view, e) {
382
- // Do not hide open popup on mouseleave.
383
- if (locked) {
384
- return false;
385
- }
386
- // If e.target is not inside the wrapper, hide.
387
- if (e.target && !wrapper.contains(e.relatedTarget)) {
388
- popup === null || popup === void 0 ? void 0 : popup.hide();
389
- currentNode = null;
390
- currentNodePos = -1;
391
- onNodeChange === null || onNodeChange === void 0 ? void 0 : onNodeChange({ editor, node: null, pos: -1 });
392
- }
393
- return false;
394
- },
395
- mousemove(view, e) {
396
- // Do not continue if popup is not initialized or open.
397
- if (!element || !popup || locked) {
398
- return false;
399
- }
400
- const nodeData = findElementNextToCoords({
401
- x: e.clientX,
402
- y: e.clientY,
403
- direction: 'right',
404
- editor,
405
- });
406
- // Skip if there is no node next to coords
407
- if (!nodeData.resultElement) {
408
- return false;
409
- }
410
- let domNode = nodeData.resultElement;
411
- domNode = getOuterDomNode(view, domNode);
412
- // Skip if domNode is editor dom.
413
- if (domNode === view.dom) {
414
- return false;
415
- }
416
- // We only want `Element`.
417
- if ((domNode === null || domNode === void 0 ? void 0 : domNode.nodeType) !== 1) {
418
- return false;
419
- }
420
- const domNodePos = view.posAtDOM(domNode, 0);
421
- const outerNode = getOuterNode(editor.state.doc, domNodePos);
422
- if (outerNode !== currentNode) {
423
- const outerNodePos = getOuterNodePos(editor.state.doc, domNodePos);
424
- currentNode = outerNode;
425
- currentNodePos = outerNodePos;
426
- // Memorize relative position to retrieve absolute position in case of collaboration
427
- currentNodeRelPos = getRelativePos(view.state, currentNodePos);
428
- // TODO: Remove
429
- // console.log('Mousemove with changed node / node data …', {
430
- // domNode,
431
- // currentNodePos,
432
- // currentNode,
433
- // rect: (domNode as Element).getBoundingClientRect(),
434
- // });
435
- onNodeChange === null || onNodeChange === void 0 ? void 0 : onNodeChange({ editor, node: currentNode, pos: currentNodePos });
436
- // Set nodes clientRect.
437
- popup.setProps({
438
- getReferenceClientRect: () => domNode.getBoundingClientRect(),
439
- });
440
- popup.show();
441
- }
442
- return false;
443
- },
444
- },
445
- },
446
- });
420
+ }
421
+ })
422
+ };
447
423
  };
448
424
 
449
- const DragHandle = Extension.create({
450
- name: 'dragHandle',
451
- addOptions() {
452
- return {
453
- render() {
454
- const element = document.createElement('div');
455
- element.classList.add('drag-handle');
456
- return element;
457
- },
458
- tippyOptions: {},
459
- locked: false,
460
- onNodeChange: () => { return null; },
461
- };
462
- },
463
- addCommands() {
464
- return {
465
- lockDragHandle: () => ({ editor }) => {
466
- this.options.locked = true;
467
- return editor.commands.setMeta('lockDragHandle', this.options.locked);
468
- },
469
- unlockDragHandle: () => ({ editor }) => {
470
- this.options.locked = false;
471
- return editor.commands.setMeta('lockDragHandle', this.options.locked);
472
- },
473
- toggleDragHandle: () => ({ editor }) => {
474
- this.options.locked = !this.options.locked;
475
- return editor.commands.setMeta('lockDragHandle', this.options.locked);
476
- },
477
- };
478
- },
479
- addProseMirrorPlugins() {
480
- const element = this.options.render();
481
- return [
482
- DragHandlePlugin({
483
- tippyOptions: this.options.tippyOptions,
484
- element,
485
- editor: this.editor,
486
- onNodeChange: this.options.onNodeChange,
487
- }),
488
- ];
489
- },
425
+ // src/drag-handle.ts
426
+ var defaultComputePositionConfig = {
427
+ placement: "left-start",
428
+ strategy: "absolute"
429
+ };
430
+ var DragHandle = Extension.create({
431
+ name: "dragHandle",
432
+ addOptions() {
433
+ return {
434
+ render() {
435
+ const element = document.createElement("div");
436
+ element.classList.add("drag-handle");
437
+ return element;
438
+ },
439
+ computePositionConfig: {},
440
+ locked: false,
441
+ onNodeChange: () => {
442
+ return null;
443
+ }
444
+ };
445
+ },
446
+ addCommands() {
447
+ return {
448
+ lockDragHandle: () => ({ editor }) => {
449
+ this.options.locked = true;
450
+ return editor.commands.setMeta("lockDragHandle", this.options.locked);
451
+ },
452
+ unlockDragHandle: () => ({ editor }) => {
453
+ this.options.locked = false;
454
+ return editor.commands.setMeta("lockDragHandle", this.options.locked);
455
+ },
456
+ toggleDragHandle: () => ({ editor }) => {
457
+ this.options.locked = !this.options.locked;
458
+ return editor.commands.setMeta("lockDragHandle", this.options.locked);
459
+ }
460
+ };
461
+ },
462
+ addProseMirrorPlugins() {
463
+ const element = this.options.render();
464
+ return [
465
+ DragHandlePlugin({
466
+ computePositionConfig: { ...defaultComputePositionConfig, ...this.options.computePositionConfig },
467
+ element,
468
+ editor: this.editor,
469
+ onNodeChange: this.options.onNodeChange
470
+ }).plugin
471
+ ];
472
+ }
490
473
  });
491
474
 
492
- export { DragHandle, DragHandlePlugin, DragHandle as default, dragHandlePluginDefaultKey };
493
- //# sourceMappingURL=index.js.map
475
+ // src/index.ts
476
+ var index_default = DragHandle;
477
+ export {
478
+ DragHandle,
479
+ DragHandlePlugin,
480
+ index_default as default,
481
+ defaultComputePositionConfig,
482
+ dragHandlePluginDefaultKey
483
+ };
484
+ //# sourceMappingURL=index.js.map