@mk-drag-and-drop/dom 0.1.0 → 0.2.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 +56 -32
  2. package/dist/controller/controller-internals.d.ts +4 -4
  3. package/dist/controller/create-drag-controller.d.ts +8 -8
  4. package/dist/controller/create-drag-controller.js +23 -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 +274 -155
  21. package/dist/runtime/lifecycle.d.ts +5 -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 +1 -1
  30. package/dist/runtime/drag-runtime-handle.d.ts +0 -22
package/README.md CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  ```
24
24
 
25
25
  The package also exports `@mk-drag-and-drop/dom/integration` for adapter authors
26
- who need lower-level DOM behavior factories and a runtime handle. Deep imports
26
+ who need lower-level DOM behavior factories and a runtime scope. Deep imports
27
27
  into `src` or `dist` files are not part of the public package exports.
28
28
 
29
29
  ## Public Root API
@@ -40,6 +40,7 @@ The root export includes:
40
40
  `DropEvent`, `DragSource`, `DragEndResult`, `DragLifecycleCallbacks`,
41
41
  `DragLifecycleHelpers`
42
42
  - placement types: `SortableDropPlacement`, `RemeasureDropTargetsInput`
43
+ - overlay types: `OverlayReleaseMode`
43
44
  - targeting helpers and types: `pointerToCenter`, `centerToCenter`,
44
45
  `pointerToRectDistance`, `getDistanceToRect`,
45
46
  `maxPointerDistanceToRect`, `maxOverlayCenterDistanceToRect`, `DropTarget`,
@@ -59,10 +60,11 @@ pipeline internals are not public root APIs.
59
60
 
60
61
  `@mk-drag-and-drop/dom/integration` exports:
61
62
 
62
- - `createDragRuntimeHandle`
63
- - `DragRuntimeHandle`, `DragRuntimeHandleOptions`,
64
- `DragRuntimeHandleConfigureInput`
65
- - `DragState`, `DragOverlayPhase`, `DragOverlayRenderState`
63
+ - `createDragRuntimeScope`
64
+ - `DragRuntimeScope`, `DragRuntimeScopeOptions`,
65
+ `DragRuntimeScopeConfigureInput`
66
+ - `DragState`, `DragOverlayPhase`, `DragOverlayRenderState`,
67
+ `OverlayReleaseMode`
66
68
  - `DragRuntimeSubscription`
67
69
  - `createDomDraggable`, `createDomDroppable`, `createDomDropContainer`,
68
70
  `createDomSortable`
@@ -74,15 +76,15 @@ ordinary DOM usage, prefer the root controller and binding helpers.
74
76
 
75
77
  ## Controller
76
78
 
77
- `createDragController(options?)` creates the public DOM controller used by the
78
- root binding helpers. Controller options include:
79
+ `createDragController(options?)` creates the public DOM drag-and-drop scope used
80
+ by the root binding helpers. Controller options include:
79
81
 
80
82
  - lifecycle callbacks: `onDragStart`, `onDragUpdate`, `onDragEnd`, `onDrop`
81
83
  - `announcements`: lifecycle callbacks that return polite live-region text
82
84
  - `dragOverlay(input)`: returns an overlay `HTMLElement` or `null`
83
85
  - `overlayRoot`: optional root for the DOM overlay wrapper
84
- - `keepOverlayOnDrop`: keeps the overlay in the `"released"` phase until
85
- `finishOverlay` or the overlay input `finish` callback removes it
86
+ - `overlayRelease`: `"auto"` removes overlays on drag end; `"manual"` keeps a
87
+ released overlay until its `removeOverlay` callback is called
86
88
  - `targetingAlgorithm` and `targetingConstraint`
87
89
  - `modifiers`
88
90
  - `pointerConfiguration`
@@ -90,23 +92,28 @@ root binding helpers. Controller options include:
90
92
 
91
93
  The returned controller has:
92
94
 
93
- - `update(options)`: replaces runtime/controller configuration
94
95
  - `remeasureDropTargets(input?)`: remeasures all targets, one target id, an
95
96
  array of target ids, or `{ group }`
96
- - `cleanup()`: ends active drag resources and removes the current overlay
97
- - `dispose()`: final teardown for the controller, runtime registrations,
98
- subscriptions, overlays, live region, and registered dispose callbacks
99
- - `finishOverlay()`: removes a kept overlay
97
+
98
+ The controller does not expose runtime teardown, disposal, update, or broad
99
+ overlay removal methods. Runtime objects are ordinary JavaScript objects; DOM
100
+ bindings and active drag resources are owned by their own lifetimes.
100
101
 
101
102
  ## Drag Overlays
102
103
 
103
104
  `dragOverlay(input)` is a creation hook. The controller calls it when overlay
104
- content is mounted for the `"dragging"` phase, and once more for the
105
- `"released"` phase when `keepOverlayOnDrop` is enabled. Pointer movement does
106
- not call `dragOverlay`.
105
+ content is mounted for the `"dragging"` phase. By default, overlays are removed
106
+ immediately when a drag ends. When `overlayRelease: "manual"` is configured, the
107
+ controller calls `dragOverlay` once more for the `"released"` phase and passes
108
+ `removeOverlay`; call that function when app-owned release animation is done.
109
+ Pointer movement does not call `dragOverlay`.
110
+
111
+ `removeOverlay` is only present for released overlays in manual release mode.
112
+ Calling it more than once is safe. It removes the overlay host state only; it is
113
+ not a controller or runtime teardown API.
107
114
 
108
115
  Apps own the returned overlay element and any dynamic content inside it. The
109
- package owns the overlay wrapper, movement, cleanup, and geometry measurement
116
+ package owns the overlay wrapper, movement, release, and geometry measurement
110
117
  used by targeting and modifiers. The wrapper is positioned with `position:
