@marsio/vue-draggable 1.0.9 → 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.
@@ -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: () => funcVoid
143
+ default: _noop.default
85
144
  },
86
145
  dragFn: {
87
146
  type: Function,
88
- default: () => funcVoid
147
+ default: _noop.default
89
148
  },
90
149
  stopFn: {
91
150
  type: Function,
92
- default: () => funcVoid
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
- const state = (0, _vue.reactive)({
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
- return props.nodeRef?.value || rootElement.value;
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 handleDrag = e => {
148
- // Get the current drag point from the event. This is used as the offset.
149
- const position = (0, _positionFns.getControlPosition)(e, {
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 - state.lastX,
162
- deltaY = y - state.lastY;
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 = state.lastX + deltaX, y = state.lastY + deltaY;
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?.(e, coreEvent);
774
+ const shouldUpdate = props.dragFn !== _noop.default ? props.dragFn?.(lastEvent, coreEvent) : undefined;
176
775
  if (shouldUpdate === false || state.mounted === false) {
177
- try {
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 handleDragStop = e => {
191
- if (!state.dragging) return;
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 || 0;
205
- let deltaY = y - state.lastY || 0;
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 shouldContinue = props.stopFn?.(e, coreEvent);
217
- if (shouldContinue === false || state.mounted === false) return false;
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 (thisNode) {
220
- // Remove user-select hack
221
- if (props.enableUserSelectHack) (0, _domFns.removeUserSelectStyles)(thisNode.ownerDocument);
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 (!(0, _get2.default)(thisNode, 'ownerDocument.body')) {
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
- // Prevent scrolling on mobile devices, like ipad/iphone.
258
- // Important that this is after handle/cancel.
259
- if (e.type === 'touchstart') e.preventDefault();
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
- const coreEvent = (0, _positionFns.createCoreData)({
279
- props,
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
- (0, _domFns.addEvent)(ownerDocument, dragEventFor.move, handleDrag);
305
- (0, _domFns.addEvent)(ownerDocument, dragEventFor.stop, handleDragStop);
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 = slots.default ? slots.default()[0] : null;
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: props.nodeRef || rootElement
1174
+ ref: vnodeRef
365
1175
  }) : child;
366
1176
  // const clonedChildren = isVNode(child) ? cloneVNode(child, {}) : child;
367
1177