@marsio/vue-draggable 1.0.8 → 1.0.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.
- package/README.md +144 -2
- package/build/cjs/Draggable.js +278 -70
- package/build/cjs/DraggableCore.js +884 -74
- package/build/cjs/utils/domFns.js +46 -16
- package/build/cjs/utils/noop.js +9 -0
- package/build/cjs/utils/positionFns.js +78 -40
- package/build/cjs/utils/shims.js +2 -2
- package/build/web/vue-draggable.min.js +1 -1
- package/package.json +4 -3
- package/typings/index.d.ts +80 -47
|
@@ -4,8 +4,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.draggableCoreProps = exports.draggableCoreDefaultProps = exports.defaultDraggableEventHandler = exports.default = void 0;
|
|
7
|
-
var _get2 = _interopRequireDefault(require("lodash/get"));
|
|
8
7
|
var _vue = require("vue");
|
|
8
|
+
var _noop = _interopRequireDefault(require("./utils/noop"));
|
|
9
9
|
var _domFns = require("./utils/domFns");
|
|
10
10
|
var _positionFns = require("./utils/positionFns");
|
|
11
11
|
var _log = _interopRequireDefault(require("./utils/log"));
|
|
@@ -45,27 +45,30 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
45
45
|
* Note: This component does not render any DOM elements itself; it merely wraps its default slot's content with draggable functionality.
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
|
-
const funcVoid = function () {};
|
|
49
48
|
// Simple abstraction for dragging events names.
|
|
50
49
|
const eventsFor = {
|
|
51
50
|
touch: {
|
|
52
51
|
start: 'touchstart',
|
|
53
52
|
move: 'touchmove',
|
|
54
|
-
stop: 'touchend'
|
|
53
|
+
stop: 'touchend',
|
|
54
|
+
cancel: 'touchcancel'
|
|
55
55
|
},
|
|
56
56
|
mouse: {
|
|
57
57
|
start: 'mousedown',
|
|
58
58
|
move: 'mousemove',
|
|
59
59
|
stop: 'mouseup'
|
|
60
|
+
},
|
|
61
|
+
pointer: {
|
|
62
|
+
start: 'pointerdown',
|
|
63
|
+
move: 'pointermove',
|
|
64
|
+
stop: 'pointerup',
|
|
65
|
+
cancel: 'pointercancel'
|
|
60
66
|
}
|
|
61
67
|
};
|
|
62
68
|
|
|
63
69
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
64
70
|
const defaultDraggableEventHandler = (e, data) => true;
|
|
65
|
-
|
|
66
|
-
// Default to mouse events.
|
|
67
71
|
exports.defaultDraggableEventHandler = defaultDraggableEventHandler;
|
|
68
|
-
let dragEventFor = eventsFor.mouse;
|
|
69
72
|
const draggableCoreDefaultProps = exports.draggableCoreDefaultProps = {
|
|
70
73
|
allowAnyClick: {
|
|
71
74
|
type: Boolean,
|
|
@@ -75,21 +78,77 @@ const draggableCoreDefaultProps = exports.draggableCoreDefaultProps = {
|
|
|
75
78
|
type: Boolean,
|
|
76
79
|
default: false
|
|
77
80
|
},
|
|
81
|
+
allowMobileScroll: {
|
|
82
|
+
type: Boolean,
|
|
83
|
+
default: false
|
|
84
|
+
},
|
|
85
|
+
autoScroll: {
|
|
86
|
+
type: Boolean,
|
|
87
|
+
default: false
|
|
88
|
+
},
|
|
89
|
+
autoScrollThreshold: {
|
|
90
|
+
type: Number,
|
|
91
|
+
default: 30
|
|
92
|
+
},
|
|
93
|
+
autoScrollMaxSpeed: {
|
|
94
|
+
type: Number,
|
|
95
|
+
default: 20
|
|
96
|
+
},
|
|
97
|
+
autoScrollAxis: {
|
|
98
|
+
type: String,
|
|
99
|
+
default: 'both'
|
|
100
|
+
},
|
|
101
|
+
autoScrollIncludeWindow: {
|
|
102
|
+
type: Boolean,
|
|
103
|
+
default: true
|
|
104
|
+
},
|
|
105
|
+
autoScrollContainer: {
|
|
106
|
+
type: [Object, String, Array],
|
|
107
|
+
default: null
|
|
108
|
+
},
|
|
109
|
+
cancelInteractiveElements: {
|
|
110
|
+
type: Boolean,
|
|
111
|
+
default: false
|
|
112
|
+
},
|
|
113
|
+
enableClickSuppression: {
|
|
114
|
+
type: Boolean,
|
|
115
|
+
default: false
|
|
116
|
+
},
|
|
117
|
+
clickSuppressionDuration: {
|
|
118
|
+
type: Number,
|
|
119
|
+
default: 250
|
|
120
|
+
},
|
|
121
|
+
dragStartThreshold: {
|
|
122
|
+
type: Number,
|
|
123
|
+
default: 0
|
|
124
|
+
},
|
|
125
|
+
dragStartDelay: {
|
|
126
|
+
type: Number,
|
|
127
|
+
default: 0
|
|
128
|
+
},
|
|
129
|
+
dragStartDelayTolerance: {
|
|
130
|
+
type: Number,
|
|
131
|
+
default: 5
|
|
132
|
+
},
|
|
78
133
|
enableUserSelectHack: {
|
|
79
134
|
type: Boolean,
|
|
80
135
|
default: true
|
|
81
136
|
},
|
|
137
|
+
useRafDrag: {
|
|
138
|
+
type: Boolean,
|
|
139
|
+
default: false
|
|
140
|
+
},
|
|
82
141
|
startFn: {
|
|
83
142
|
type: Function,
|
|
84
|
-
default:
|
|
143
|
+
default: _noop.default
|
|
85
144
|
},
|
|
86
145
|
dragFn: {
|
|
87
146
|
type: Function,
|
|
88
|
-
default:
|
|
147
|
+
default: _noop.default
|
|
89
148
|
},
|
|
90
149
|
stopFn: {
|
|
91
150
|
type: Function,
|
|
92
|
-
default:
|
|
151
|
+
default: _noop.default
|
|
93
152
|
},
|
|
94
153
|
scale: {
|
|
95
154
|
type: Number,
|
|
@@ -133,20 +192,541 @@ var _default = exports.default = (0, _vue.defineComponent)({
|
|
|
133
192
|
emit
|
|
134
193
|
} = _ref;
|
|
135
194
|
const rootElement = (0, _vue.ref)(null);
|
|
136
|
-
|
|
195
|
+
// Default to mouse events (instance-scoped, avoids cross-instance interference).
|
|
196
|
+
let dragEventFor = eventsFor.mouse;
|
|
197
|
+
let draggingNode = null;
|
|
198
|
+
let dragRafId = null;
|
|
199
|
+
let pendingDragEvent = null;
|
|
200
|
+
let lastProcessedX = NaN;
|
|
201
|
+
let lastProcessedY = NaN;
|
|
202
|
+
let pendingDragStart = false;
|
|
203
|
+
let pendingDragStartMode = null;
|
|
204
|
+
let dragStartX = NaN;
|
|
205
|
+
let dragStartY = NaN;
|
|
206
|
+
let pendingX = NaN;
|
|
207
|
+
let pendingY = NaN;
|
|
208
|
+
let dragStartDelayTimerId = null;
|
|
209
|
+
let dragStartDelayOwnerWindow = null;
|
|
210
|
+
let dragStartDelayPassed = false;
|
|
211
|
+
let dragStartDelayEvent = null;
|
|
212
|
+
let hasDragged = false;
|
|
213
|
+
let suppressClickDoc = null;
|
|
214
|
+
let suppressClickListener = null;
|
|
215
|
+
let suppressClickTimerId = null;
|
|
216
|
+
let autoScrollRafId = null;
|
|
217
|
+
let autoScrollContainers = [];
|
|
218
|
+
let autoScrollLastEvent = null;
|
|
219
|
+
let pointerCaptureTarget = null;
|
|
220
|
+
let ignoreTouchStartUntil = 0;
|
|
221
|
+
const isElementNode = v => {
|
|
222
|
+
return !!v && typeof v === 'object' && 'nodeType' in v && v.nodeType === 1;
|
|
223
|
+
};
|
|
224
|
+
const isRefLike = v => {
|
|
225
|
+
return !!v && typeof v === 'object' && 'value' in v;
|
|
226
|
+
};
|
|
227
|
+
const state = {
|
|
137
228
|
dragging: false,
|
|
138
229
|
// Used while dragging to determine deltas.
|
|
139
230
|
lastX: NaN,
|
|
140
231
|
lastY: NaN,
|
|
141
232
|
touchIdentifier: null,
|
|
233
|
+
pointerIdentifier: null,
|
|
142
234
|
mounted: false
|
|
143
|
-
}
|
|
235
|
+
};
|
|
144
236
|
const findDOMNode = () => {
|
|
145
|
-
|
|
237
|
+
if (draggingNode) return draggingNode;
|
|
238
|
+
const nodeRef = props.nodeRef;
|
|
239
|
+
if (isRefLike(nodeRef)) {
|
|
240
|
+
const v = nodeRef.value;
|
|
241
|
+
if (isElementNode(v)) return v;
|
|
242
|
+
} else if (isElementNode(nodeRef)) {
|
|
243
|
+
return nodeRef;
|
|
244
|
+
}
|
|
245
|
+
return rootElement.value;
|
|
146
246
|
};
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
247
|
+
const getOwnerWindow = () => {
|
|
248
|
+
const ownerWindow = draggingNode?.ownerDocument?.defaultView;
|
|
249
|
+
return ownerWindow || window;
|
|
250
|
+
};
|
|
251
|
+
const getDragStartDelay = () => {
|
|
252
|
+
const delay = typeof props.dragStartDelay === 'number' ? props.dragStartDelay : 0;
|
|
253
|
+
return delay > 0 ? delay : 0;
|
|
254
|
+
};
|
|
255
|
+
const getDragStartThreshold = () => {
|
|
256
|
+
const threshold = typeof props.dragStartThreshold === 'number' ? props.dragStartThreshold : 0;
|
|
257
|
+
if (threshold <= 0) return 0;
|
|
258
|
+
const scale = typeof props.scale === 'number' ? props.scale : 1;
|
|
259
|
+
if (!scale) return threshold;
|
|
260
|
+
return threshold / scale;
|
|
261
|
+
};
|
|
262
|
+
const getDragStartDelayTolerance = () => {
|
|
263
|
+
const tolerance = typeof props.dragStartDelayTolerance === 'number' ? props.dragStartDelayTolerance : 0;
|
|
264
|
+
if (tolerance <= 0) return 0;
|
|
265
|
+
const scale = typeof props.scale === 'number' ? props.scale : 1;
|
|
266
|
+
if (!scale) return tolerance;
|
|
267
|
+
return tolerance / scale;
|
|
268
|
+
};
|
|
269
|
+
const meetsDragStartThreshold = (x, y) => {
|
|
270
|
+
const threshold = getDragStartThreshold();
|
|
271
|
+
if (threshold <= 0) return true;
|
|
272
|
+
if (!Number.isFinite(dragStartX) || !Number.isFinite(dragStartY)) return true;
|
|
273
|
+
const dx = x - dragStartX;
|
|
274
|
+
const dy = y - dragStartY;
|
|
275
|
+
return dx * dx + dy * dy >= threshold * threshold;
|
|
276
|
+
};
|
|
277
|
+
const exceedsDragStartDelayTolerance = (x, y) => {
|
|
278
|
+
const tolerance = getDragStartDelayTolerance();
|
|
279
|
+
if (tolerance <= 0) return false;
|
|
280
|
+
if (!Number.isFinite(dragStartX) || !Number.isFinite(dragStartY)) return false;
|
|
281
|
+
const dx = x - dragStartX;
|
|
282
|
+
const dy = y - dragStartY;
|
|
283
|
+
return dx * dx + dy * dy > tolerance * tolerance;
|
|
284
|
+
};
|
|
285
|
+
const clearDragStartDelayTimer = () => {
|
|
286
|
+
if (dragStartDelayTimerId == null) return;
|
|
287
|
+
const ownerWindow = dragStartDelayOwnerWindow || window;
|
|
288
|
+
ownerWindow.clearTimeout?.(dragStartDelayTimerId);
|
|
289
|
+
dragStartDelayTimerId = null;
|
|
290
|
+
dragStartDelayOwnerWindow = null;
|
|
291
|
+
};
|
|
292
|
+
const clearClickSuppression = () => {
|
|
293
|
+
if (suppressClickTimerId != null) {
|
|
294
|
+
const ownerWindow = suppressClickDoc?.defaultView || window;
|
|
295
|
+
ownerWindow.clearTimeout?.(suppressClickTimerId);
|
|
296
|
+
suppressClickTimerId = null;
|
|
297
|
+
}
|
|
298
|
+
if (suppressClickDoc && suppressClickListener) {
|
|
299
|
+
suppressClickDoc.removeEventListener('click', suppressClickListener, true);
|
|
300
|
+
}
|
|
301
|
+
suppressClickDoc = null;
|
|
302
|
+
suppressClickListener = null;
|
|
303
|
+
};
|
|
304
|
+
const installClickSuppression = doc => {
|
|
305
|
+
if (!props.enableClickSuppression) return;
|
|
306
|
+
const duration = typeof props.clickSuppressionDuration === 'number' ? props.clickSuppressionDuration : 0;
|
|
307
|
+
if (duration < 0) return;
|
|
308
|
+
clearClickSuppression();
|
|
309
|
+
const ownerWindow = doc.defaultView || window;
|
|
310
|
+
const suppressUntil = Date.now() + duration;
|
|
311
|
+
const handler = event => {
|
|
312
|
+
if (Date.now() > suppressUntil) {
|
|
313
|
+
clearClickSuppression();
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
event.preventDefault();
|
|
317
|
+
event.stopPropagation();
|
|
318
|
+
event.stopImmediatePropagation?.();
|
|
319
|
+
clearClickSuppression();
|
|
320
|
+
};
|
|
321
|
+
suppressClickDoc = doc;
|
|
322
|
+
suppressClickListener = handler;
|
|
323
|
+
doc.addEventListener('click', handler, true);
|
|
324
|
+
suppressClickTimerId = ownerWindow.setTimeout(() => {
|
|
325
|
+
clearClickSuppression();
|
|
326
|
+
}, duration);
|
|
327
|
+
};
|
|
328
|
+
const getAutoScrollThreshold = () => {
|
|
329
|
+
const threshold = typeof props.autoScrollThreshold === 'number' ? props.autoScrollThreshold : 0;
|
|
330
|
+
return threshold > 0 ? threshold : 0;
|
|
331
|
+
};
|
|
332
|
+
const getAutoScrollMaxSpeed = () => {
|
|
333
|
+
const maxSpeed = typeof props.autoScrollMaxSpeed === 'number' ? props.autoScrollMaxSpeed : 0;
|
|
334
|
+
return maxSpeed > 0 ? maxSpeed : 0;
|
|
335
|
+
};
|
|
336
|
+
const getAutoScrollAxis = () => {
|
|
337
|
+
const axis = props.autoScrollAxis;
|
|
338
|
+
if (axis === 'x' || axis === 'y' || axis === 'none') return axis;
|
|
339
|
+
return 'both';
|
|
340
|
+
};
|
|
341
|
+
const shouldAutoScrollIncludeWindow = () => {
|
|
342
|
+
return props.autoScrollIncludeWindow !== false;
|
|
343
|
+
};
|
|
344
|
+
const isPointerEvent = e => {
|
|
345
|
+
return !!e && typeof e === 'object' && 'pointerId' in e;
|
|
346
|
+
};
|
|
347
|
+
const shouldUsePointerEventsForTouch = (node, e) => {
|
|
348
|
+
if (e.pointerType !== 'touch') return true;
|
|
349
|
+
const ownerWindow = node.ownerDocument.defaultView || window;
|
|
350
|
+
const style = ownerWindow.getComputedStyle(node);
|
|
351
|
+
const touchAction = style.touchAction || '';
|
|
352
|
+
return touchAction !== '' && touchAction !== 'auto';
|
|
353
|
+
};
|
|
354
|
+
const releasePointerCapture = () => {
|
|
355
|
+
if (!pointerCaptureTarget) return;
|
|
356
|
+
const pointerId = state.pointerIdentifier;
|
|
357
|
+
if (typeof pointerId !== 'number') {
|
|
358
|
+
pointerCaptureTarget = null;
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
try {
|
|
362
|
+
if (pointerCaptureTarget.hasPointerCapture?.(pointerId)) {
|
|
363
|
+
pointerCaptureTarget.releasePointerCapture(pointerId);
|
|
364
|
+
} else {
|
|
365
|
+
pointerCaptureTarget.releasePointerCapture?.(pointerId);
|
|
366
|
+
}
|
|
367
|
+
} catch {
|
|
368
|
+
// ignore
|
|
369
|
+
}
|
|
370
|
+
pointerCaptureTarget = null;
|
|
371
|
+
};
|
|
372
|
+
const trySetPointerCapture = (node, pointerId) => {
|
|
373
|
+
if (!node.setPointerCapture) return;
|
|
374
|
+
try {
|
|
375
|
+
node.setPointerCapture(pointerId);
|
|
376
|
+
pointerCaptureTarget = node;
|
|
377
|
+
} catch {
|
|
378
|
+
// ignore
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
const getEventClientPosition = e => {
|
|
382
|
+
if (typeof state.pointerIdentifier === 'number') {
|
|
383
|
+
const maybePointerEvent = e;
|
|
384
|
+
if (typeof maybePointerEvent.pointerId !== 'number') return null;
|
|
385
|
+
if (maybePointerEvent.pointerId !== state.pointerIdentifier) return null;
|
|
386
|
+
if (typeof maybePointerEvent.clientX === 'number' && typeof maybePointerEvent.clientY === 'number') {
|
|
387
|
+
const result = {
|
|
388
|
+
clientX: maybePointerEvent.clientX,
|
|
389
|
+
clientY: maybePointerEvent.clientY
|
|
390
|
+
};
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
if (typeof state.touchIdentifier === 'number') {
|
|
396
|
+
const touch = (0, _domFns.getTouch)(e, state.touchIdentifier);
|
|
397
|
+
if (touch && typeof touch.clientX === 'number' && typeof touch.clientY === 'number') {
|
|
398
|
+
const result = {
|
|
399
|
+
clientX: touch.clientX,
|
|
400
|
+
clientY: touch.clientY
|
|
401
|
+
};
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
const maybeMouseEvent = e;
|
|
407
|
+
if (typeof maybeMouseEvent.clientX === 'number' && typeof maybeMouseEvent.clientY === 'number') {
|
|
408
|
+
const result = {
|
|
409
|
+
clientX: maybeMouseEvent.clientX,
|
|
410
|
+
clientY: maybeMouseEvent.clientY
|
|
411
|
+
};
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
return null;
|
|
415
|
+
};
|
|
416
|
+
const isScrollableElement = (el, ownerWindow) => {
|
|
417
|
+
const style = ownerWindow.getComputedStyle(el);
|
|
418
|
+
const overflowY = style.overflowY;
|
|
419
|
+
const overflowX = style.overflowX;
|
|
420
|
+
const canScrollY = (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') && el.scrollHeight > el.clientHeight;
|
|
421
|
+
const canScrollX = (overflowX === 'auto' || overflowX === 'scroll' || overflowX === 'overlay') && el.scrollWidth > el.clientWidth;
|
|
422
|
+
return canScrollX || canScrollY;
|
|
423
|
+
};
|
|
424
|
+
const resolveAutoScrollContainerInput = (input, node) => {
|
|
425
|
+
const ownerDocument = node.ownerDocument;
|
|
426
|
+
const ownerWindow = ownerDocument.defaultView || window;
|
|
427
|
+
if (typeof input === 'string') {
|
|
428
|
+
const selector = input.trim();
|
|
429
|
+
if (!selector) return [];
|
|
430
|
+
if (selector === 'window') return [ownerWindow];
|
|
431
|
+
if (selector === 'self') return [node];
|
|
432
|
+
if (selector === 'parent') {
|
|
433
|
+
const parent = node.parentElement;
|
|
434
|
+
return parent ? [parent] : [];
|
|
435
|
+
}
|
|
436
|
+
const el = ownerDocument.querySelector(selector);
|
|
437
|
+
if (el && el instanceof ownerWindow.HTMLElement) return [el];
|
|
438
|
+
return [];
|
|
439
|
+
}
|
|
440
|
+
if (input === ownerWindow) return [ownerWindow];
|
|
441
|
+
if (isElementNode(input)) return [input];
|
|
442
|
+
return [];
|
|
443
|
+
};
|
|
444
|
+
const getAutoScrollContainers = (node, includeWindow) => {
|
|
445
|
+
const ownerDocument = node.ownerDocument;
|
|
446
|
+
const ownerWindow = ownerDocument.defaultView || window;
|
|
447
|
+
const containers = [];
|
|
448
|
+
let el = node.parentElement;
|
|
449
|
+
while (el && el !== ownerDocument.body) {
|
|
450
|
+
if (isScrollableElement(el, ownerWindow)) containers.push(el);
|
|
451
|
+
el = el.parentElement;
|
|
452
|
+
}
|
|
453
|
+
if (includeWindow) containers.push(ownerWindow);
|
|
454
|
+
return containers;
|
|
455
|
+
};
|
|
456
|
+
const getAutoScrollContainersForNode = node => {
|
|
457
|
+
const includeWindow = shouldAutoScrollIncludeWindow();
|
|
458
|
+
const containerProp = props.autoScrollContainer;
|
|
459
|
+
if (containerProp == null) return getAutoScrollContainers(node, includeWindow);
|
|
460
|
+
const inputs = Array.isArray(containerProp) ? containerProp : [containerProp];
|
|
461
|
+
const resolved = [];
|
|
462
|
+
for (const input of inputs) {
|
|
463
|
+
resolved.push(...resolveAutoScrollContainerInput(input, node));
|
|
464
|
+
}
|
|
465
|
+
if (includeWindow) {
|
|
466
|
+
const ownerWindow = node.ownerDocument.defaultView || window;
|
|
467
|
+
resolved.push(ownerWindow);
|
|
468
|
+
}
|
|
469
|
+
const deduped = [];
|
|
470
|
+
const seen = new Set();
|
|
471
|
+
for (const item of resolved) {
|
|
472
|
+
if (seen.has(item)) continue;
|
|
473
|
+
seen.add(item);
|
|
474
|
+
deduped.push(item);
|
|
475
|
+
}
|
|
476
|
+
return deduped;
|
|
477
|
+
};
|
|
478
|
+
const cancelAutoScrollRaf = () => {
|
|
479
|
+
if (autoScrollRafId == null) return;
|
|
480
|
+
const ownerWindow = getOwnerWindow();
|
|
481
|
+
ownerWindow.cancelAnimationFrame?.(autoScrollRafId);
|
|
482
|
+
autoScrollRafId = null;
|
|
483
|
+
};
|
|
484
|
+
const scrollElementBy = (el, dx, dy) => {
|
|
485
|
+
let movedX = 0;
|
|
486
|
+
let movedY = 0;
|
|
487
|
+
if (dx) {
|
|
488
|
+
const prev = el.scrollLeft;
|
|
489
|
+
const max = Math.max(0, el.scrollWidth - el.clientWidth);
|
|
490
|
+
const next = Math.max(0, Math.min(prev + dx, max));
|
|
491
|
+
el.scrollLeft = next;
|
|
492
|
+
movedX = next - prev;
|
|
493
|
+
}
|
|
494
|
+
if (dy) {
|
|
495
|
+
const prev = el.scrollTop;
|
|
496
|
+
const max = Math.max(0, el.scrollHeight - el.clientHeight);
|
|
497
|
+
const next = Math.max(0, Math.min(prev + dy, max));
|
|
498
|
+
el.scrollTop = next;
|
|
499
|
+
movedY = next - prev;
|
|
500
|
+
}
|
|
501
|
+
return {
|
|
502
|
+
dx: movedX,
|
|
503
|
+
dy: movedY
|
|
504
|
+
};
|
|
505
|
+
};
|
|
506
|
+
const scrollWindowBy = (ownerWindow, dx, dy) => {
|
|
507
|
+
const doc = ownerWindow.document;
|
|
508
|
+
const scrollingElement = doc.scrollingElement || doc.documentElement || doc.body;
|
|
509
|
+
if (!scrollingElement) {
|
|
510
|
+
ownerWindow.scrollBy(dx, dy);
|
|
511
|
+
return {
|
|
512
|
+
dx,
|
|
513
|
+
dy
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
return scrollElementBy(scrollingElement, dx, dy);
|
|
517
|
+
};
|
|
518
|
+
const calcAutoScrollSpeed = (distanceToEdge, threshold, maxSpeed) => {
|
|
519
|
+
if (threshold <= 0 || maxSpeed <= 0) return 0;
|
|
520
|
+
if (distanceToEdge >= threshold) return 0;
|
|
521
|
+
const clamped = Math.max(0, Math.min(distanceToEdge, threshold));
|
|
522
|
+
const ratio = (threshold - clamped) / threshold;
|
|
523
|
+
const speed = Math.ceil(ratio * maxSpeed);
|
|
524
|
+
return speed > 0 ? speed : 0;
|
|
525
|
+
};
|
|
526
|
+
const getAutoScrollDeltaForElement = (el, clientX, clientY, threshold, maxSpeed) => {
|
|
527
|
+
const rect = el.getBoundingClientRect();
|
|
528
|
+
const thresholdX = Math.min(threshold, rect.width / 2);
|
|
529
|
+
const thresholdY = Math.min(threshold, rect.height / 2);
|
|
530
|
+
let dx = 0;
|
|
531
|
+
let dy = 0;
|
|
532
|
+
if (thresholdX > 0) {
|
|
533
|
+
const leftDist = Math.abs(clientX - rect.left);
|
|
534
|
+
const rightDist = Math.abs(rect.right - clientX);
|
|
535
|
+
if (leftDist < thresholdX || rightDist < thresholdX) {
|
|
536
|
+
if (leftDist <= rightDist) dx = -calcAutoScrollSpeed(leftDist, thresholdX, maxSpeed);else dx = calcAutoScrollSpeed(rightDist, thresholdX, maxSpeed);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (thresholdY > 0) {
|
|
540
|
+
const topDist = Math.abs(clientY - rect.top);
|
|
541
|
+
const bottomDist = Math.abs(rect.bottom - clientY);
|
|
542
|
+
if (topDist < thresholdY || bottomDist < thresholdY) {
|
|
543
|
+
if (topDist <= bottomDist) dy = -calcAutoScrollSpeed(topDist, thresholdY, maxSpeed);else dy = calcAutoScrollSpeed(bottomDist, thresholdY, maxSpeed);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return {
|
|
547
|
+
dx,
|
|
548
|
+
dy
|
|
549
|
+
};
|
|
550
|
+
};
|
|
551
|
+
const getAutoScrollDeltaForWindow = (ownerWindow, clientX, clientY, threshold, maxSpeed) => {
|
|
552
|
+
const width = ownerWindow.innerWidth || 0;
|
|
553
|
+
const height = ownerWindow.innerHeight || 0;
|
|
554
|
+
const thresholdX = Math.min(threshold, width / 2);
|
|
555
|
+
const thresholdY = Math.min(threshold, height / 2);
|
|
556
|
+
let dx = 0;
|
|
557
|
+
let dy = 0;
|
|
558
|
+
if (thresholdX > 0) {
|
|
559
|
+
const leftDist = Math.abs(clientX);
|
|
560
|
+
const rightDist = Math.abs(width - clientX);
|
|
561
|
+
if (leftDist < thresholdX || rightDist < thresholdX) {
|
|
562
|
+
if (leftDist <= rightDist) dx = -calcAutoScrollSpeed(leftDist, thresholdX, maxSpeed);else dx = calcAutoScrollSpeed(rightDist, thresholdX, maxSpeed);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (thresholdY > 0) {
|
|
566
|
+
const topDist = Math.abs(clientY);
|
|
567
|
+
const bottomDist = Math.abs(height - clientY);
|
|
568
|
+
if (topDist < thresholdY || bottomDist < thresholdY) {
|
|
569
|
+
if (topDist <= bottomDist) dy = -calcAutoScrollSpeed(topDist, thresholdY, maxSpeed);else dy = calcAutoScrollSpeed(bottomDist, thresholdY, maxSpeed);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
return {
|
|
573
|
+
dx,
|
|
574
|
+
dy
|
|
575
|
+
};
|
|
576
|
+
};
|
|
577
|
+
const autoScrollTick = () => {
|
|
578
|
+
autoScrollRafId = null;
|
|
579
|
+
if (!props.autoScroll || !state.dragging) return;
|
|
580
|
+
if (!autoScrollContainers.length) return;
|
|
581
|
+
const threshold = getAutoScrollThreshold();
|
|
582
|
+
const maxSpeed = getAutoScrollMaxSpeed();
|
|
583
|
+
if (!threshold || !maxSpeed) return;
|
|
584
|
+
const lastEvent = autoScrollLastEvent || pendingDragEvent;
|
|
585
|
+
if (!lastEvent) return;
|
|
586
|
+
const pos = getEventClientPosition(lastEvent);
|
|
587
|
+
if (!pos) return;
|
|
588
|
+
const posClientX = pos.clientX;
|
|
589
|
+
const posClientY = pos.clientY;
|
|
590
|
+
const ownerWindow = getOwnerWindow();
|
|
591
|
+
const axis = getAutoScrollAxis();
|
|
592
|
+
const allowX = axis === 'both' || axis === 'x';
|
|
593
|
+
const allowY = axis === 'both' || axis === 'y';
|
|
594
|
+
if (!allowX && !allowY) return;
|
|
595
|
+
let didX = !allowX;
|
|
596
|
+
let didY = !allowY;
|
|
597
|
+
let didScroll = false;
|
|
598
|
+
for (const container of autoScrollContainers) {
|
|
599
|
+
if (didX && didY) break;
|
|
600
|
+
const {
|
|
601
|
+
dx,
|
|
602
|
+
dy
|
|
603
|
+
} = container === ownerWindow ? getAutoScrollDeltaForWindow(ownerWindow, posClientX, posClientY, threshold, maxSpeed) : getAutoScrollDeltaForElement(container, posClientX, posClientY, threshold, maxSpeed);
|
|
604
|
+
const attemptX = didX ? 0 : dx;
|
|
605
|
+
const attemptY = didY ? 0 : dy;
|
|
606
|
+
if (!attemptX && !attemptY) continue;
|
|
607
|
+
const moved = container === ownerWindow ? scrollWindowBy(ownerWindow, attemptX, attemptY) : scrollElementBy(container, attemptX, attemptY);
|
|
608
|
+
if (moved.dx) didX = true;
|
|
609
|
+
if (moved.dy) didY = true;
|
|
610
|
+
if (moved.dx || moved.dy) didScroll = true;
|
|
611
|
+
}
|
|
612
|
+
if (!didScroll) return;
|
|
613
|
+
autoScrollLastEvent = lastEvent;
|
|
614
|
+
if (props.useRafDrag) {
|
|
615
|
+
cancelDragRaf();
|
|
616
|
+
pendingDragEvent = lastEvent;
|
|
617
|
+
flushDragRaf();
|
|
618
|
+
} else {
|
|
619
|
+
handleDrag(lastEvent);
|
|
620
|
+
}
|
|
621
|
+
if (!state.dragging) return;
|
|
622
|
+
ensureAutoScrollRaf();
|
|
623
|
+
};
|
|
624
|
+
const ensureAutoScrollRaf = () => {
|
|
625
|
+
if (!props.autoScroll || !state.dragging) return;
|
|
626
|
+
if (autoScrollRafId != null) return;
|
|
627
|
+
const ownerWindow = getOwnerWindow();
|
|
628
|
+
autoScrollRafId = ownerWindow.requestAnimationFrame(autoScrollTick);
|
|
629
|
+
};
|
|
630
|
+
const cancelPendingDragStart = () => {
|
|
631
|
+
const thisNode = findDOMNode();
|
|
632
|
+
cancelDragRaf();
|
|
633
|
+
clearDragStartDelayTimer();
|
|
634
|
+
cancelAutoScrollRaf();
|
|
635
|
+
state.dragging = false;
|
|
636
|
+
state.lastX = NaN;
|
|
637
|
+
state.lastY = NaN;
|
|
638
|
+
state.touchIdentifier = null;
|
|
639
|
+
releasePointerCapture();
|
|
640
|
+
state.pointerIdentifier = null;
|
|
641
|
+
draggingNode = null;
|
|
642
|
+
pendingDragEvent = null;
|
|
643
|
+
lastProcessedX = NaN;
|
|
644
|
+
lastProcessedY = NaN;
|
|
645
|
+
pendingDragStart = false;
|
|
646
|
+
pendingDragStartMode = null;
|
|
647
|
+
dragStartX = NaN;
|
|
648
|
+
dragStartY = NaN;
|
|
649
|
+
pendingX = NaN;
|
|
650
|
+
pendingY = NaN;
|
|
651
|
+
dragStartDelayPassed = false;
|
|
652
|
+
dragStartDelayEvent = null;
|
|
653
|
+
hasDragged = false;
|
|
654
|
+
autoScrollContainers = [];
|
|
655
|
+
autoScrollLastEvent = null;
|
|
656
|
+
if (thisNode) {
|
|
657
|
+
(0, _domFns.removeEvent)(thisNode.ownerDocument, dragEventFor.move, handleDrag);
|
|
658
|
+
(0, _domFns.removeEvent)(thisNode.ownerDocument, dragEventFor.stop, handleDragStop);
|
|
659
|
+
if (dragEventFor.cancel) {
|
|
660
|
+
(0, _domFns.removeEvent)(thisNode.ownerDocument, dragEventFor.cancel, handleDragStop);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
const startDrag = (e, x, y, options) => {
|
|
665
|
+
const coreEvent = (0, _positionFns.createCoreData)({
|
|
666
|
+
props,
|
|
667
|
+
findDOMNode,
|
|
668
|
+
state: {
|
|
669
|
+
lastX: NaN,
|
|
670
|
+
lastY: NaN
|
|
671
|
+
}
|
|
672
|
+
}, x, y);
|
|
673
|
+
(0, _log.default)('DraggableCore: handleDragStart: %j', coreEvent);
|
|
674
|
+
(0, _log.default)('calling', props.startFn);
|
|
675
|
+
const shouldUpdate = props.startFn !== _noop.default ? props.startFn?.(e, coreEvent) : undefined;
|
|
676
|
+
if (shouldUpdate === false || state.mounted === false) {
|
|
677
|
+
cancelPendingDragStart();
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
const thisNode = findDOMNode();
|
|
681
|
+
if (thisNode && props.enableUserSelectHack) (0, _domFns.addUserSelectStyles)(thisNode.ownerDocument);
|
|
682
|
+
if (options?.resetLastCoords) {
|
|
683
|
+
state.lastX = x;
|
|
684
|
+
state.lastY = y;
|
|
685
|
+
lastProcessedX = x;
|
|
686
|
+
lastProcessedY = y;
|
|
687
|
+
}
|
|
688
|
+
pendingDragStart = false;
|
|
689
|
+
pendingDragStartMode = null;
|
|
690
|
+
dragStartDelayPassed = false;
|
|
691
|
+
dragStartDelayEvent = null;
|
|
692
|
+
state.dragging = true;
|
|
693
|
+
autoScrollLastEvent = e;
|
|
694
|
+
if (props.autoScroll) {
|
|
695
|
+
const node = findDOMNode();
|
|
696
|
+
autoScrollContainers = node ? getAutoScrollContainersForNode(node) : [];
|
|
697
|
+
ensureAutoScrollRaf();
|
|
698
|
+
}
|
|
699
|
+
return true;
|
|
700
|
+
};
|
|
701
|
+
const tryStartDrag = (e, x, y) => {
|
|
702
|
+
if (!pendingDragStart) return state.dragging;
|
|
703
|
+
if (pendingDragStartMode === 'delay') {
|
|
704
|
+
if (!dragStartDelayPassed) return false;
|
|
705
|
+
return startDrag(e, x, y, {
|
|
706
|
+
resetLastCoords: true
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
if (!meetsDragStartThreshold(x, y)) return false;
|
|
710
|
+
return startDrag(e, x, y);
|
|
711
|
+
};
|
|
712
|
+
const cancelDragRaf = () => {
|
|
713
|
+
if (dragRafId == null) return;
|
|
714
|
+
const ownerWindow = getOwnerWindow();
|
|
715
|
+
ownerWindow.cancelAnimationFrame?.(dragRafId);
|
|
716
|
+
dragRafId = null;
|
|
717
|
+
};
|
|
718
|
+
const scheduleDragRaf = cb => {
|
|
719
|
+
if (dragRafId != null) return;
|
|
720
|
+
const ownerWindow = getOwnerWindow();
|
|
721
|
+
dragRafId = ownerWindow.requestAnimationFrame(cb);
|
|
722
|
+
};
|
|
723
|
+
const flushDragRaf = () => {
|
|
724
|
+
dragRafId = null;
|
|
725
|
+
if (!state.dragging && !pendingDragStart) return;
|
|
726
|
+
const lastEvent = pendingDragEvent;
|
|
727
|
+
if (!lastEvent) return;
|
|
728
|
+
autoScrollLastEvent = lastEvent;
|
|
729
|
+
const position = (0, _positionFns.getControlPosition)(lastEvent, {
|
|
150
730
|
props,
|
|
151
731
|
findDOMNode
|
|
152
732
|
}, state.touchIdentifier);
|
|
@@ -155,15 +735,34 @@ var _default = exports.default = (0, _vue.defineComponent)({
|
|
|
155
735
|
x,
|
|
156
736
|
y
|
|
157
737
|
} = position;
|
|
738
|
+
const baseX = Number.isFinite(lastProcessedX) ? lastProcessedX : state.lastX;
|
|
739
|
+
const baseY = Number.isFinite(lastProcessedY) ? lastProcessedY : state.lastY;
|
|
740
|
+
if (!state.dragging) {
|
|
741
|
+
if (pendingDragStartMode === 'delay') {
|
|
742
|
+
pendingX = x;
|
|
743
|
+
pendingY = y;
|
|
744
|
+
if (!dragStartDelayPassed) {
|
|
745
|
+
if (exceedsDragStartDelayTolerance(x, y)) cancelPendingDragStart();
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
const started = tryStartDrag(lastEvent, x, y);
|
|
750
|
+
if (!started) return;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Skip useless drag (no movement).
|
|
754
|
+
if (x === baseX && y === baseY) return;
|
|
158
755
|
|
|
159
756
|
// Snap to grid if prop has been provided
|
|
160
757
|
if (Array.isArray(props.grid)) {
|
|
161
|
-
let deltaX = x -
|
|
162
|
-
deltaY = y -
|
|
758
|
+
let deltaX = x - baseX,
|
|
759
|
+
deltaY = y - baseY;
|
|
163
760
|
[deltaX, deltaY] = (0, _positionFns.snapToGrid)(props.grid, deltaX, deltaY);
|
|
164
761
|
if (!deltaX && !deltaY) return; // skip useless drag
|
|
165
|
-
x =
|
|
762
|
+
x = baseX + deltaX, y = baseY + deltaY;
|
|
166
763
|
}
|
|
764
|
+
lastProcessedX = x;
|
|
765
|
+
lastProcessedY = y;
|
|
167
766
|
const coreEvent = (0, _positionFns.createCoreData)({
|
|
168
767
|
props,
|
|
169
768
|
findDOMNode,
|
|
@@ -172,23 +771,59 @@ var _default = exports.default = (0, _vue.defineComponent)({
|
|
|
172
771
|
(0, _log.default)('DraggableCore: handleDrag: %j', coreEvent);
|
|
173
772
|
|
|
174
773
|
// Call event handler. If it returns explicit false, trigger end.
|
|
175
|
-
const shouldUpdate = props.dragFn?.(
|
|
774
|
+
const shouldUpdate = props.dragFn !== _noop.default ? props.dragFn?.(lastEvent, coreEvent) : undefined;
|
|
176
775
|
if (shouldUpdate === false || state.mounted === false) {
|
|
177
|
-
|
|
178
|
-
handleDragStop(new MouseEvent('mouseup'));
|
|
179
|
-
} catch (err) {
|
|
180
|
-
// Old browsers
|
|
181
|
-
const event = document.createEvent('MouseEvents');
|
|
182
|
-
event.initMouseEvent('mouseup', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
|
183
|
-
handleDragStop(event);
|
|
184
|
-
}
|
|
776
|
+
handleDragStop(lastEvent);
|
|
185
777
|
return;
|
|
186
778
|
}
|
|
187
779
|
state.lastX = x;
|
|
188
780
|
state.lastY = y;
|
|
781
|
+
hasDragged = true;
|
|
782
|
+
ensureAutoScrollRaf();
|
|
189
783
|
};
|
|
190
|
-
const
|
|
191
|
-
if (
|
|
784
|
+
const flushPendingDragRaf = () => {
|
|
785
|
+
if (dragRafId == null) return;
|
|
786
|
+
cancelDragRaf();
|
|
787
|
+
flushDragRaf();
|
|
788
|
+
};
|
|
789
|
+
const handleDrag = e => {
|
|
790
|
+
if (typeof state.pointerIdentifier === 'number') {
|
|
791
|
+
if (!isPointerEvent(e)) return;
|
|
792
|
+
if (e.pointerId !== state.pointerIdentifier) return;
|
|
793
|
+
}
|
|
794
|
+
autoScrollLastEvent = e;
|
|
795
|
+
if (props.useRafDrag) {
|
|
796
|
+
const isTouchLike = dragEventFor === eventsFor.touch || dragEventFor === eventsFor.pointer && isPointerEvent(e) && e.pointerType === 'touch';
|
|
797
|
+
if (isTouchLike) {
|
|
798
|
+
const position = (0, _positionFns.getControlPosition)(e, {
|
|
799
|
+
props,
|
|
800
|
+
findDOMNode
|
|
801
|
+
}, state.touchIdentifier);
|
|
802
|
+
if (position == null) return;
|
|
803
|
+
const {
|
|
804
|
+
x,
|
|
805
|
+
y
|
|
806
|
+
} = position;
|
|
807
|
+
if (!state.dragging) {
|
|
808
|
+
if (pendingDragStartMode === 'delay') {
|
|
809
|
+
pendingX = x;
|
|
810
|
+
pendingY = y;
|
|
811
|
+
if (!dragStartDelayPassed) {
|
|
812
|
+
if (exceedsDragStartDelayTolerance(x, y)) cancelPendingDragStart();
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
const started = tryStartDrag(e, x, y);
|
|
817
|
+
if (!started) return;
|
|
818
|
+
}
|
|
819
|
+
if (!props.allowMobileScroll && e.cancelable !== false) e.preventDefault();
|
|
820
|
+
}
|
|
821
|
+
pendingDragEvent = e;
|
|
822
|
+
scheduleDragRaf(flushDragRaf);
|
|
823
|
+
ensureAutoScrollRaf();
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
// Get the current drag point from the event. This is used as the offset.
|
|
192
827
|
const position = (0, _positionFns.getControlPosition)(e, {
|
|
193
828
|
props,
|
|
194
829
|
findDOMNode
|
|
@@ -198,12 +833,32 @@ var _default = exports.default = (0, _vue.defineComponent)({
|
|
|
198
833
|
x,
|
|
199
834
|
y
|
|
200
835
|
} = position;
|
|
836
|
+
if (!state.dragging) {
|
|
837
|
+
if (pendingDragStartMode === 'delay') {
|
|
838
|
+
pendingX = x;
|
|
839
|
+
pendingY = y;
|
|
840
|
+
if (!dragStartDelayPassed) {
|
|
841
|
+
if (exceedsDragStartDelayTolerance(x, y)) cancelPendingDragStart();
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
const started = tryStartDrag(e, x, y);
|
|
846
|
+
if (!started) return;
|
|
847
|
+
}
|
|
848
|
+
const isTouchLike = dragEventFor === eventsFor.touch || dragEventFor === eventsFor.pointer && isPointerEvent(e) && e.pointerType === 'touch';
|
|
849
|
+
if (isTouchLike && !props.allowMobileScroll && e.cancelable !== false) {
|
|
850
|
+
e.preventDefault();
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// Skip useless drag (no movement).
|
|
854
|
+
if (x === state.lastX && y === state.lastY) return;
|
|
201
855
|
|
|
202
856
|
// Snap to grid if prop has been provided
|
|
203
857
|
if (Array.isArray(props.grid)) {
|
|
204
|
-
let deltaX = x - state.lastX
|
|
205
|
-
|
|
858
|
+
let deltaX = x - state.lastX,
|
|
859
|
+
deltaY = y - state.lastY;
|
|
206
860
|
[deltaX, deltaY] = (0, _positionFns.snapToGrid)(props.grid, deltaX, deltaY);
|
|
861
|
+
if (!deltaX && !deltaY) return; // skip useless drag
|
|
207
862
|
x = state.lastX + deltaX, y = state.lastY + deltaY;
|
|
208
863
|
}
|
|
209
864
|
const coreEvent = (0, _positionFns.createCoreData)({
|
|
@@ -211,26 +866,97 @@ var _default = exports.default = (0, _vue.defineComponent)({
|
|
|
211
866
|
findDOMNode,
|
|
212
867
|
state
|
|
213
868
|
}, x, y);
|
|
869
|
+
(0, _log.default)('DraggableCore: handleDrag: %j', coreEvent);
|
|
214
870
|
|
|
215
|
-
// Call event handler
|
|
216
|
-
const
|
|
217
|
-
if (
|
|
871
|
+
// Call event handler. If it returns explicit false, trigger end.
|
|
872
|
+
const shouldUpdate = props.dragFn !== _noop.default ? props.dragFn?.(e, coreEvent) : undefined;
|
|
873
|
+
if (shouldUpdate === false || state.mounted === false) {
|
|
874
|
+
handleDragStop(e);
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
state.lastX = x;
|
|
878
|
+
state.lastY = y;
|
|
879
|
+
hasDragged = true;
|
|
880
|
+
ensureAutoScrollRaf();
|
|
881
|
+
};
|
|
882
|
+
const handleDragStop = e => {
|
|
883
|
+
if (!state.dragging && !pendingDragStart) return;
|
|
884
|
+
if (typeof state.pointerIdentifier === 'number' && isPointerEvent(e) && e.pointerId !== state.pointerIdentifier) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (props.useRafDrag) {
|
|
888
|
+
flushPendingDragRaf();
|
|
889
|
+
}
|
|
890
|
+
cancelAutoScrollRaf();
|
|
891
|
+
autoScrollContainers = [];
|
|
892
|
+
autoScrollLastEvent = null;
|
|
218
893
|
const thisNode = findDOMNode();
|
|
219
|
-
if (
|
|
220
|
-
|
|
221
|
-
|
|
894
|
+
if (state.dragging) {
|
|
895
|
+
const position = (0, _positionFns.getControlPosition)(e, {
|
|
896
|
+
props,
|
|
897
|
+
findDOMNode
|
|
898
|
+
}, state.touchIdentifier);
|
|
899
|
+
if (position == null) return;
|
|
900
|
+
let {
|
|
901
|
+
x,
|
|
902
|
+
y
|
|
903
|
+
} = position;
|
|
904
|
+
|
|
905
|
+
// Snap to grid if prop has been provided
|
|
906
|
+
if (Array.isArray(props.grid)) {
|
|
907
|
+
let deltaX = x - state.lastX || 0;
|
|
908
|
+
let deltaY = y - state.lastY || 0;
|
|
909
|
+
[deltaX, deltaY] = (0, _positionFns.snapToGrid)(props.grid, deltaX, deltaY);
|
|
910
|
+
x = state.lastX + deltaX, y = state.lastY + deltaY;
|
|
911
|
+
}
|
|
912
|
+
const coreEvent = (0, _positionFns.createCoreData)({
|
|
913
|
+
props,
|
|
914
|
+
findDOMNode,
|
|
915
|
+
state
|
|
916
|
+
}, x, y);
|
|
917
|
+
|
|
918
|
+
// Call event handler
|
|
919
|
+
const shouldContinue = props.stopFn !== _noop.default ? props.stopFn?.(e, coreEvent) : undefined;
|
|
920
|
+
if (shouldContinue === false || state.mounted === false) return false;
|
|
921
|
+
if (thisNode) {
|
|
922
|
+
// Remove user-select hack
|
|
923
|
+
if (props.enableUserSelectHack) (0, _domFns.removeUserSelectStyles)(thisNode.ownerDocument);
|
|
924
|
+
}
|
|
925
|
+
(0, _log.default)('DraggableCore: handleDragStop: %j', coreEvent);
|
|
926
|
+
}
|
|
927
|
+
if (thisNode && state.dragging && hasDragged) {
|
|
928
|
+
installClickSuppression(thisNode.ownerDocument);
|
|
222
929
|
}
|
|
223
|
-
(0, _log.default)('DraggableCore: handleDragStop: %j', coreEvent);
|
|
224
930
|
|
|
225
931
|
// Reset the el.
|
|
226
932
|
state.dragging = false;
|
|
227
933
|
state.lastX = NaN;
|
|
228
934
|
state.lastY = NaN;
|
|
935
|
+
state.touchIdentifier = null;
|
|
936
|
+
releasePointerCapture();
|
|
937
|
+
state.pointerIdentifier = null;
|
|
938
|
+
draggingNode = null;
|
|
939
|
+
pendingDragEvent = null;
|
|
940
|
+
lastProcessedX = NaN;
|
|
941
|
+
lastProcessedY = NaN;
|
|
942
|
+
pendingDragStart = false;
|
|
943
|
+
pendingDragStartMode = null;
|
|
944
|
+
dragStartX = NaN;
|
|
945
|
+
dragStartY = NaN;
|
|
946
|
+
pendingX = NaN;
|
|
947
|
+
pendingY = NaN;
|
|
948
|
+
dragStartDelayPassed = false;
|
|
949
|
+
dragStartDelayEvent = null;
|
|
950
|
+
clearDragStartDelayTimer();
|
|
951
|
+
hasDragged = false;
|
|
229
952
|
if (thisNode) {
|
|
230
953
|
// Remove event handlers
|
|
231
954
|
(0, _log.default)('DraggableCore: Removing handlers');
|
|
232
955
|
(0, _domFns.removeEvent)(thisNode.ownerDocument, dragEventFor.move, handleDrag);
|
|
233
956
|
(0, _domFns.removeEvent)(thisNode.ownerDocument, dragEventFor.stop, handleDragStop);
|
|
957
|
+
if (dragEventFor.cancel) {
|
|
958
|
+
(0, _domFns.removeEvent)(thisNode.ownerDocument, dragEventFor.cancel, handleDragStop);
|
|
959
|
+
}
|
|
234
960
|
}
|
|
235
961
|
};
|
|
236
962
|
const handleDragStart = e => {
|
|
@@ -242,7 +968,7 @@ var _default = exports.default = (0, _vue.defineComponent)({
|
|
|
242
968
|
|
|
243
969
|
// Get nodes. Be sure to grab relative document (could be iframed)
|
|
244
970
|
const thisNode = findDOMNode();
|
|
245
|
-
if (!
|
|
971
|
+
if (!thisNode?.ownerDocument?.body) {
|
|
246
972
|
// throw new Error('<DraggableCore> not mounted on DragStart!');
|
|
247
973
|
}
|
|
248
974
|
const {
|
|
@@ -253,15 +979,14 @@ var _default = exports.default = (0, _vue.defineComponent)({
|
|
|
253
979
|
if (props.disabled || ownerDocument && !(e.target instanceof ownerDocument.defaultView.Node) || props.handle && !(0, _domFns.matchesSelectorAndParentsTo)(e.target, props.handle, thisNode) || props.cancel && (0, _domFns.matchesSelectorAndParentsTo)(e.target, props.cancel, thisNode)) {
|
|
254
980
|
return;
|
|
255
981
|
}
|
|
982
|
+
if (props.cancelInteractiveElements && (0, _domFns.matchesSelectorAndParentsTo)(e.target, 'input,textarea,button,select,option,a,[contenteditable]:not([contenteditable="false"])', thisNode)) {
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
256
985
|
|
|
257
|
-
//
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// Set touch identifier in component state if this is a touch event. This allows us to
|
|
262
|
-
// distinguish between individual touches on multitouch screens by identifying which
|
|
263
|
-
// touchpoint was set to this element.
|
|
264
|
-
const touchIdentifier = (0, _domFns.getTouchIdentifier)(e);
|
|
986
|
+
// Track which pointer/touch is active so multi-touch / multi-pointer doesn't interfere.
|
|
987
|
+
const pointerEvent = isPointerEvent(e) ? e : null;
|
|
988
|
+
state.pointerIdentifier = pointerEvent ? pointerEvent.pointerId : null;
|
|
989
|
+
const touchIdentifier = state.pointerIdentifier == null ? (0, _domFns.getTouchIdentifier)(e) : null;
|
|
265
990
|
state.touchIdentifier = touchIdentifier;
|
|
266
991
|
// Get the current drag point from the event. This is used as the offset.
|
|
267
992
|
const position = (0, _positionFns.getControlPosition)(e, {
|
|
@@ -275,45 +1000,88 @@ var _default = exports.default = (0, _vue.defineComponent)({
|
|
|
275
1000
|
} = position;
|
|
276
1001
|
|
|
277
1002
|
// Create an event object with all the data parents need to make a decision here.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
findDOMNode,
|
|
281
|
-
state
|
|
282
|
-
}, x, y);
|
|
283
|
-
(0, _log.default)('DraggableCore: handleDragStart: %j', coreEvent);
|
|
284
|
-
|
|
285
|
-
// Call event handler. If it returns explicit false, cancel.
|
|
286
|
-
(0, _log.default)('calling', props.startFn);
|
|
287
|
-
const shouldUpdate = props.startFn?.(e, coreEvent);
|
|
288
|
-
if (shouldUpdate === false || state.mounted === false) return;
|
|
289
|
-
|
|
290
|
-
// Add a style to the body to disable user-select. This prevents text from
|
|
291
|
-
// being selected all over the page.
|
|
292
|
-
if (props.enableUserSelectHack) (0, _domFns.addUserSelectStyles)(ownerDocument);
|
|
293
|
-
|
|
294
|
-
// Initiate dragging. Set the current x and y as offsets
|
|
295
|
-
// so we know how much we've moved during the drag. This allows us
|
|
296
|
-
// to drag elements around even if they have been moved, without issue.
|
|
297
|
-
state.dragging = true;
|
|
1003
|
+
draggingNode = thisNode;
|
|
1004
|
+
pendingDragEvent = null;
|
|
298
1005
|
state.lastX = x;
|
|
299
1006
|
state.lastY = y;
|
|
1007
|
+
lastProcessedX = x;
|
|
1008
|
+
lastProcessedY = y;
|
|
1009
|
+
cancelDragRaf();
|
|
1010
|
+
clearDragStartDelayTimer();
|
|
1011
|
+
clearClickSuppression();
|
|
1012
|
+
dragStartDelayPassed = false;
|
|
1013
|
+
dragStartDelayEvent = null;
|
|
1014
|
+
hasDragged = false;
|
|
1015
|
+
dragStartX = x;
|
|
1016
|
+
dragStartY = y;
|
|
1017
|
+
pendingX = x;
|
|
1018
|
+
pendingY = y;
|
|
1019
|
+
const shouldDelay = (e.type === 'touchstart' || pointerEvent?.pointerType === 'touch') && getDragStartDelay() > 0;
|
|
1020
|
+
pendingDragStart = shouldDelay || getDragStartThreshold() > 0;
|
|
1021
|
+
pendingDragStartMode = shouldDelay ? 'delay' : pendingDragStart ? 'threshold' : null;
|
|
1022
|
+
if (pendingDragStartMode === 'delay') {
|
|
1023
|
+
const delay = getDragStartDelay();
|
|
1024
|
+
dragStartDelayEvent = e;
|
|
1025
|
+
const ownerWindow = ownerDocument.defaultView || window;
|
|
1026
|
+
dragStartDelayOwnerWindow = ownerWindow;
|
|
1027
|
+
dragStartDelayTimerId = ownerWindow.setTimeout(() => {
|
|
1028
|
+
dragStartDelayTimerId = null;
|
|
1029
|
+
dragStartDelayOwnerWindow = null;
|
|
1030
|
+
dragStartDelayPassed = true;
|
|
1031
|
+
if (!pendingDragStart || pendingDragStartMode !== 'delay' || state.dragging || state.mounted === false) return;
|
|
1032
|
+
const nextX = Number.isFinite(pendingX) ? pendingX : dragStartX;
|
|
1033
|
+
const nextY = Number.isFinite(pendingY) ? pendingY : dragStartY;
|
|
1034
|
+
startDrag(dragStartDelayEvent || e, nextX, nextY, {
|
|
1035
|
+
resetLastCoords: true
|
|
1036
|
+
});
|
|
1037
|
+
}, delay);
|
|
1038
|
+
} else if (!pendingDragStart) {
|
|
1039
|
+
const started = startDrag(e, x, y);
|
|
1040
|
+
if (started === false) return false;
|
|
1041
|
+
}
|
|
300
1042
|
|
|
301
1043
|
// Add events to the document directly so we catch when the user's mouse/touch moves outside of
|
|
302
1044
|
// this element. We use different events depending on whether or not we have detected that this
|
|
303
1045
|
// is a touch-capable device.
|
|
304
|
-
|
|
305
|
-
|
|
1046
|
+
const isTouchLike = dragEventFor === eventsFor.touch || dragEventFor === eventsFor.pointer && pointerEvent?.pointerType === 'touch';
|
|
1047
|
+
const touchListenerOptions = isTouchLike ? {
|
|
1048
|
+
passive: false
|
|
1049
|
+
} : undefined;
|
|
1050
|
+
(0, _domFns.addEvent)(ownerDocument, dragEventFor.move, handleDrag, touchListenerOptions);
|
|
1051
|
+
(0, _domFns.addEvent)(ownerDocument, dragEventFor.stop, handleDragStop, touchListenerOptions);
|
|
1052
|
+
if (dragEventFor.cancel) {
|
|
1053
|
+
(0, _domFns.addEvent)(ownerDocument, dragEventFor.cancel, handleDragStop, touchListenerOptions);
|
|
1054
|
+
}
|
|
1055
|
+
if (dragEventFor === eventsFor.pointer && pointerEvent) {
|
|
1056
|
+
trySetPointerCapture(thisNode, pointerEvent.pointerId);
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
const onPointerdown = e => {
|
|
1060
|
+
if (!isPointerEvent(e)) return;
|
|
1061
|
+
const thisNode = findDOMNode();
|
|
1062
|
+
if (!thisNode) return;
|
|
1063
|
+
if (!shouldUsePointerEventsForTouch(thisNode, e)) return;
|
|
1064
|
+
if (e.pointerType === 'touch') ignoreTouchStartUntil = Date.now() + 100;
|
|
1065
|
+
dragEventFor = eventsFor.pointer;
|
|
1066
|
+
return handleDragStart(e);
|
|
306
1067
|
};
|
|
307
1068
|
const onMousedown = e => {
|
|
1069
|
+
const ownerWindow = getOwnerWindow();
|
|
1070
|
+
if (typeof ownerWindow.PointerEvent !== 'undefined') return;
|
|
308
1071
|
dragEventFor = eventsFor.mouse; // on touchscreen laptops we could switch back to mouse
|
|
309
|
-
emit('mousedown', e);
|
|
310
1072
|
return handleDragStart(e);
|
|
311
1073
|
};
|
|
312
1074
|
const onMouseup = e => {
|
|
1075
|
+
const ownerWindow = getOwnerWindow();
|
|
1076
|
+
if (typeof ownerWindow.PointerEvent !== 'undefined') return;
|
|
313
1077
|
dragEventFor = eventsFor.mouse;
|
|
314
1078
|
return handleDragStop(e);
|
|
315
1079
|
};
|
|
316
1080
|
const onTouchStart = e => {
|
|
1081
|
+
if (ignoreTouchStartUntil && Date.now() < ignoreTouchStartUntil) {
|
|
1082
|
+
ignoreTouchStartUntil = 0;
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
317
1085
|
// We're on a touch device now, so change the event handlers
|
|
318
1086
|
dragEventFor = eventsFor.touch;
|
|
319
1087
|
return handleDragStart(e);
|
|
@@ -334,6 +1102,16 @@ var _default = exports.default = (0, _vue.defineComponent)({
|
|
|
334
1102
|
});
|
|
335
1103
|
(0, _vue.onUnmounted)(() => {
|
|
336
1104
|
state.mounted = false;
|
|
1105
|
+
draggingNode = null;
|
|
1106
|
+
cancelDragRaf();
|
|
1107
|
+
clearDragStartDelayTimer();
|
|
1108
|
+
clearClickSuppression();
|
|
1109
|
+
cancelAutoScrollRaf();
|
|
1110
|
+
releasePointerCapture();
|
|
1111
|
+
state.pointerIdentifier = null;
|
|
1112
|
+
state.touchIdentifier = null;
|
|
1113
|
+
autoScrollContainers = [];
|
|
1114
|
+
autoScrollLastEvent = null;
|
|
337
1115
|
// Remove any leftover event handlers. Remove both touch and mouse handlers in case
|
|
338
1116
|
// some browser quirk caused a touch event to fire during a mouse move, or vice versa.
|
|
339
1117
|
const thisNode = findDOMNode();
|
|
@@ -343,25 +1121,57 @@ var _default = exports.default = (0, _vue.defineComponent)({
|
|
|
343
1121
|
} = thisNode;
|
|
344
1122
|
(0, _domFns.removeEvent)(ownerDocument, eventsFor.mouse.move, handleDrag);
|
|
345
1123
|
(0, _domFns.removeEvent)(ownerDocument, eventsFor.touch.move, handleDrag);
|
|
1124
|
+
(0, _domFns.removeEvent)(ownerDocument, eventsFor.pointer.move, handleDrag);
|
|
346
1125
|
(0, _domFns.removeEvent)(ownerDocument, eventsFor.mouse.stop, handleDragStop);
|
|
347
1126
|
(0, _domFns.removeEvent)(ownerDocument, eventsFor.touch.stop, handleDragStop);
|
|
1127
|
+
(0, _domFns.removeEvent)(ownerDocument, eventsFor.touch.cancel, handleDragStop);
|
|
1128
|
+
(0, _domFns.removeEvent)(ownerDocument, eventsFor.pointer.stop, handleDragStop);
|
|
1129
|
+
(0, _domFns.removeEvent)(ownerDocument, eventsFor.pointer.cancel, handleDragStop);
|
|
348
1130
|
(0, _domFns.removeEvent)(thisNode, eventsFor.touch.start, onTouchStart, {
|
|
349
1131
|
passive: false
|
|
350
1132
|
});
|
|
351
1133
|
if (props.enableUserSelectHack) (0, _domFns.removeUserSelectStyles)(ownerDocument);
|
|
352
1134
|
}
|
|
353
1135
|
});
|
|
1136
|
+
const getFirstUsableChild = () => {
|
|
1137
|
+
const raw = slots.default ? slots.default() : [];
|
|
1138
|
+
const stack = Array.isArray(raw) ? [...raw] : [raw];
|
|
1139
|
+
while (stack.length) {
|
|
1140
|
+
const item = stack.shift();
|
|
1141
|
+
if (Array.isArray(item)) {
|
|
1142
|
+
for (let i = item.length - 1; i >= 0; i -= 1) stack.unshift(item[i]);
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
if (!(0, _vue.isVNode)(item)) continue;
|
|
1146
|
+
if (item.type === _vue.Comment) continue;
|
|
1147
|
+
if (item.type === _vue.Text) {
|
|
1148
|
+
const txt = typeof item.children === 'string' ? item.children : '';
|
|
1149
|
+
if (!txt || !txt.trim()) continue;
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
if (item.type === _vue.Fragment) {
|
|
1153
|
+
const fragChildren = item.children;
|
|
1154
|
+
if (Array.isArray(fragChildren)) {
|
|
1155
|
+
for (let i = fragChildren.length - 1; i >= 0; i -= 1) stack.unshift(fragChildren[i]);
|
|
1156
|
+
}
|
|
1157
|
+
continue;
|
|
1158
|
+
}
|
|
1159
|
+
return item;
|
|
1160
|
+
}
|
|
1161
|
+
return null;
|
|
1162
|
+
};
|
|
354
1163
|
return () => {
|
|
355
|
-
const child =
|
|
1164
|
+
const child = getFirstUsableChild();
|
|
356
1165
|
if (!child) return null;
|
|
357
|
-
// 判断child.type是否是一个组件
|
|
358
|
-
const isComponent = typeof child.type === 'object' && 'name' in child.type;
|
|
359
1166
|
// const clonedChildren = isVNode(child) ? cloneVNode(child, { onMousedown, onMouseup, onTouchend, ref: props.nodeRef || rootElement }) : child;
|
|
1167
|
+
const safeRef = props.nodeRef;
|
|
1168
|
+
const vnodeRef = isRefLike(safeRef) ? safeRef : rootElement;
|
|
360
1169
|
const clonedChildren = (0, _vue.isVNode)(child) ? (0, _vue.cloneVNode)(child, {
|
|
1170
|
+
onPointerdown,
|
|
361
1171
|
onMousedown,
|
|
362
1172
|
onMouseup,
|
|
363
1173
|
onTouchend,
|
|
364
|
-
ref:
|
|
1174
|
+
ref: vnodeRef
|
|
365
1175
|
}) : child;
|
|
366
1176
|
// const clonedChildren = isVNode(child) ? cloneVNode(child, {}) : child;
|
|
367
1177
|
|