111
118
  fixed`, source rect dimensions, `pointer-events: none`, and a transform derived
112
119
  from the current pointer delta.
@@ -200,14 +207,15 @@ Helpers:
200
207
  `onDrop` only runs for a valid successful drop. `onDragEnd` runs whenever a drag
201
208
  ends. `result: "dropped"` means a valid target was accepted and `onDrop` also
202
209
  runs. `result: "no-target"` means the user ended normally with no active target.
203
- `result: "invalid-target"` means an active target candidate was stale by finish
204
- time. `result: "canceled"` means the drag was canceled, such as Escape,
210
+ `result: "invalid-target"` means an active target candidate was stale by drag
211
+ end. `result: "canceled"` means the drag was canceled, such as Escape,
205
212
  pointercancel, or runtime cancellation.
206
213
 
207
214
  ## Bindings
208
215
 
209
216
  Root binding helpers attach behavior to DOM elements and return `void`.
210
- Registrations and listeners are tied to the controller lifetime.
217
+ Registrations are WeakRef-backed and stale disconnected elements are pruned on
218
+ drag-critical paths and explicit remeasurement.
211
219
 
212
220
  `createDraggable(input)` accepts:
213
221
 
@@ -303,7 +311,10 @@ distance helper that matches the intended behavior.
303
311
 
304
312
  Sortable placement is separate from targeting. The configured targeting
305
313
  algorithm alone chooses `activeDropTarget`; sortable only decides before/after
306
- preview placement relative to that active target.
314
+ preview placement relative to that active target. For sortable groups, the
315
+ registry may narrow measured candidates to relevant sortable item, neighbor, or
316
+ container entries before the targeting algorithm runs; the algorithm still makes
317
+ the active-target decision from that narrowed measured list.
307
318
 
308
319
  ## Modifiers
309
320
 
@@ -353,19 +364,33 @@ and container ids are identifiers that your app maps to its own model.
353
364
  Commit data in lifecycle callbacks, usually `onDrop`. Rerender or patch the DOM
354
365
  using your app's normal rendering path after the commit.
355
366
 
356
- ## Cleanup Model
367
+ ## Lifetime Model
368
+
369
+ Root binding helpers do not return per-item disposers. Runtime/controller
370
+ objects are ordinary JavaScript objects and are released by garbage collection
371
+ when unreachable.
357
372
 
358
- Root binding helpers do not return per-item disposers. Dispose the controller
359
- when tearing down the view or application area that owns those bindings.
373
+ DOM registrations are a separate lifetime. Bindings keep WeakRef-backed records,
374
+ and disconnected drop targets/sortables are pruned on drag-critical paths and
375
+ explicit remeasurement. Active drag resources are owned by the input drag
376
+ lifecycle and are released on drop, cancel, and pointercancel.
360
377
 
361
- Use:
378
+ Pending pointer activation is also separate from an active drag. Activation
379
+ delay timers, the pending activation state, and the latest pre-activation
380
+ pointer position are canceled by pointerup, pointercancel, activation success,
381
+ activation failure, or active drag reset.
362
382
 
363
- - `controller.cleanup()` to end active drag work and remove the active overlay
364
- - `controller.dispose()` for final teardown
383
+ Active drag resources include pointer/window listeners, keyboard listeners, RAF
384
+ pointer updates, text-selection suppression, active target/session state,
385
+ overlay host state, active modifiers, and sortable preview/snapshot state. They
386
+ are released by active drag lifecycle paths. Runtime disposal is not the cleanup
387
+ mechanism for those resources.
365
388
 
366
- Disconnected drop targets are pruned on drag-critical paths, and dispose clears
367
- runtime registrations. React users usually rely on `DragProvider` and hooks for
368
- runtime and binding lifetime.
389
+ Sortable registration state and sortable active preview state are separate.
390
+ Registered sortable elements, ids, groups, containers, and options remain until
391
+ they are unregistered or pruned as stale DOM. Per-drag sortable snapshots,
392
+ dragged attributes, preview placement, and moved DOM are restored by active drag
393
+ cleanup.
369
394
 
370
395
  ## Accessibility Scope
371
396
 
@@ -429,7 +454,6 @@ for (const columnId of ["todo", "done"]) {
429
454
  }
430
455
 
431
456
  document.body.append(item);
432
- window.addEventListener("pagehide", () => controller.dispose(), { once: true });
433
457
  ```
