@mk-drag-and-drop/dom 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +111 -35
  2. package/dist/controller/controller-internals.d.ts +4 -4
  3. package/dist/controller/create-drag-controller.d.ts +11 -8
  4. package/dist/controller/create-drag-controller.js +29 -53
  5. package/dist/draggable/create-draggable-binding.js +3 -3
  6. package/dist/droppable/create-drop-container-binding.js +4 -4
  7. package/dist/droppable/create-drop-container.d.ts +1 -1
  8. package/dist/droppable/create-drop-container.js +3 -3
  9. package/dist/droppable/create-droppable-binding.js +4 -4
  10. package/dist/droppable/create-droppable.d.ts +1 -1
  11. package/dist/droppable/create-droppable.js +3 -3
  12. package/dist/index.d.ts +1 -0
  13. package/dist/input/pointer-activation.d.ts +4 -2
  14. package/dist/input/pointer-activation.js +28 -16
  15. package/dist/integration/index.d.ts +2 -2
  16. package/dist/integration/index.js +1 -1
  17. package/dist/runtime/drag-runtime-scope.d.ts +21 -0
  18. package/dist/runtime/{drag-runtime-handle.js → drag-runtime-scope.js} +8 -9
  19. package/dist/runtime/drag-runtime.d.ts +21 -17
  20. package/dist/runtime/drag-runtime.js +279 -155
  21. package/dist/runtime/lifecycle.d.ts +7 -0
  22. package/dist/runtime/types.d.ts +3 -2
  23. package/dist/sortable/create-sortable-binding.js +4 -4
  24. package/dist/sortable/create-sortable.d.ts +1 -1
  25. package/dist/sortable/create-sortable.js +3 -3
  26. package/dist/sortable/sortable-preview.js +20 -17
  27. package/dist/sortable/sortable-registry.d.ts +15 -6
  28. package/dist/sortable/sortable-registry.js +65 -47
  29. package/package.json +2 -1
  30. package/dist/runtime/drag-runtime-handle.d.ts +0 -22
