@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.
- package/README.md +56 -32
- package/dist/controller/controller-internals.d.ts +4 -4
- package/dist/controller/create-drag-controller.d.ts +8 -8
- package/dist/controller/create-drag-controller.js +23 -53
- package/dist/draggable/create-draggable-binding.js +3 -3
- package/dist/droppable/create-drop-container-binding.js +4 -4
- package/dist/droppable/create-drop-container.d.ts +1 -1
- package/dist/droppable/create-drop-container.js +3 -3
- package/dist/droppable/create-droppable-binding.js +4 -4
- package/dist/droppable/create-droppable.d.ts +1 -1
- package/dist/droppable/create-droppable.js +3 -3
- package/dist/index.d.ts +1 -0
- package/dist/input/pointer-activation.d.ts +4 -2
- package/dist/input/pointer-activation.js +28 -16
- package/dist/integration/index.d.ts +2 -2
- package/dist/integration/index.js +1 -1
- package/dist/runtime/drag-runtime-scope.d.ts +21 -0
- package/dist/runtime/{drag-runtime-handle.js → drag-runtime-scope.js} +8 -9
- package/dist/runtime/drag-runtime.d.ts +21 -17
- package/dist/runtime/drag-runtime.js +274 -155
- package/dist/runtime/lifecycle.d.ts +5 -0
- package/dist/runtime/types.d.ts +3 -2
- package/dist/sortable/create-sortable-binding.js +4 -4
- package/dist/sortable/create-sortable.d.ts +1 -1
- package/dist/sortable/create-sortable.js +3 -3
- package/dist/sortable/sortable-preview.js +20 -17
- package/dist/sortable/sortable-registry.d.ts +15 -6
- package/dist/sortable/sortable-registry.js +65 -47
- package/package.json +1 -1
- 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
|
|
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
|
-
- `
|
|
63
|
-
- `
|
|
64
|
-
`
|
|
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
|
|
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
|
-
- `
|
|
85
|
-
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
105
|
-
|
|
106
|
-
|
|
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,
|
|
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
|
|
204
|
-
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
359
|
-
|
|
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
|
-
|
|
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
|
-
|
|
364
|
-
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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 {
|
|
1
|
+
import type { DragRuntimeScope, InternalStaleDomBindingRuntime } from "../runtime/drag-runtime-scope.js";
|
|
2
2
|
import type { DragController } from "./create-drag-controller.js";
|
|
3
|
-
export type
|
|
4
|
-
export declare function setControllerRuntime(controller: DragController, runtime:
|
|
5
|
-
export declare function getControllerRuntime(controller: DragController):
|
|
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 {
|
|
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:
|
|
16
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
71
|
+
clearOverlayHost();
|
|
105
72
|
}
|
|
106
73
|
function mountOverlay(overlayState) {
|
|
107
74
|
if (!currentOptions.dragOverlay) {
|
|
108
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
49
|
-
|
|
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
|
|
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.
|
|
22
|
+
behavior.releaseRegistration();
|
|
23
23
|
};
|
|
24
|
-
runtime.
|
|
25
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
28
|
+
releaseRegistration();
|
|
29
29
|
if (!element) {
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
32
|
registerElement(element);
|
|
33
33
|
},
|
|
34
|
-
|
|
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
|
|
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.
|
|
23
|
+
behavior.releaseRegistration();
|
|
24
24
|
};
|
|
25
|
-
runtime.
|
|
26
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
private
|
|
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.
|
|
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
|
-
|
|
29
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
67
|
-
this.
|
|
67
|
+
pendingActivation.activationTimerId = window.setTimeout(() => {
|
|
68
|
+
this.activatePendingPointerDrag(pendingActivation);
|
|
68
69
|
}, activationDelay);
|
|
69
70
|
}
|
|
70
|
-
this.pendingActivation = pendingActivation;
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
cancelPendingActivation() {
|
|
73
73
|
const pendingActivation = this.pendingActivation;
|
|
74
74
|
if (!pendingActivation) {
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
|
|
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.
|
|
81
|
-
|
|
92
|
+
window.clearTimeout(pendingActivation.activationTimerId);
|
|
93
|
+
pendingActivation.activationTimerId = null;
|
|
82
94
|
}
|
|
83
|
-
|
|
95
|
+
activatePendingPointerDrag(pendingActivation) {
|
|
84
96
|
if (this.pendingActivation !== pendingActivation) {
|
|
85
97
|
return;
|
|
86
98
|
}
|
|
87
|
-
this.
|
|
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 {
|
|
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 {
|
|
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;
|