434
458
 
435
459
  ## Examples
@@ -1,5 +1,5 @@
1
- import type { DragRuntimeHandle, InternalBindingCleanupRuntime } from "../runtime/drag-runtime-handle.js";
1
+ import type { DragRuntimeScope, InternalStaleDomBindingRuntime } from "../runtime/drag-runtime-scope.js";
2
2
  import type { DragController } from "./create-drag-controller.js";
3
- export type DragControllerRuntimeHandle = DragRuntimeHandle & InternalBindingCleanupRuntime;
4
- export declare function setControllerRuntime(controller: DragController, runtime: DragRuntimeHandle): void;
5
- export declare function getControllerRuntime(controller: DragController): DragControllerRuntimeHandle;
3
+ export type DragControllerRuntimeScope = DragRuntimeScope & InternalStaleDomBindingRuntime;
4
+ export declare function setControllerRuntime(controller: DragController, runtime: DragRuntimeScope): void;
5
+ export declare function getControllerRuntime(controller: DragController): DragControllerRuntimeScope;
@@ -2,7 +2,7 @@ import type { KeyboardConfiguration, PointerConfiguration } from "../input/confi
2
2
  import type { DragModifierInput } from "../modifiers/types.js";
3
3
  import type { RemeasureDropTargetsInput } from "../runtime/drop-target-registry.js";
4
4
  import type { DragEndEvent, DragLifecycleCallbacks, DragStartEvent, DragUpdateEvent, DropEvent } from "../runtime/lifecycle.js";
5
- import type { DragOverlayPhase, DragState } from "../runtime/types.js";
5
+ import type { DragState, OverlayReleaseMode } from "../runtime/types.js";
6
6
  import type { TargetingAlgorithm, TargetingConstraint } from "../targeting/types.js";