@@ -16,13 +16,12 @@ export class DragRuntime {
16
16
  session = { status: "idle" };
17
17
  modifiers = [];
18
18
  activeDragModifiers = [];
19
- cleanupWindowListeners = null;
20
- cleanupTextSelectionSuppression = null;
19
+ releaseActiveInputListeners = null;
20
+ restoreTextSelectionSuppression = null;
21
21
  queuedPointerPosition = null;
22
22
  pointerFrameId = null;
23
23
  subscriptions = new Set();
24
- disposeCallbacks = new Set();
25
- bindingCleanupRecords = new Set();
24
+ staleDomBindingRecords = new Set();
26
25
  lifecycleCallbacks = {};
27
26
  lifecycleHelpers = {
28
27
  getDropTargetRect: (dropTargetId) => this.getLifecycleDropTargetRect(dropTargetId),
@@ -37,7 +36,7 @@ export class DragRuntime {
37
36
  updateOverlayHost;
38
37
  targetingAlgorithm;
39
38
  hasDragOverlay;
40
- keepOverlayOnDrop;
39
+ overlayRelease;
41
40
  targetingConstraint;
42
41
  pointerActivation = new PointerActivationController({
43
42
  getConfiguration: () => this.pointerConfiguration,
@@ -96,12 +95,12 @@ export class DragRuntime {
96
95
  this.updateOverlayHost = options.updateOverlayHost ?? noopUpdateOverlayHost;
97
96
  this.targetingAlgorithm = options.targetingAlgorithm ?? pointerToCenter;
98
97
  this.hasDragOverlay = options.hasDragOverlay ?? false;
99
- this.keepOverlayOnDrop = options.keepOverlayOnDrop ?? false;
98
+ this.overlayRelease = options.overlayRelease ?? "auto";
100
99
  this.targetingConstraint = options.targetingConstraint;
101
100
  }
102
101
  configure(input) {
103
102
  this.hasDragOverlay = input.hasDragOverlay;
104
- this.keepOverlayOnDrop = input.keepOverlayOnDrop;
103
+ this.overlayRelease = input.overlayRelease;
105
104
  this.targetingAlgorithm = input.targetingAlgorithm;
106
105
  this.targetingConstraint = input.targetingConstraint;
107
106
  this.lifecycleCallbacks = input.lifecycleCallbacks;
@@ -110,7 +109,7 @@ export class DragRuntime {
110
109
  this.pointerConfiguration = normalizePointerConfiguration(input.pointerConfiguration);
111
110
  }
112
111
  requestDragStart(input) {
113
- this.pruneDisconnectedBindingCleanups();
112
+ this.pruneDisconnectedDomBindings();
114
113
  this.pointerActivation.request(input);
115
114
  }
116
115
  isKeyboardDragEnabled() {
@@ -120,8 +119,8 @@ export class DragRuntime {
120
119
  return this.keyboardDrag.handleSourceKeyDown(input);
121
120
  }
122
121
  requestKeyboardDragStart(input) {
123
- this.pruneDisconnectedBindingCleanups();
124
- this.pointerActivation.cancel();
122
+ this.pruneDisconnectedDomBindings();
123
+ this.cancelPendingActivation();
125
124
  if (this.isDragging ||
126
125
  !this.keyboardDrag.isEnabled() ||
127
126
  !input.element.isConnected) {
@@ -162,7 +161,7 @@ export class DragRuntime {
162
161
  else {
163
162
  pointerPosition.x += distance;
164
163
  }
165
- this.updatePointerNow(pointerPosition);
164
+ this.updatePointerNowOrRelease(pointerPosition);
166
165
  }
167
166
  updatePointer(rawPointerPosition) {
168
167
  if (this.session.status !== "dragging") {
@@ -177,7 +176,7 @@ export class DragRuntime {
177
176
  const nextPointerPosition = this.queuedPointerPosition;
178
177
  this.queuedPointerPosition = null;
179
178
  if (nextPointerPosition) {
180
- this.updatePointerNow(nextPointerPosition);
179
+ this.updatePointerNowOrRelease(nextPointerPosition);
181
180
  }
182
181
  });
183
182
  }
@@ -222,6 +221,7 @@ export class DragRuntime {
222
221
  draggableId: nextSession.draggableId,
223
222
  source: nextSession.source,
224
223
  pointerPosition,
224
+ overlayRect,
225
225
  activeDropTargetId: nextSession.activeDropTargetId,
226
226
  previousDropTargetId,
227
227
  };
@@ -230,20 +230,38 @@ export class DragRuntime {
230
230
  placementPosition: sortablePlacementPosition,
231
231
  });
232
232
  }
233
+ updatePointerNowOrRelease(rawPointerPosition) {
234
+ try {
235
+ this.updatePointerNow(rawPointerPosition);
236
+ }
237
+ catch (error) {
238
+ this.releaseActiveDragResourcesAndRethrow(error);
239
+ }
240
+ }
241
+ finishDragAfterQueuedPointerUpdate(input) {
242
+ try {
243
+ this.flushQueuedPointerUpdate();
244
+ }
245
+ catch (error) {
246
+ this.releaseActiveDragResourcesAndRethrow(error);
247
+ }
248
+ this.finishDrag(input);
249
+ }
233
250
  endDrag() {
234
- this.flushQueuedPointerUpdate();
235
- this.finishDrag({
251
+ this.finishDragAfterQueuedPointerUpdate({
236
252
  reason: "drop",
237
- keepReleasedOverlay: this.keepOverlayOnDrop,
253
+ overlayRelease: this.overlayRelease,
238
254
  });
239
255
  }
240
256
  cancelDrag() {
241
- this.flushQueuedPointerUpdate();
242
- this.finishDrag({
257
+ this.finishDragAfterQueuedPointerUpdate({
243
258
  reason: "cancel",
244
- keepReleasedOverlay: false,
259
+ overlayRelease: this.overlayRelease,
245
260
  });
246
261
  }
262
+ cancelPendingActivation() {
263
+ this.pointerActivation.cancelPendingActivation();
264
+ }
247
265
  registerDropTarget(dropTargetId, element, group, options) {
248
266
  const removedTargets = this.dropTargetRegistry.register(dropTargetId, element, group, options);
249
267
  this.clearActiveDropTargetIdIfRemoved(removedTargets);
@@ -263,12 +281,53 @@ export class DragRuntime {
263
281
  const removedTargets = this.dropTargetRegistry.unregister(containerId, element);
264
282
  this.clearActiveDropTargetIdIfRemoved(removedTargets);
265
283
  }
266
- cleanup() {
267
- this.pointerActivation.cancel();
284
+ releaseActiveDragResources() {
285
+ const activeSession = this.getDraggingSession();
286
+ this.cancelPendingActivation();
287
+ let firstReleaseError;
288
+ let hasReleaseError = false;
289
+ if (activeSession) {
290
+ try {
291
+ this.notifyActiveDragReset({
292
+ draggableId: activeSession.draggableId,
293
+ source: activeSession.source,
294
+ });
295
+ }
296
+ catch (error) {
297
+ firstReleaseError = error;
298
+ hasReleaseError = true;
299
+ }
300
+ }
268
301
  this.resetActiveDragState();
269
- this.cleanupActiveDragResources();
270
- this.updateOverlayHost({ type: "unmount" });
271
- this.pruneDisconnectedBindingCleanups();
302
+ try {
303
+ this.releaseActiveInputResources();
304
+ }
305
+ catch (error) {
306
+ firstReleaseError = error;
307
+ hasReleaseError = true;
308
+ }
309
+ try {
310
+ this.clearOverlayHost();
311
+ }
312
+ catch (error) {
313
+ if (!hasReleaseError) {
314
+ firstReleaseError = error;
315
+ hasReleaseError = true;
316
+ }
317
+ }
318
+ if (hasReleaseError) {
319
+ throw firstReleaseError;
320
+ }
321
+ }
322
+ releaseActiveDragResourcesAndRethrow(error) {
323
+ try {
324
+ this.releaseActiveDragResources();
325
+ }
326
+ catch {
327
+ // Preserve the original failure while still making a best effort to
328
+ // release resources that were acquired before it was thrown.
329
+ }
330
+ throw error;
272
331
  }
273
332
  getDropTargetRect(dropTargetId) {
274
333
  return this.dropTargetRegistry.getViewportRect(dropTargetId);
@@ -302,16 +361,16 @@ export class DragRuntime {
302
361
  ...session,
303
362
  overlayMeasurement,
304
363
  });
305
- this.updatePointerNow(session.rawPointerPosition);
364
+ this.updatePointerNowOrRelease(session.rawPointerPosition);
306
365
  }
307
366
  remeasureDropTargets(input) {
308
367
  const pruneGroup = input !== undefined && typeof input !== "string" && !Array.isArray(input)
309
368
  ? input.group
310
369
  : undefined;
311
- this.pruneDisconnectedBindingCleanups();
370
+ this.pruneDisconnectedDomBindings();
312
371
  this.removeDisconnectedDropTargets(pruneGroup);
313
372
  this.dropTargetRegistry.remeasure(input);
314
- this.pruneDisconnectedBindingCleanups();
373
+ this.pruneDisconnectedDomBindings();
315
374
  }
316
375
  subscribe(subscription) {
317
376
  this.subscriptions.add(subscription);
@@ -319,121 +378,110 @@ export class DragRuntime {
319
378
  this.subscriptions.delete(subscription);
320
379
  };
321
380
  }
322
- onDispose(callback) {
323
- this.disposeCallbacks.add(callback);
324
- return () => {
325
- this.disposeCallbacks.delete(callback);
326
- };
327
- }
328
- registerBindingCleanup(record) {
381
+ registerStaleDomBinding(record) {
329
382
  const storedRecord = {
330
383
  record,
331
384
  hasBeenConnected: record.isConnected(),
332
385
  };
333
- // Keep registration O(1). Full cleanup pruning runs on lifecycle/manual
334
- // cleanup paths so bulk renders do not rescan every prior binding.
335
- this.bindingCleanupRecords.add(storedRecord);
386
+ // Keep registration O(1). Full stale DOM pruning runs on drag lifecycle and
387
+ // explicit remeasurement paths so bulk renders do not rescan prior bindings.
388
+ this.staleDomBindingRecords.add(storedRecord);
336
389
  return () => {
337
- if (!this.bindingCleanupRecords.delete(storedRecord)) {
390
+ if (!this.staleDomBindingRecords.delete(storedRecord)) {
338
391
  return;
339
392
  }
340
- record.cleanup();
393
+ record.release();
341
394
  };
342
395
  }
343
- pruneDisconnectedBindingCleanups() {
344
- this.pruneDisconnectedBindingCleanupRecords();
396
+ pruneDisconnectedDomBindings() {
397
+ this.pruneDisconnectedDomBindingRecords();
345
398
  }
346
- getBindingCleanupRecordCount() {
347
- return this.bindingCleanupRecords.size;
348
- }
349
- dispose() {
350
- this.cleanup();
351
- this.disposeBindingCleanupRecords();
352
- for (const disposeCallback of Array.from(this.disposeCallbacks)) {
353
- disposeCallback();
354
- }
355
- this.disposeCallbacks.clear();
356
- this.subscriptions.clear();
357
- this.dropTargetRegistry.clear();
358
- this.lifecycleCallbacks = {};
359
- this.modifiers = [];
360
- this.updateOverlayHost = noopUpdateOverlayHost;
399
+ getStaleDomBindingRecordCount() {
400
+ return this.staleDomBindingRecords.size;
361
401
  }
362
402
  startDragNow(input) {
363
- if (this.targetingAlgorithm.mode === "rect" && !this.hasDragOverlay) {
364
- throw new Error("The selected targeting algorithm requires a drag overlay. Provide dragOverlay or use a pointer-based targeting algorithm.");
365
- }
366
- this.pruneDisconnectedBindingCleanups();
367
- const rawPointerPosition = input.pointerPosition;
368
- const activeDragModifiers = createActiveDragModifiers({
369
- modifiers: this.modifiers,
370
- setupInput: {
403
+ this.cancelPendingActivation();
404
+ try {
405
+ if (this.targetingAlgorithm.mode === "rect" && !this.hasDragOverlay) {
406
+ throw new Error("The selected targeting algorithm requires a drag overlay. Provide dragOverlay or use a pointer-based targeting algorithm.");
407
+ }
408
+ this.pruneDisconnectedDomBindings();
409
+ const rawPointerPosition = input.pointerPosition;
410
+ const activeDragModifiers = createActiveDragModifiers({
411
+ modifiers: this.modifiers,
412
+ setupInput: {
413
+ draggableId: input.draggableId,
414
+ group: input.group,
415
+ sourceRect: input.sourceRect,
416
+ initialPointerPosition: rawPointerPosition,
417
+ },
418
+ });
419
+ this.activeDragModifiers = activeDragModifiers;
420
+ const effectivePointerPosition = applyDragModifiers({
421
+ activeModifiers: activeDragModifiers,
371
422
  draggableId: input.draggableId,
372
423
  group: input.group,
373
424
  sourceRect: input.sourceRect,
374
425
  initialPointerPosition: rawPointerPosition,
375
- },
376
- });
377
- this.activeDragModifiers = activeDragModifiers;
378
- const effectivePointerPosition = applyDragModifiers({
379
- activeModifiers: activeDragModifiers,
380
- draggableId: input.draggableId,
381
- group: input.group,
382
- sourceRect: input.sourceRect,
383
- initialPointerPosition: rawPointerPosition,
384
- rawPointerPosition,
385
- });
386
- const sourceContainerId = this.dropTargetRegistry.getDropTargetRegistration(input.draggableId, input.group)?.containerId ?? null;
387
- const sourceSortablePlacement = this.dropTargetRegistry.getSortableItemPlacement(input.draggableId, input.group);
388
- const nextSession = {
389
- status: "dragging",
390
- source: input.inputType,
391
- draggableId: input.draggableId,
392
- group: input.group,
393
- sourceRect: input.sourceRect,
394
- sourceContainerId,
395
- sourceSortablePlacement,
396
- overlayMeasurement: null,
397
- startPointerPosition: rawPointerPosition,
398
- rawPointerPosition,
399
- pointerPosition: effectivePointerPosition,
400
- activeDropTargetId: null,
401
- };
402
- this.setSession(nextSession);
403
- this.remeasureDropTargets({ group: input.group });
404
- this.updateOverlayHost({
405
- type: "mount",
406
- state: {
407
- dragState: this.createDragState(nextSession),
408
- phase: "dragging",
409
- },
410
- });
411
- this.suppressTextSelection();
412
- if (input.inputType === "pointer") {
413
- this.bindPointerWindowListeners(input.pointerId);
426
+ rawPointerPosition,
427
+ });
428
+ const sourceContainerId = this.dropTargetRegistry.getDropTargetRegistration(input.draggableId, input.group)?.containerId ?? null;
429
+ const sourceSortablePlacement = this.dropTargetRegistry.getSortableItemPlacement(input.draggableId, input.group);
430
+ const nextSession = {
431
+ status: "dragging",
432
+ source: input.inputType,
433
+ draggableId: input.draggableId,
434
+ group: input.group,
435
+ sourceRect: input.sourceRect,
436
+ sourceContainerId,
437
+ sourceSortablePlacement,
438
+ overlayMeasurement: null,
439
+ startPointerPosition: rawPointerPosition,
440
+ rawPointerPosition,
441
+ pointerPosition: effectivePointerPosition,
442
+ activeDropTargetId: null,
443
+ };
444
+ this.setSession(nextSession);
445
+ this.remeasureDropTargets({ group: input.group });
446
+ const dragStartEvent = {
447
+ draggableId: input.draggableId,
448
+ source: input.inputType,
449
+ pointerPosition: effectivePointerPosition,
450
+ sourceRect: input.sourceRect,
451
+ };
452
+ const startOverlayRect = this.hasDragOverlay
453
+ ? this.getCurrentDragRectAt(effectivePointerPosition)
454
+ : null;
455
+ const dragStartSubscriptionEvent = {
456
+ ...dragStartEvent,
457
+ placementPosition: this.getSortablePlacementPosition({
458
+ pointerPosition: effectivePointerPosition,
459
+ overlayRect: startOverlayRect,
460
+ }),
461
+ };
462
+ this.notifyDragStartSubscriptions(dragStartSubscriptionEvent);
463
+ this.updateOverlayHost({
464
+ type: "mount",
465
+ state: {
466
+ dragState: this.createDragState(nextSession),
467
+ phase: "dragging",
468
+ },
469
+ });
470
+ this.suppressTextSelection();
471
+ if (input.inputType === "pointer") {
472
+ this.bindPointerWindowListeners(input.pointerId);
473
+ }
474
+ else {
475
+ this.bindKeyboardWindowListeners();
476
+ }
477
+ this.notifyDragStartLifecycle(dragStartEvent);
414
478
  }
415
- else {
416
- this.bindKeyboardWindowListeners();
479
+ catch (error) {
480
+ this.releaseActiveDragResourcesAndRethrow(error);
417
481
  }
418
- const dragStartEvent = {
419
- draggableId: input.draggableId,
420
- source: input.inputType,
421
- pointerPosition: effectivePointerPosition,
422
- sourceRect: input.sourceRect,
423
- };
424
- const startOverlayRect = this.hasDragOverlay
425
- ? this.getCurrentDragRectAt(effectivePointerPosition)
426
- : null;
427
- this.notifyDragStart(dragStartEvent, {
428
- ...dragStartEvent,
429
- placementPosition: this.getSortablePlacementPosition({
430
- pointerPosition: effectivePointerPosition,
431
- overlayRect: startOverlayRect,
432
- }),
433
- });
434
482
  }
435
483
  finishDrag(input) {
436
- this.pointerActivation.cancel();
484
+ this.cancelPendingActivation();
437
485
  const session = this.getDraggingSession();
438
486
  const releasedDragState = session
439
487
  ? this.createDragState(session)
@@ -452,6 +500,9 @@ export class DragRuntime {
452
500
  ? "dropped"
453
501
  : "invalid-target"
454
502
  : null;
503
+ const overlayRect = session && this.hasDragOverlay
504
+ ? this.getCurrentDragRectAt(session.pointerPosition)
505
+ : null;
455
506
  const sortablePlacement = session && dropTargetId
456
507
  ? this.getDropEventSortablePlacement(session, dropTargetId)
457
508
  : undefined;
@@ -462,7 +513,15 @@ export class DragRuntime {
462
513
  })
463
514
  : null;
464
515
  this.resetActiveDragState();
465
- this.cleanupActiveDragResources();
516
+ let finishError;
517
+ let hasFinishError = false;
518
+ try {
519
+ this.releaseActiveInputResources();
520
+ }
521
+ catch (error) {
522
+ finishError = error;
523
+ hasFinishError = true;
524
+ }
466
525
  if (session && result) {
467
526
  this.lifecycleDropTargetRectSnapshot = lifecycleDropTargetRectSnapshot;
468
527
  try {
@@ -470,6 +529,7 @@ export class DragRuntime {
470
529
  draggableId: session.draggableId,
471
530
  source: session.source,
472
531
  result,
532
+ overlayRect,
473
533
  dropTargetId,
474
534
  });
475
535
  if (result === "dropped" && dropTargetId) {
@@ -481,25 +541,60 @@ export class DragRuntime {
481
541
  });
482
542
  }
483
543
  }
544
+ catch (error) {
545
+ finishError = error;
546
+ hasFinishError = true;
547
+ }
484
548
  finally {
485
549
  this.lifecycleDropTargetRectSnapshot = null;
486
550
  }
487
551
  }
488
- if (input.keepReleasedOverlay && releasedDragState && this.hasDragOverlay) {
489
- this.updateOverlayHost({
490
- type: "release",
491
- state: {
492
- dragState: releasedDragState,
493
- phase: "released",
494
- },
552
+ try {
553
+ this.releaseOverlayAfterDrag({
554
+ overlayRelease: input.overlayRelease,
555
+ releasedDragState,
495
556
  });
496
557
  }
497
- else {
498
- this.updateOverlayHost({ type: "unmount" });
558
+ catch (error) {
559
+ if (!hasFinishError) {
560
+ finishError = error;
561
+ hasFinishError = true;
562
+ }
563
+ }
564
+ if (hasFinishError) {
565
+ throw finishError;
499
566
  }
500
567
  }
568
+ releaseOverlayAfterDrag(input) {
569
+ if (input.overlayRelease === "manual" &&
570
+ input.releasedDragState &&
571
+ this.hasDragOverlay) {
572
+ try {
573
+ this.updateOverlayHost({
574
+ type: "release",
575
+ state: {
576
+ dragState: input.releasedDragState,
577
+ phase: "released",
578
+ },
579
+ });
580
+ }
581
+ catch (error) {
582
+ try {
583
+ this.clearOverlayHost();
584
+ }
585
+ catch {
586
+ // Preserve the overlay release error while still making a best effort
587
+ // to clear the host state.
588
+ }
589
+ throw error;
590
+ }
591
+ return;
592
+ }
593
+ this.clearOverlayHost();
594
+ }
501
595
  bindPointerWindowListeners(pointerId) {
502
- this.cleanupWindowListeners?.();
596
+ this.releaseActiveInputListeners?.();
597
+ let listenersReleased = false;
503
598
  const handlePointerMove = (event) => {
504
599
  if (event.pointerId !== pointerId) {
505
600
  return;
@@ -524,25 +619,29 @@ export class DragRuntime {
524
619
  window.addEventListener("pointermove", handlePointerMove);
525
620
  window.addEventListener("pointerup", handlePointerUp);
526
621
  window.addEventListener("pointercancel", handlePointerCancel);
527
- this.cleanupWindowListeners = () => {
622
+ this.releaseActiveInputListeners = () => {
623
+ if (listenersReleased) {
624
+ return;
625
+ }
626
+ listenersReleased = true;
528
627
  window.removeEventListener("pointermove", handlePointerMove);
529
628
  window.removeEventListener("pointerup", handlePointerUp);
530
629
  window.removeEventListener("pointercancel", handlePointerCancel);
531
630
  };
532
631
  }
533
632
  bindKeyboardWindowListeners() {
534
- this.cleanupWindowListeners?.();
535
- this.cleanupWindowListeners = this.keyboardDrag.bindWindowListeners();
633
+ this.releaseActiveInputListeners?.();
634
+ this.releaseActiveInputListeners = this.keyboardDrag.bindWindowListeners();
536
635
  }
537
636
  suppressTextSelection() {
538
- this.cleanupTextSelectionSuppression?.();
637
+ this.restoreTextSelectionSuppression?.();
539
638
  const root = document.documentElement;
540
639
  const body = document.body;
541
640
  const previousRootUserSelect = root.style.userSelect;
542
641
  const previousBodyUserSelect = body.style.userSelect;
543
642
  root.style.userSelect = "none";
544
643
  body.style.userSelect = "none";
545
- this.cleanupTextSelectionSuppression = () => {
644
+ this.restoreTextSelectionSuppression = () => {
546
645
  root.style.userSelect = previousRootUserSelect;
547
646
  body.style.userSelect = previousBodyUserSelect;
548
647
  };
@@ -552,11 +651,35 @@ export class DragRuntime {
552
651
  this.setSession({ status: "idle" });
553
652
  this.activeDragModifiers = [];
554
653
  }
555
- cleanupActiveDragResources() {
556
- this.cleanupWindowListeners?.();
557
- this.cleanupWindowListeners = null;
558
- this.cleanupTextSelectionSuppression?.();
559
- this.cleanupTextSelectionSuppression = null;
654
+ releaseActiveInputResources() {
655
+ const releaseActiveInputListeners = this.releaseActiveInputListeners;
656
+ this.releaseActiveInputListeners = null;
657
+ const restoreTextSelectionSuppression = this.restoreTextSelectionSuppression;
658
+ this.restoreTextSelectionSuppression = null;
659
+ let releaseError;
660
+ let hasReleaseError = false;
661
+ try {
662
+ releaseActiveInputListeners?.();
663
+ }
664
+ catch (error) {
665
+ releaseError = error;
666
+ hasReleaseError = true;
667
+ }
668
+ try {
669
+ restoreTextSelectionSuppression?.();
670
+ }
671
+ catch (error) {
672
+ if (!hasReleaseError) {
673
+ releaseError = error;
674
+ hasReleaseError = true;
675
+ }
676
+ }
677
+ if (hasReleaseError) {
678
+ throw releaseError;
679
+ }
680
+ }
681
+ clearOverlayHost() {
682
+ this.updateOverlayHost({ type: "unmount" });
560
683
  }
561
684
  flushQueuedPointerUpdate() {
562
685
  if (this.pointerFrameId !== null) {
@@ -697,8 +820,8 @@ export class DragRuntime {
697
820
  const removedTargets = this.dropTargetRegistry.pruneDisconnected(group);
698
821
  this.clearActiveDropTargetIdIfRemoved(removedTargets);
699
822
  }
700
- pruneDisconnectedBindingCleanupRecords(skipRecord) {
701
- for (const storedRecord of Array.from(this.bindingCleanupRecords)) {
823
+ pruneDisconnectedDomBindingRecords(skipRecord) {
824
+ for (const storedRecord of Array.from(this.staleDomBindingRecords)) {
702
825
  if (storedRecord === skipRecord) {
703
826
  continue;
704
827
  }
@@ -709,14 +832,8 @@ export class DragRuntime {
709
832
  if (!storedRecord.hasBeenConnected) {
710
833
  continue;
711
834
  }
712
- this.bindingCleanupRecords.delete(storedRecord);
713
- storedRecord.record.cleanup();
714
- }
715
- }
716
- disposeBindingCleanupRecords() {
717
- for (const storedRecord of Array.from(this.bindingCleanupRecords)) {
718
- this.bindingCleanupRecords.delete(storedRecord);
719
- storedRecord.record.cleanup();
835
+ this.staleDomBindingRecords.delete(storedRecord);
836
+ storedRecord.record.release();
720
837
  }
721
838
  }
722
839
  clearActiveDropTargetIdIfRemoved(removedTargets) {
@@ -734,17 +851,19 @@ export class DragRuntime {
734
851
  activeDropTargetId: null,
735
852
  });
736
853
  }
737
- notifyDragStart(event, subscriptionEvent) {
738
- this.lifecycleCallbacks.onDragStart?.(event, this.lifecycleHelpers);
854
+ notifyDragStartSubscriptions(subscriptionEvent) {
739
855
  for (const subscription of Array.from(this.subscriptions)) {
740
856
  subscription.onDragStart?.(subscriptionEvent);
741
857
  }
742
858
  }
859
+ notifyDragStartLifecycle(event) {
860
+ this.lifecycleCallbacks.onDragStart?.(event, this.lifecycleHelpers);
861
+ }
743
862
  notifyDragUpdate(event, subscriptionEvent) {
744
- this.lifecycleCallbacks.onDragUpdate?.(event, this.lifecycleHelpers);
745
863
  for (const subscription of Array.from(this.subscriptions)) {
746
864
  subscription.onDragUpdate?.(subscriptionEvent);
747
865
  }
866
+ this.lifecycleCallbacks.onDragUpdate?.(event, this.lifecycleHelpers);
748
867
  }
749
868
  notifyDragEnd(event) {
750
869
  for (const subscription of Array.from(this.subscriptions)) {
@@ -758,6 +877,11 @@ export class DragRuntime {
758
877
  }
759
878
  this.lifecycleCallbacks.onDrop?.(event, this.lifecycleHelpers);
760
879
  }
880
+ notifyActiveDragReset(event) {
881
+ for (const subscription of Array.from(this.subscriptions)) {
882
+ subscription.onActiveDragReset?.(event);
883
+ }
884
+ }
761
885
  }
762
886
  function areOverlayMeasurementsEqual(previous, next) {
763
887
  if (previous === next) {
@@ -12,6 +12,7 @@ export type DragUpdateEvent = {
12
12
  draggableId: string;
13
13
  source: DragSource;
14
14
  pointerPosition: DragPoint;
15
+ overlayRect: DragRect | null;
15
16
  activeDropTargetId: string | null;
16
17
  previousDropTargetId: string | null;
17
18
  };
@@ -19,6 +20,7 @@ export type DragEndEvent = {
19
20
  draggableId: string;
20
21
  source: DragSource;
21
22
  result: DragEndResult;
23
+ overlayRect: DragRect | null;
22
24
  dropTargetId: string | null;
23
25
  };
24
26
  export type DropEvent = {
@@ -27,6 +29,10 @@ export type DropEvent = {
27
29
  dropTargetId: string;
28
30
  sortablePlacement?: SortableDropPlacement;
29
31
  };
32
+ export type ActiveDragResetEvent = {
33
+ draggableId: string;
34
+ source: DragSource;
35
+ };
30
36
  export type DragLifecycleHelpers = {
31
37
  getDropTargetRect: (dropTargetId: string) => DragRect | null;
32
38
  };
@@ -41,4 +47,5 @@ export type DragRuntimeSubscription = {
41
47
  onDragUpdate?: (event: DragUpdateEvent) => void;
42
48
  onDragEnd?: (event: DragEndEvent) => void;
43
49
  onDrop?: (event: DropEvent) => void;
50
+ onActiveDragReset?: (event: ActiveDragResetEvent) => void;
44
51
  };