7
7
  export type DragControllerAnnouncements = {
8
8
  onDragStart?: (event: DragStartEvent) => string | null;
@@ -12,25 +12,25 @@ export type DragControllerAnnouncements = {
12
12
  };
13
13
  export type DragControllerOverlayInput = {
14
14
  dragState: DragState;
15
- phase: DragOverlayPhase;
16
- finish: () => void;
15
+ phase: "dragging";
16
+ removeOverlay?: never;
17
+ } | {
18
+ dragState: DragState;
19
+ phase: "released";
20
+ removeOverlay: () => void;
17
21
  };
18
22
  export type DragControllerOptions = {
19
23
  announcements?: DragControllerAnnouncements;
20
24
  dragOverlay?: (input: DragControllerOverlayInput) => HTMLElement | null;
21
25
  keyboardConfiguration?: KeyboardConfiguration;
22
- keepOverlayOnDrop?: boolean;
23
26
  modifiers?: readonly DragModifierInput[];
27
+ overlayRelease?: OverlayReleaseMode;
24
28
  overlayRoot?: HTMLElement;
25
29
  pointerConfiguration?: PointerConfiguration;
26
30
  targetingAlgorithm?: TargetingAlgorithm;
27
31
  targetingConstraint?: TargetingConstraint;
28
32
  } & DragLifecycleCallbacks;
29
33
  export type DragController = {
30
- update: (options: DragControllerOptions) => void;
31
- cleanup: () => void;
32
- dispose: () => void;
33
- finishOverlay: () => void;
34
34
  remeasureDropTargets: (input?: RemeasureDropTargetsInput) => void;
35
35
  };
36
36
  export declare function createDragController(options?: DragControllerOptions): DragController;
@@ -1,53 +1,24 @@
1
1
  import { rectToDragRect } from "../geometry/rects.js";
2
- import { createDragRuntimeHandle } from "../runtime/drag-runtime-handle.js";
2
+ import { createDragRuntimeScope } from "../runtime/drag-runtime-scope.js";
3
3
  import { pointerToCenter } from "../targeting/algorithms.js";
4
4
  import { setControllerRuntime } from "./controller-internals.js";
5
5
  export function createDragController(options = {}) {
6
- let currentOptions = options;
6
+ const currentOptions = options;
7
7
  let overlayWrapper = null;
8
8
  let overlayElement = null;
9
9
  let overlayResizeObserver = null;
10
10
  let liveRegion = null;
11
11
  let lastAnnouncement = null;
12
- let disposed = false;
13
- const runtime = createDragRuntimeHandle({
12
+ const runtime = createDragRuntimeScope({
14
13
  updateOverlayHost,
15
14
  targetingAlgorithm: options.targetingAlgorithm ?? pointerToCenter,
16
15
  targetingConstraint: options.targetingConstraint,
17
16
  hasDragOverlay: hasDragOverlay(options),
18
- keepOverlayOnDrop: options.keepOverlayOnDrop ?? false,
17
+ overlayRelease: options.overlayRelease ?? "auto",
19
18
  });
20
19
  configureRuntime(options);
21
20
  syncLiveRegion();
22
21
  const controller = {
23
- update: (nextOptions) => {
24
- if (disposed) {
25
- return;
26
- }
27
- currentOptions = nextOptions;
28
- configureRuntime(nextOptions);
29
- syncLiveRegion();
30
- if (!hasDragOverlay(nextOptions)) {
31
- removeOverlay();
32
- }
33
- },
34
- cleanup: () => {
35
- if (disposed) {
36
- return;
37
- }
38
- runtime.cleanup();
39
- removeOverlay();
40
- },
41
- dispose: () => {
42
- if (disposed) {
43
- return;
44
- }
45
- runtime.dispose();
46
- removeOverlay();
47
- removeLiveRegion();
48
- disposed = true;
49
- },
50
- finishOverlay,
51
22
  remeasureDropTargets: (input) => {
52
23
  runtime.remeasureDropTargets(input);
53
24
  },
@@ -59,7 +30,7 @@ export function createDragController(options = {}) {
59
30
  targetingAlgorithm: nextOptions.targetingAlgorithm ?? pointerToCenter,
60
31
  targetingConstraint: nextOptions.targetingConstraint,
61
32
  hasDragOverlay: hasDragOverlay(nextOptions),
62
- keepOverlayOnDrop: nextOptions.keepOverlayOnDrop ?? false,
33
+ overlayRelease: nextOptions.overlayRelease ?? "auto",
63
34
  lifecycleCallbacks: createLifecycleCallbacks(nextOptions),
64
35
  keyboardConfiguration: nextOptions.keyboardConfiguration,
65
36
  modifiers: nextOptions.modifiers,
@@ -89,10 +60,6 @@ export function createDragController(options = {}) {
89
60
  };
90
61
  }
91
62
  function updateOverlayHost(update) {
92
- if (disposed) {
93
- removeOverlay();
94
- return;
95
- }
96
63
  if (update.type === "mount" || update.type === "release") {
97
64
  mountOverlay(update.state);
98
65
  return;
@@ -101,20 +68,16 @@ export function createDragController(options = {}) {
101
68
  moveOverlay(update.dragState);
102
69
  return;
103
70
  }
104
- removeOverlay();
71
+ clearOverlayHost();
105
72
  }
106
73
  function mountOverlay(overlayState) {
107
74
  if (!currentOptions.dragOverlay) {
108
- removeOverlay();
75
+ clearOverlayHost();
109
76
  return;
110
77
  }
111
- const nextOverlayElement = currentOptions.dragOverlay({
112
- dragState: overlayState.dragState,
113
- phase: overlayState.phase,
114
- finish: finishOverlay,
115
- });
78
+ const nextOverlayElement = currentOptions.dragOverlay(createOverlayInput(overlayState));
116
79
  if (!nextOverlayElement) {
117
- removeOverlay();
80
+ clearOverlayHost();
118
81
  return;
119
82
  }
120
83
  const wrapper = getOverlayWrapper(currentOptions.overlayRoot ?? document.body);
@@ -174,19 +137,26 @@ export function createDragController(options = {}) {
174
137
  }
175
138
  return overlayWrapper;
176
139
  }
177
- function finishOverlay() {
178
- if (disposed) {
179
- return;
180
- }
181
- removeOverlay();
182
- }
183
- function removeOverlay() {
140
+ function clearOverlayHost() {
184
141
  disconnectOverlayResizeObserver();
185
142
  overlayElement = null;
186
143
  runtime.setOverlayRect(null);
187
144
  overlayWrapper?.remove();
188
145
  overlayWrapper = null;
189
146
  }
147
+ function createOverlayInput(overlayState) {
148
+ if (overlayState.phase === "released") {
149
+ return {
150
+ dragState: overlayState.dragState,
151
+ phase: "released",
152
+ removeOverlay: clearOverlayHost,
153
+ };
154
+ }
155
+ return {
156
+ dragState: overlayState.dragState,
157
+ phase: "dragging",
158
+ };
159
+ }
190
160
  function syncLiveRegion() {
191
161
  if (currentOptions.announcements) {
192
162
  ensureLiveRegion();
@@ -25,7 +25,7 @@ export function createDraggable(input) {
25
25
  input.element.addEventListener("keydown", onKeyDown);
26
26
  keyDownAttached = true;
27
27
  }
28
- const cleanup = () => {
28
+ const releaseDomBinding = () => {
29
29
  if (cleanedUp) {
30
30
  return;
31
31
  }
@@ -45,8 +45,8 @@ export function createDraggable(input) {
45
45
  }
46
46
  }
47
47
  };
48
- runtime.registerBindingCleanup({
49
- cleanup,
48
+ runtime.registerStaleDomBinding({
49
+ release: releaseDomBinding,
50
50
  isConnected: () => elementRef.deref()?.isConnected === true,
51
51
  });
52
52
  }
@@ -11,7 +11,7 @@ export function createDropContainer(input) {
11
11
  getElement: () => elementRef.deref() ?? null,
12
12
  });
13
13
  let cleanedUp = false;
14
- const cleanup = () => {
14
+ const releaseDomBinding = () => {
15
15
  if (cleanedUp) {
16
16
  return;
17
17
  }
@@ -19,10 +19,10 @@ export function createDropContainer(input) {
19
19
  if (!elementRef.deref()) {
20
20
  return;
21
21
  }
22
- behavior.cleanup();
22
+ behavior.releaseRegistration();
23
23
  };
24
- runtime.registerBindingCleanup({
25
- cleanup,
24
+ runtime.registerStaleDomBinding({
25
+ release: releaseDomBinding,
26
26
  isConnected: () => elementRef.deref()?.isConnected === true,
27
27
  });
28
28
  }
@@ -13,6 +13,6 @@ export type CreateDomDropContainerInput = {
13
13
  };
14
14
  export type DomDropContainerBehavior = {
15
15
  setElement: (element: HTMLElement | null) => void;
16
- cleanup: () => void;
16
+ releaseRegistration: () => void;
17
17
  };
18
18
  export declare function createDomDropContainer(input: CreateDomDropContainerInput): DomDropContainerBehavior;
@@ -1,7 +1,7 @@
1
1
  export function createDomDropContainer(input) {
2
2
  let registeredElementRef = null;
3
3
  let registeredContainerId = null;
4
- const cleanup = () => {
4
+ const releaseRegistration = () => {
5
5
  const registeredElement = registeredElementRef?.deref() ?? null;
6
6
  if (registeredContainerId !== null) {
7
7
  input.runtime.unregisterDropContainer(registeredContainerId, registeredElement ?? undefined);
@@ -25,12 +25,12 @@ export function createDomDropContainer(input) {
25
25
  registeredContainerId === input.containerId) {
26
26
  return;
27
27
  }
28
- cleanup();
28
+ releaseRegistration();
29
29
  if (!element) {
30
30
  return;
31
31
  }
32
32
  registerElement(element);
33
33
  },
34
- cleanup,
34
+ releaseRegistration,
35
35
  };
36
36
  }
@@ -12,7 +12,7 @@ export function createDroppable(input) {
12
12
  });
13
13
  let cleanedUp = false;
14
14
  behavior.setElement(input.element);
15
- const cleanup = () => {
15
+ const releaseDomBinding = () => {
16
16
  if (cleanedUp) {
17
17
  return;
18
18
  }
@@ -20,10 +20,10 @@ export function createDroppable(input) {
20
20
  if (!elementRef.deref()) {
21
21
  return;
22
22
  }
23
- behavior.cleanup();
23
+ behavior.releaseRegistration();
24
24
  };
25
- runtime.registerBindingCleanup({
26
- cleanup,
25
+ runtime.registerStaleDomBinding({
26
+ release: releaseDomBinding,
27
27
  isConnected: () => elementRef.deref()?.isConnected === true,
28
28
  });
29
29
  }
@@ -12,6 +12,6 @@ export type CreateDomDroppableInput = {
12
12
  };
13
13
  export type DomDroppableBehavior = {
14
14
  setElement: (element: HTMLElement | null) => void;
15
- cleanup: () => void;
15
+ releaseRegistration: () => void;
16
16
  };
17
17
  export declare function createDomDroppable(input: CreateDomDroppableInput): DomDroppableBehavior;
@@ -1,7 +1,7 @@
1
1
  export function createDomDroppable(input) {
2
2
  let registeredElementRef = null;
3
3
  let registeredTargetId = null;
4
- const cleanup = () => {
4
+ const releaseRegistration = () => {
5
5
  const registeredElement = registeredElementRef?.deref() ?? null;
6
6
  if (registeredTargetId !== null) {
7
7
  input.runtime.unregisterDropTarget(registeredTargetId, registeredElement ?? undefined);
@@ -16,7 +16,7 @@ export function createDomDroppable(input) {
16
16
  registeredTargetId === input.dropTargetId) {
17
17
  return;
18
18
  }
19
- cleanup();
19
+ releaseRegistration();
20
20
  if (!element) {
21
21
  return;
22
22
  }
@@ -26,6 +26,6 @@ export function createDomDroppable(input) {
26
26
  registeredElementRef = new WeakRef(element);
27
27
  registeredTargetId = input.dropTargetId;
28
28
  },
29
- cleanup,
29
+ releaseRegistration,
30
30
  };
31
31
  }
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export type { SortableAxis, SortablePlacementBoundary, } from "./sortable/sortab
7
7
  export { createDragHandle, type CreateDragHandleInput, } from "./input/create-drag-handle.js";
8
8
  export type { DragEndResult, DragEndEvent, DragLifecycleCallbacks, DragLifecycleHelpers, DragSource, DragStartEvent, DragUpdateEvent, DropEvent, } from "./runtime/lifecycle.js";
9
9
  export type { RemeasureDropTargetsInput, SortableDropPlacement, } from "./runtime/drop-target-registry.js";
10
+ export type { OverlayReleaseMode } from "./runtime/types.js";
10
11
  export { centerToCenter, pointerToCenter, pointerToRectDistance, } from "./targeting/algorithms.js";
11
12
  export { getDistanceToRect, maxOverlayCenterDistanceToRect, maxPointerDistanceToRect, } from "./targeting/constraints.js";
12
13
  export type { DropTarget, TargetingAlgorithm, TargetingAlgorithmInput, TargetingConstraint, TargetingConstraintInput, } from "./targeting/types.js";
@@ -26,6 +26,8 @@ export declare class PointerActivationController {
26
26
  private pendingActivation;
27
27
  constructor(options: PointerActivationControllerOptions);
28
28
  request(request: PointerDragActivationRequest): void;
29
- cancel(): void;
30
- private activatePending;
29
+ cancelPendingActivation(): void;
30
+ private clearPendingActivation;
31
+ private clearPendingActivationTimer;
32
+ private activatePendingPointerDrag;
31
33
  }
@@ -5,7 +5,7 @@ export class PointerActivationController {
5
5
  this.options = options;
6
6
  }
7
7
  request(request) {
8
- this.cancel();
8
+ this.cancelPendingActivation();
9
9
  if (this.options.isDragging()) {
10
10
  return;
11
11
  }
@@ -25,8 +25,8 @@ export class PointerActivationController {
25
25
  pointerId: request.pointerId,
26
26
  initialPointerPosition,
27
27
  latestPointerPosition: initialPointerPosition,
28
- timeoutId: null,
29
- cleanupWindowListeners: () => { },
28
+ activationTimerId: null,
29
+ releasePendingPointerListeners: null,
30
30
  };
31
31
  const handlePointerMove = (event) => {
32
32
  if (event.pointerId !== pendingActivation.pointerId) {
@@ -45,46 +45,58 @@ export class PointerActivationController {
45
45
  const distanceSquared = distanceX * distanceX + distanceY * distanceY;
46
46
  const activationDistanceSquared = activationDistance * activationDistance;
47
47
  if (distanceSquared >= activationDistanceSquared) {
48
- this.activatePending(pendingActivation);
48
+ this.activatePendingPointerDrag(pendingActivation);
49
49
  }
50
50
  };
51
51
  const handlePointerEnd = (event) => {
52
52
  if (event.pointerId !== pendingActivation.pointerId) {
53
53
  return;
54
54
  }
55
- this.cancel();
55
+ this.cancelPendingActivation();
56
56
  };
57
57
  window.addEventListener("pointermove", handlePointerMove);
58
58
  window.addEventListener("pointerup", handlePointerEnd);
59
59
  window.addEventListener("pointercancel", handlePointerEnd);
60
- pendingActivation.cleanupWindowListeners = () => {
60
+ pendingActivation.releasePendingPointerListeners = () => {
61
61
  window.removeEventListener("pointermove", handlePointerMove);
62
62
  window.removeEventListener("pointerup", handlePointerEnd);
63
63
  window.removeEventListener("pointercancel", handlePointerEnd);
64
64
  };
65
+ this.pendingActivation = pendingActivation;
65
66
  if (activationDelay !== null) {
66
- pendingActivation.timeoutId = window.setTimeout(() => {
67
- this.activatePending(pendingActivation);
67
+ pendingActivation.activationTimerId = window.setTimeout(() => {
68
+ this.activatePendingPointerDrag(pendingActivation);
68
69
  }, activationDelay);
69
70
  }
70
- this.pendingActivation = pendingActivation;
71
71
  }
72
- cancel() {
72
+ cancelPendingActivation() {
73
73
  const pendingActivation = this.pendingActivation;
74
74
  if (!pendingActivation) {
75
75
  return;
76
76
  }
77
- if (pendingActivation.timeoutId !== null) {
78
- window.clearTimeout(pendingActivation.timeoutId);
77
+ this.clearPendingActivation(pendingActivation);
78
+ }
79
+ clearPendingActivation(pendingActivation) {
80
+ if (this.pendingActivation === pendingActivation) {
81
+ this.pendingActivation = null;
82
+ }
83
+ this.clearPendingActivationTimer(pendingActivation);
84
+ const releasePendingPointerListeners = pendingActivation.releasePendingPointerListeners;
85
+ pendingActivation.releasePendingPointerListeners = null;
86
+ releasePendingPointerListeners?.();
87
+ }
88
+ clearPendingActivationTimer(pendingActivation) {
89
+ if (pendingActivation.activationTimerId === null) {
90
+ return;
79
91
  }
80
- pendingActivation.cleanupWindowListeners();
81
- this.pendingActivation = null;
92
+ window.clearTimeout(pendingActivation.activationTimerId);
93
+ pendingActivation.activationTimerId = null;
82
94
  }
83
- activatePending(pendingActivation) {
95
+ activatePendingPointerDrag(pendingActivation) {
84
96
  if (this.pendingActivation !== pendingActivation) {
85
97
  return;
86
98
  }
87
- this.cancel();
99
+ this.clearPendingActivation(pendingActivation);
88
100
  this.options.activate({
89
101
  draggableId: pendingActivation.draggableId,
90
102
  group: pendingActivation.group,
@@ -1,5 +1,5 @@
1
- export { createDragRuntimeHandle, type DragRuntimeHandle, type DragRuntimeHandleConfigureInput, type DragRuntimeHandleOptions, } from "../runtime/drag-runtime-handle.js";
2
- export type { DragOverlayHostUpdate, DragOverlayPhase, DragOverlayRenderState, DragState, } from "../runtime/types.js";
1
+ export { createDragRuntimeScope, type DragRuntimeScope, type DragRuntimeScopeConfigureInput, type DragRuntimeScopeOptions, } from "../runtime/drag-runtime-scope.js";
2
+ export type { DragOverlayHostUpdate, DragOverlayPhase, DragOverlayRenderState, DragState, OverlayReleaseMode, } from "../runtime/types.js";
3
3
  export type { DragRuntimeSubscription } from "../runtime/lifecycle.js";
4
4
  export { createDomDraggable, type CreateDomDraggableInput, type DomDraggableBehavior, type DomDraggableKeyDownEvent, type DomDraggablePointerDownEvent, type DomDraggableRuntime, } from "../draggable/create-draggable.js";
5
5
  export { createDomDroppable, type CreateDomDroppableInput, type DomDroppableBehavior, type DomDroppableRuntime, } from "../droppable/create-droppable.js";
@@ -1,4 +1,4 @@
1
- export { createDragRuntimeHandle, } from "../runtime/drag-runtime-handle.js";
1
+ export { createDragRuntimeScope, } from "../runtime/drag-runtime-scope.js";
2
2
  export { createDomDraggable, } from "../draggable/create-draggable.js";
3
3
  export { createDomDroppable, } from "../droppable/create-droppable.js";
4
4
  export { createDomDropContainer, } from "../droppable/create-drop-container.js";
@@ -0,0 +1,21 @@
1
+ import type { DragRect } from "../geometry/rects.js";
2
+ import type { DomDraggableRuntime } from "../draggable/create-draggable.js";
3
+ import type { DomDropContainerRuntime } from "../droppable/create-drop-container.js";
4
+ import type { DomDroppableRuntime } from "../droppable/create-droppable.js";
5
+ import type { DomSortableRuntime } from "../sortable/sortable-registry.js";
6
+ import { type StaleDomBindingRecord } from "./drag-runtime.js";
7
+ import type { DragRuntimeConfigureInput, DragRuntimeOptions } from "./types.js";
8
+ export type DragRuntimeScopeOptions = DragRuntimeOptions;
9
+ export type DragRuntimeScopeConfigureInput = DragRuntimeConfigureInput;
10
+ export type DragRuntimeScope = DomDraggableRuntime & DomDroppableRuntime & DomDropContainerRuntime & DomSortableRuntime & {
11
+ configure: (input: DragRuntimeScopeConfigureInput) => void;
12
+ cancelDrag: () => void;
13
+ releaseActiveDragResources: () => void;
14
+ setOverlayRect: (overlayRect: DragRect | null) => void;
15
+ };
16
+ export type InternalStaleDomBindingRuntime = {
17
+ registerStaleDomBinding: (record: StaleDomBindingRecord) => () => void;
18
+ pruneDisconnectedDomBindings: () => void;
19
+ getStaleDomBindingRecordCount: () => number;
20
+ };
21
+ export declare function createDragRuntimeScope(options?: DragRuntimeScopeOptions): DragRuntimeScope;