@mk-drag-and-drop/dom 0.1.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/LICENSE +21 -0
- package/README.md +460 -0
- package/dist/controller/controller-internals.d.ts +5 -0
- package/dist/controller/controller-internals.js +11 -0
- package/dist/controller/create-drag-controller.d.ts +36 -0
- package/dist/controller/create-drag-controller.js +232 -0
- package/dist/draggable/create-draggable-binding.d.ts +8 -0
- package/dist/draggable/create-draggable-binding.js +52 -0
- package/dist/draggable/create-draggable.d.ts +46 -0
- package/dist/draggable/create-draggable.js +57 -0
- package/dist/droppable/create-drop-container-binding.d.ts +8 -0
- package/dist/droppable/create-drop-container-binding.js +28 -0
- package/dist/droppable/create-drop-container.d.ts +18 -0
- package/dist/droppable/create-drop-container.js +36 -0
- package/dist/droppable/create-droppable-binding.d.ts +9 -0
- package/dist/droppable/create-droppable-binding.js +29 -0
- package/dist/droppable/create-droppable.d.ts +17 -0
- package/dist/droppable/create-droppable.js +31 -0
- package/dist/geometry/measurement.d.ts +4 -0
- package/dist/geometry/measurement.js +33 -0
- package/dist/geometry/overlay.d.ts +19 -0
- package/dist/geometry/overlay.js +32 -0
- package/dist/geometry/rects.d.ts +17 -0
- package/dist/geometry/rects.js +34 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +9 -0
- package/dist/input/config.d.ts +35 -0
- package/dist/input/config.js +56 -0
- package/dist/input/create-drag-handle.d.ts +4 -0
- package/dist/input/create-drag-handle.js +4 -0
- package/dist/input/drag-handle.d.ts +10 -0
- package/dist/input/drag-handle.js +74 -0
- package/dist/input/keyboard-drag.d.ts +28 -0
- package/dist/input/keyboard-drag.js +77 -0
- package/dist/input/pointer-activation.d.ts +31 -0
- package/dist/input/pointer-activation.js +97 -0
- package/dist/integration/index.d.ts +10 -0
- package/dist/integration/index.js +6 -0
- package/dist/modifiers/built-ins.d.ts +6 -0
- package/dist/modifiers/built-ins.js +62 -0
- package/dist/modifiers/pipeline.d.ts +18 -0
- package/dist/modifiers/pipeline.js +34 -0
- package/dist/modifiers/types.d.ts +32 -0
- package/dist/modifiers/types.js +1 -0
- package/dist/runtime/drag-runtime-handle.d.ts +22 -0
- package/dist/runtime/drag-runtime-handle.js +33 -0
- package/dist/runtime/drag-runtime.d.ts +96 -0
- package/dist/runtime/drag-runtime.js +798 -0
- package/dist/runtime/drop-target-registry.d.ts +101 -0
- package/dist/runtime/drop-target-registry.js +855 -0
- package/dist/runtime/lifecycle.d.ts +44 -0
- package/dist/runtime/lifecycle.js +1 -0
- package/dist/runtime/types.d.ts +65 -0
- package/dist/runtime/types.js +1 -0
- package/dist/sortable/create-sortable-binding.d.ts +10 -0
- package/dist/sortable/create-sortable-binding.js +58 -0
- package/dist/sortable/create-sortable.d.ts +19 -0
- package/dist/sortable/create-sortable.js +55 -0
- package/dist/sortable/sortable-options.d.ts +17 -0
- package/dist/sortable/sortable-options.js +20 -0
- package/dist/sortable/sortable-preview.d.ts +79 -0
- package/dist/sortable/sortable-preview.js +357 -0
- package/dist/sortable/sortable-registry.d.ts +113 -0
- package/dist/sortable/sortable-registry.js +145 -0
- package/dist/targeting/algorithms.d.ts +6 -0
- package/dist/targeting/algorithms.js +56 -0
- package/dist/targeting/constraints.d.ts +15 -0
- package/dist/targeting/constraints.js +58 -0
- package/dist/targeting/types.d.ts +23 -0
- package/dist/targeting/types.js +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { rectToDragRect } from "../geometry/rects.js";
|
|
2
|
+
import { createDragRuntimeHandle } from "../runtime/drag-runtime-handle.js";
|
|
3
|
+
import { pointerToCenter } from "../targeting/algorithms.js";
|
|
4
|
+
import { setControllerRuntime } from "./controller-internals.js";
|
|
5
|
+
export function createDragController(options = {}) {
|
|
6
|
+
let currentOptions = options;
|
|
7
|
+
let overlayWrapper = null;
|
|
8
|
+
let overlayElement = null;
|
|
9
|
+
let overlayResizeObserver = null;
|
|
10
|
+
let liveRegion = null;
|
|
11
|
+
let lastAnnouncement = null;
|
|
12
|
+
let disposed = false;
|
|
13
|
+
const runtime = createDragRuntimeHandle({
|
|
14
|
+
updateOverlayHost,
|
|
15
|
+
targetingAlgorithm: options.targetingAlgorithm ?? pointerToCenter,
|
|
16
|
+
targetingConstraint: options.targetingConstraint,
|
|
17
|
+
hasDragOverlay: hasDragOverlay(options),
|
|
18
|
+
keepOverlayOnDrop: options.keepOverlayOnDrop ?? false,
|
|
19
|
+
});
|
|
20
|
+
configureRuntime(options);
|
|
21
|
+
syncLiveRegion();
|
|
22
|
+
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
|
+
remeasureDropTargets: (input) => {
|
|
52
|
+
runtime.remeasureDropTargets(input);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
setControllerRuntime(controller, runtime);
|
|
56
|
+
return controller;
|
|
57
|
+
function configureRuntime(nextOptions) {
|
|
58
|
+
runtime.configure({
|
|
59
|
+
targetingAlgorithm: nextOptions.targetingAlgorithm ?? pointerToCenter,
|
|
60
|
+
targetingConstraint: nextOptions.targetingConstraint,
|
|
61
|
+
hasDragOverlay: hasDragOverlay(nextOptions),
|
|
62
|
+
keepOverlayOnDrop: nextOptions.keepOverlayOnDrop ?? false,
|
|
63
|
+
lifecycleCallbacks: createLifecycleCallbacks(nextOptions),
|
|
64
|
+
keyboardConfiguration: nextOptions.keyboardConfiguration,
|
|
65
|
+
modifiers: nextOptions.modifiers,
|
|
66
|
+
pointerConfiguration: nextOptions.pointerConfiguration,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function createLifecycleCallbacks(nextOptions) {
|
|
70
|
+
return {
|
|
71
|
+
onDragStart: (event, helpers) => {
|
|
72
|
+
nextOptions.onDragStart?.(event, helpers);
|
|
73
|
+
announce(nextOptions.announcements?.onDragStart?.(event));
|
|
74
|
+
},
|
|
75
|
+
onDragUpdate: (event, helpers) => {
|
|
76
|
+
nextOptions.onDragUpdate?.(event, helpers);
|
|
77
|
+
if (event.activeDropTargetId !== event.previousDropTargetId) {
|
|
78
|
+
announce(nextOptions.announcements?.onDragUpdate?.(event));
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
onDragEnd: (event, helpers) => {
|
|
82
|
+
nextOptions.onDragEnd?.(event, helpers);
|
|
83
|
+
announce(nextOptions.announcements?.onDragEnd?.(event));
|
|
84
|
+
},
|
|
85
|
+
onDrop: (event, helpers) => {
|
|
86
|
+
nextOptions.onDrop?.(event, helpers);
|
|
87
|
+
announce(nextOptions.announcements?.onDrop?.(event));
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function updateOverlayHost(update) {
|
|
92
|
+
if (disposed) {
|
|
93
|
+
removeOverlay();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (update.type === "mount" || update.type === "release") {
|
|
97
|
+
mountOverlay(update.state);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (update.type === "move") {
|
|
101
|
+
moveOverlay(update.dragState);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
removeOverlay();
|
|
105
|
+
}
|
|
106
|
+
function mountOverlay(overlayState) {
|
|
107
|
+
if (!currentOptions.dragOverlay) {
|
|
108
|
+
removeOverlay();
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const nextOverlayElement = currentOptions.dragOverlay({
|
|
112
|
+
dragState: overlayState.dragState,
|
|
113
|
+
phase: overlayState.phase,
|
|
114
|
+
finish: finishOverlay,
|
|
115
|
+
});
|
|
116
|
+
if (!nextOverlayElement) {
|
|
117
|
+
removeOverlay();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const wrapper = getOverlayWrapper(currentOptions.overlayRoot ?? document.body);
|
|
121
|
+
styleOverlayWrapper(wrapper, overlayState.dragState);
|
|
122
|
+
wrapper.replaceChildren(nextOverlayElement);
|
|
123
|
+
setOverlayElement(nextOverlayElement);
|
|
124
|
+
}
|
|
125
|
+
function moveOverlay(dragState) {
|
|
126
|
+
if (!overlayWrapper) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
overlayWrapper.style.transform = getOverlayTransform(dragState);
|
|
130
|
+
}
|
|
131
|
+
function styleOverlayWrapper(wrapper, dragState) {
|
|
132
|
+
wrapper.style.position = "fixed";
|
|
133
|
+
wrapper.style.left = `${dragState.sourceRect.left}px`;
|
|
134
|
+
wrapper.style.top = `${dragState.sourceRect.top}px`;
|
|
135
|
+
wrapper.style.width = `${dragState.sourceRect.width}px`;
|
|
136
|
+
wrapper.style.height = `${dragState.sourceRect.height}px`;
|
|
137
|
+
wrapper.style.transform = getOverlayTransform(dragState);
|
|
138
|
+
wrapper.style.pointerEvents = "none";
|
|
139
|
+
wrapper.style.zIndex = "9999";
|
|
140
|
+
}
|
|
141
|
+
function getOverlayTransform(dragState) {
|
|
142
|
+
const deltaX = dragState.pointerPosition.x - dragState.startPointerPosition.x;
|
|
143
|
+
const deltaY = dragState.pointerPosition.y - dragState.startPointerPosition.y;
|
|
144
|
+
return `translate3d(${deltaX}px, ${deltaY}px, 0)`;
|
|
145
|
+
}
|
|
146
|
+
function setOverlayElement(nextOverlayElement) {
|
|
147
|
+
disconnectOverlayResizeObserver();
|
|
148
|
+
overlayElement = nextOverlayElement;
|
|
149
|
+
measureOverlayElement();
|
|
150
|
+
if (typeof ResizeObserver === "undefined") {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
overlayResizeObserver = new ResizeObserver(() => {
|
|
154
|
+
measureOverlayElement();
|
|
155
|
+
});
|
|
156
|
+
overlayResizeObserver.observe(nextOverlayElement);
|
|
157
|
+
}
|
|
158
|
+
function measureOverlayElement() {
|
|
159
|
+
if (!overlayElement?.isConnected) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
runtime.setOverlayRect(rectToDragRect(overlayElement.getBoundingClientRect()));
|
|
163
|
+
}
|
|
164
|
+
function disconnectOverlayResizeObserver() {
|
|
165
|
+
overlayResizeObserver?.disconnect();
|
|
166
|
+
overlayResizeObserver = null;
|
|
167
|
+
}
|
|
168
|
+
function getOverlayWrapper(overlayRoot) {
|
|
169
|
+
if (!overlayWrapper) {
|
|
170
|
+
overlayWrapper = document.createElement("div");
|
|
171
|
+
}
|
|
172
|
+
if (overlayWrapper.parentElement !== overlayRoot) {
|
|
173
|
+
overlayRoot.append(overlayWrapper);
|
|
174
|
+
}
|
|
175
|
+
return overlayWrapper;
|
|
176
|
+
}
|
|
177
|
+
function finishOverlay() {
|
|
178
|
+
if (disposed) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
removeOverlay();
|
|
182
|
+
}
|
|
183
|
+
function removeOverlay() {
|
|
184
|
+
disconnectOverlayResizeObserver();
|
|
185
|
+
overlayElement = null;
|
|
186
|
+
runtime.setOverlayRect(null);
|
|
187
|
+
overlayWrapper?.remove();
|
|
188
|
+
overlayWrapper = null;
|
|
189
|
+
}
|
|
190
|
+
function syncLiveRegion() {
|
|
191
|
+
if (currentOptions.announcements) {
|
|
192
|
+
ensureLiveRegion();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
removeLiveRegion();
|
|
196
|
+
}
|
|
197
|
+
function ensureLiveRegion() {
|
|
198
|
+
if (!liveRegion) {
|
|
199
|
+
liveRegion = document.createElement("div");
|
|
200
|
+
liveRegion.setAttribute("aria-live", "polite");
|
|
201
|
+
liveRegion.setAttribute("aria-atomic", "true");
|
|
202
|
+
liveRegion.style.position = "absolute";
|
|
203
|
+
liveRegion.style.width = "1px";
|
|
204
|
+
liveRegion.style.height = "1px";
|
|
205
|
+
liveRegion.style.padding = "0";
|
|
206
|
+
liveRegion.style.margin = "-1px";
|
|
207
|
+
liveRegion.style.overflow = "hidden";
|
|
208
|
+
liveRegion.style.clip = "rect(0 0 0 0)";
|
|
209
|
+
liveRegion.style.whiteSpace = "nowrap";
|
|
210
|
+
liveRegion.style.border = "0";
|
|
211
|
+
document.body.append(liveRegion);
|
|
212
|
+
}
|
|
213
|
+
return liveRegion;
|
|
214
|
+
}
|
|
215
|
+
function announce(message) {
|
|
216
|
+
if (!message ||
|
|
217
|
+
message === lastAnnouncement ||
|
|
218
|
+
!currentOptions.announcements) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
lastAnnouncement = message;
|
|
222
|
+
ensureLiveRegion().textContent = message;
|
|
223
|
+
}
|
|
224
|
+
function removeLiveRegion() {
|
|
225
|
+
lastAnnouncement = null;
|
|
226
|
+
liveRegion?.remove();
|
|
227
|
+
liveRegion = null;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function hasDragOverlay(options) {
|
|
231
|
+
return options.dragOverlay !== undefined;
|
|
232
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DragController } from "../controller/create-drag-controller.js";
|
|
2
|
+
export type CreateDraggableInput = {
|
|
3
|
+
controller: DragController;
|
|
4
|
+
element: HTMLElement;
|
|
5
|
+
draggableId: string;
|
|
6
|
+
group?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function createDraggable(input: CreateDraggableInput): void;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { getControllerRuntime } from "../controller/controller-internals.js";
|
|
2
|
+
import { createDomDraggable } from "./create-draggable.js";
|
|
3
|
+
const defaultDraggableGroup = "default";
|
|
4
|
+
export function createDraggable(input) {
|
|
5
|
+
const elementRef = new WeakRef(input.element);
|
|
6
|
+
const runtime = getControllerRuntime(input.controller);
|
|
7
|
+
const behavior = createDomDraggable({
|
|
8
|
+
runtime,
|
|
9
|
+
draggableId: input.draggableId,
|
|
10
|
+
group: input.group ?? defaultDraggableGroup,
|
|
11
|
+
getElement: () => elementRef.deref() ?? null,
|
|
12
|
+
});
|
|
13
|
+
const previousTabIndex = input.element.getAttribute("tabindex");
|
|
14
|
+
let keyDownAttached = false;
|
|
15
|
+
let cleanedUp = false;
|
|
16
|
+
const onPointerDown = (event) => {
|
|
17
|
+
behavior.onPointerDown(event);
|
|
18
|
+
};
|
|
19
|
+
const onKeyDown = (event) => {
|
|
20
|
+
behavior.onKeyDown(event);
|
|
21
|
+
};
|
|
22
|
+
input.element.addEventListener("pointerdown", onPointerDown);
|
|
23
|
+
if (behavior.tabIndex !== undefined) {
|
|
24
|
+
input.element.tabIndex = behavior.tabIndex;
|
|
25
|
+
input.element.addEventListener("keydown", onKeyDown);
|
|
26
|
+
keyDownAttached = true;
|
|
27
|
+
}
|
|
28
|
+
const cleanup = () => {
|
|
29
|
+
if (cleanedUp) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
cleanedUp = true;
|
|
33
|
+
const element = elementRef.deref();
|
|
34
|
+
if (!element) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
element.removeEventListener("pointerdown", onPointerDown);
|
|
38
|
+
if (keyDownAttached) {
|
|
39
|
+
element.removeEventListener("keydown", onKeyDown);
|
|
40
|
+
if (previousTabIndex === null) {
|
|
41
|
+
element.removeAttribute("tabindex");
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
element.setAttribute("tabindex", previousTabIndex);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
runtime.registerBindingCleanup({
|
|
49
|
+
cleanup,
|
|
50
|
+
isConnected: () => elementRef.deref()?.isConnected === true,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { DragPoint } from "../geometry/rects.js";
|
|
2
|
+
export type DomDraggableRuntime = {
|
|
3
|
+
requestDragStart: (input: {
|
|
4
|
+
draggableId: string;
|
|
5
|
+
group: string;
|
|
6
|
+
element: HTMLElement;
|
|
7
|
+
pointerId: number;
|
|
8
|
+
pointerPosition: DragPoint;
|
|
9
|
+
}) => void;
|
|
10
|
+
isKeyboardDragEnabled: () => boolean;
|
|
11
|
+
handleSourceKeyboardKeyDown: (input: {
|
|
12
|
+
draggableId: string;
|
|
13
|
+
group: string;
|
|
14
|
+
element: HTMLElement;
|
|
15
|
+
key: string;
|
|
16
|
+
}) => boolean;
|
|
17
|
+
};
|
|
18
|
+
export type CreateDomDraggableInput = {
|
|
19
|
+
runtime: DomDraggableRuntime;
|
|
20
|
+
draggableId: string;
|
|
21
|
+
group: string;
|
|
22
|
+
getElement: () => HTMLElement | null;
|
|
23
|
+
keyboardDragEnabled?: boolean;
|
|
24
|
+
};
|
|
25
|
+
export type DomDraggablePointerDownEvent = {
|
|
26
|
+
target: EventTarget | null;
|
|
27
|
+
preventDefault: () => void;
|
|
28
|
+
stopPropagation: () => void;
|
|
29
|
+
pointerId: number;
|
|
30
|
+
button?: number;
|
|
31
|
+
isPrimary?: boolean;
|
|
32
|
+
clientX: number;
|
|
33
|
+
clientY: number;
|
|
34
|
+
};
|
|
35
|
+
export type DomDraggableKeyDownEvent = {
|
|
36
|
+
target: EventTarget | null;
|
|
37
|
+
preventDefault: () => void;
|
|
38
|
+
stopPropagation: () => void;
|
|
39
|
+
key: string;
|
|
40
|
+
};
|
|
41
|
+
export type DomDraggableBehavior = {
|
|
42
|
+
onPointerDown: (event: DomDraggablePointerDownEvent) => void;
|
|
43
|
+
onKeyDown: (event: DomDraggableKeyDownEvent) => void;
|
|
44
|
+
tabIndex: 0 | undefined;
|
|
45
|
+
};
|
|
46
|
+
export declare function createDomDraggable(input: CreateDomDraggableInput): DomDraggableBehavior;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { shouldHandleKeyboardDragFromTarget, shouldStartDragFromTarget, } from "../input/drag-handle.js";
|
|
2
|
+
export function createDomDraggable(input) {
|
|
3
|
+
return {
|
|
4
|
+
onPointerDown: (event) => {
|
|
5
|
+
const element = input.getElement();
|
|
6
|
+
if (!element) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (!shouldStartDragFromTarget({
|
|
10
|
+
draggableElement: element,
|
|
11
|
+
eventTarget: event.target,
|
|
12
|
+
})) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (event.isPrimary === false ||
|
|
16
|
+
(event.button !== undefined && event.button !== 0)) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
event.stopPropagation();
|
|
21
|
+
input.runtime.requestDragStart({
|
|
22
|
+
draggableId: input.draggableId,
|
|
23
|
+
group: input.group,
|
|
24
|
+
element,
|
|
25
|
+
pointerId: event.pointerId,
|
|
26
|
+
pointerPosition: {
|
|
27
|
+
x: event.clientX,
|
|
28
|
+
y: event.clientY,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
onKeyDown: (event) => {
|
|
33
|
+
const element = input.getElement();
|
|
34
|
+
if (!element ||
|
|
35
|
+
!shouldHandleKeyboardDragFromTarget({
|
|
36
|
+
draggableElement: element,
|
|
37
|
+
eventTarget: event.target,
|
|
38
|
+
})) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const handled = input.runtime.handleSourceKeyboardKeyDown({
|
|
42
|
+
draggableId: input.draggableId,
|
|
43
|
+
group: input.group,
|
|
44
|
+
element,
|
|
45
|
+
key: event.key,
|
|
46
|
+
});
|
|
47
|
+
if (!handled) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
event.stopPropagation();
|
|
52
|
+
},
|
|
53
|
+
tabIndex: (input.keyboardDragEnabled ?? input.runtime.isKeyboardDragEnabled())
|
|
54
|
+
? 0
|
|
55
|
+
: undefined,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DragController } from "../controller/create-drag-controller.js";
|
|
2
|
+
export type CreateDropContainerInput = {
|
|
3
|
+
controller: DragController;
|
|
4
|
+
element: HTMLElement;
|
|
5
|
+
containerId: string;
|
|
6
|
+
group?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function createDropContainer(input: CreateDropContainerInput): void;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { getControllerRuntime } from "../controller/controller-internals.js";
|
|
2
|
+
import { createDomDropContainer } from "./create-drop-container.js";
|
|
3
|
+
const defaultDropContainerGroup = "default";
|
|
4
|
+
export function createDropContainer(input) {
|
|
5
|
+
const elementRef = new WeakRef(input.element);
|
|
6
|
+
const runtime = getControllerRuntime(input.controller);
|
|
7
|
+
const behavior = createDomDropContainer({
|
|
8
|
+
runtime,
|
|
9
|
+
containerId: input.containerId,
|
|
10
|
+
group: input.group ?? defaultDropContainerGroup,
|
|
11
|
+
getElement: () => elementRef.deref() ?? null,
|
|
12
|
+
});
|
|
13
|
+
let cleanedUp = false;
|
|
14
|
+
const cleanup = () => {
|
|
15
|
+
if (cleanedUp) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
cleanedUp = true;
|
|
19
|
+
if (!elementRef.deref()) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
behavior.cleanup();
|
|
23
|
+
};
|
|
24
|
+
runtime.registerBindingCleanup({
|
|
25
|
+
cleanup,
|
|
26
|
+
isConnected: () => elementRef.deref()?.isConnected === true,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type DomDropContainerRuntime = {
|
|
2
|
+
registerDropContainer: (containerId: string, element: HTMLElement, group: string, options?: {
|
|
3
|
+
containerId?: string | null;
|
|
4
|
+
container?: boolean;
|
|
5
|
+
}) => void;
|
|
6
|
+
unregisterDropContainer: (containerId: string, element?: HTMLElement) => void;
|
|
7
|
+
};
|
|
8
|
+
export type CreateDomDropContainerInput = {
|
|
9
|
+
runtime: DomDropContainerRuntime;
|
|
10
|
+
containerId: string;
|
|
11
|
+
group: string;
|
|
12
|
+
getElement: () => HTMLElement | null;
|
|
13
|
+
};
|
|
14
|
+
export type DomDropContainerBehavior = {
|
|
15
|
+
setElement: (element: HTMLElement | null) => void;
|
|
16
|
+
cleanup: () => void;
|
|
17
|
+
};
|
|
18
|
+
export declare function createDomDropContainer(input: CreateDomDropContainerInput): DomDropContainerBehavior;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function createDomDropContainer(input) {
|
|
2
|
+
let registeredElementRef = null;
|
|
3
|
+
let registeredContainerId = null;
|
|
4
|
+
const cleanup = () => {
|
|
5
|
+
const registeredElement = registeredElementRef?.deref() ?? null;
|
|
6
|
+
if (registeredContainerId !== null) {
|
|
7
|
+
input.runtime.unregisterDropContainer(registeredContainerId, registeredElement ?? undefined);
|
|
8
|
+
}
|
|
9
|
+
registeredElementRef = null;
|
|
10
|
+
registeredContainerId = null;
|
|
11
|
+
};
|
|
12
|
+
const registerElement = (element) => {
|
|
13
|
+
input.runtime.registerDropContainer(input.containerId, element, input.group);
|
|
14
|
+
registeredElementRef = new WeakRef(element);
|
|
15
|
+
registeredContainerId = input.containerId;
|
|
16
|
+
};
|
|
17
|
+
const initialElement = input.getElement();
|
|
18
|
+
if (initialElement) {
|
|
19
|
+
registerElement(initialElement);
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
setElement: (element) => {
|
|
23
|
+
const registeredElement = registeredElementRef?.deref() ?? null;
|
|
24
|
+
if (element === registeredElement &&
|
|
25
|
+
registeredContainerId === input.containerId) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
cleanup();
|
|
29
|
+
if (!element) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
registerElement(element);
|
|
33
|
+
},
|
|
34
|
+
cleanup,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DragController } from "../controller/create-drag-controller.js";
|
|
2
|
+
export type CreateDroppableInput = {
|
|
3
|
+
controller: DragController;
|
|
4
|
+
element: HTMLElement;
|
|
5
|
+
dropTargetId: string;
|
|
6
|
+
containerId?: string | null;
|
|
7
|
+
group?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function createDroppable(input: CreateDroppableInput): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getControllerRuntime } from "../controller/controller-internals.js";
|
|
2
|
+
import { createDomDroppable } from "./create-droppable.js";
|
|
3
|
+
const defaultDroppableGroup = "default";
|
|
4
|
+
export function createDroppable(input) {
|
|
5
|
+
const elementRef = new WeakRef(input.element);
|
|
6
|
+
const runtime = getControllerRuntime(input.controller);
|
|
7
|
+
const behavior = createDomDroppable({
|
|
8
|
+
runtime,
|
|
9
|
+
dropTargetId: input.dropTargetId,
|
|
10
|
+
containerId: input.containerId ?? null,
|
|
11
|
+
group: input.group ?? defaultDroppableGroup,
|
|
12
|
+
});
|
|
13
|
+
let cleanedUp = false;
|
|
14
|
+
behavior.setElement(input.element);
|
|
15
|
+
const cleanup = () => {
|
|
16
|
+
if (cleanedUp) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
cleanedUp = true;
|
|
20
|
+
if (!elementRef.deref()) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
behavior.cleanup();
|
|
24
|
+
};
|
|
25
|
+
runtime.registerBindingCleanup({
|
|
26
|
+
cleanup,
|
|
27
|
+
isConnected: () => elementRef.deref()?.isConnected === true,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type DomDroppableRuntime = {
|
|
2
|
+
registerDropTarget: (dropTargetId: string, element: HTMLElement, group: string, options?: {
|
|
3
|
+
containerId?: string | null;
|
|
4
|
+
}) => void;
|
|
5
|
+
unregisterDropTarget: (dropTargetId: string, element?: HTMLElement) => void;
|
|
6
|
+
};
|
|
7
|
+
export type CreateDomDroppableInput = {
|
|
8
|
+
runtime: DomDroppableRuntime;
|
|
9
|
+
dropTargetId: string;
|
|
10
|
+
containerId?: string | null;
|
|
11
|
+
group: string;
|
|
12
|
+
};
|
|
13
|
+
export type DomDroppableBehavior = {
|
|
14
|
+
setElement: (element: HTMLElement | null) => void;
|
|
15
|
+
cleanup: () => void;
|
|
16
|
+
};
|
|
17
|
+
export declare function createDomDroppable(input: CreateDomDroppableInput): DomDroppableBehavior;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function createDomDroppable(input) {
|
|
2
|
+
let registeredElementRef = null;
|
|
3
|
+
let registeredTargetId = null;
|
|
4
|
+
const cleanup = () => {
|
|
5
|
+
const registeredElement = registeredElementRef?.deref() ?? null;
|
|
6
|
+
if (registeredTargetId !== null) {
|
|
7
|
+
input.runtime.unregisterDropTarget(registeredTargetId, registeredElement ?? undefined);
|
|
8
|
+
}
|
|
9
|
+
registeredElementRef = null;
|
|
10
|
+
registeredTargetId = null;
|
|
11
|
+
};
|
|
12
|
+
return {
|
|
13
|
+
setElement: (element) => {
|
|
14
|
+
const registeredElement = registeredElementRef?.deref() ?? null;
|
|
15
|
+
if (element === registeredElement &&
|
|
16
|
+
registeredTargetId === input.dropTargetId) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
cleanup();
|
|
20
|
+
if (!element) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
input.runtime.registerDropTarget(input.dropTargetId, element, input.group, {
|
|
24
|
+
containerId: input.containerId ?? null,
|
|
25
|
+
});
|
|
26
|
+
registeredElementRef = new WeakRef(element);
|
|
27
|
+
registeredTargetId = input.dropTargetId;
|
|
28
|
+
},
|
|
29
|
+
cleanup,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DragRect } from "./rects.js";
|
|
2
|
+
export declare function measureDomElement(element: HTMLElement): DragRect;
|
|
3
|
+
export declare function measureDocumentRect(element: HTMLElement): DragRect;
|
|
4
|
+
export declare function documentRectToViewportRect(rect: DragRect): DragRect;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { rectToDragRect } from "./rects.js";
|
|
2
|
+
export function measureDomElement(element) {
|
|
3
|
+
return rectToDragRect(element.getBoundingClientRect());
|
|
4
|
+
}
|
|
5
|
+
export function measureDocumentRect(element) {
|
|
6
|
+
const viewportRect = element.getBoundingClientRect();
|
|
7
|
+
const scrollX = window.scrollX;
|
|
8
|
+
const scrollY = window.scrollY;
|
|
9
|
+
return {
|
|
10
|
+
x: viewportRect.x + scrollX,
|
|
11
|
+
y: viewportRect.y + scrollY,
|
|
12
|
+
top: viewportRect.top + scrollY,
|
|
13
|
+
right: viewportRect.right + scrollX,
|
|
14
|
+
bottom: viewportRect.bottom + scrollY,
|
|
15
|
+
left: viewportRect.left + scrollX,
|
|
16
|
+
width: viewportRect.width,
|
|
17
|
+
height: viewportRect.height,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function documentRectToViewportRect(rect) {
|
|
21
|
+
const scrollX = window.scrollX;
|
|
22
|
+
const scrollY = window.scrollY;
|
|
23
|
+
return {
|
|
24
|
+
x: rect.x - scrollX,
|
|
25
|
+
y: rect.y - scrollY,
|
|
26
|
+
top: rect.top - scrollY,
|
|
27
|
+
right: rect.right - scrollX,
|
|
28
|
+
bottom: rect.bottom - scrollY,
|
|
29
|
+
left: rect.left - scrollX,
|
|
30
|
+
width: rect.width,
|
|
31
|
+
height: rect.height,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DragPoint, DragRect } from "./rects.js";
|
|
2
|
+
export type DragOverlayMeasurement = {
|
|
3
|
+
offsetX: number;
|
|
4
|
+
offsetY: number;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function getOverlayRect(input: {
|
|
9
|
+
sourceRect: DragRect;
|
|
10
|
+
initialPointerPosition: DragPoint;
|
|
11
|
+
pointerPosition: DragPoint;
|
|
12
|
+
overlayMeasurement?: DragOverlayMeasurement | null;
|
|
13
|
+
}): DragRect;
|
|
14
|
+
export declare function getOverlayMeasurement(input: {
|
|
15
|
+
sourceRect: DragRect;
|
|
16
|
+
initialPointerPosition: DragPoint;
|
|
17
|
+
pointerPosition: DragPoint;
|
|
18
|
+
overlayRect: DragRect;
|
|
19
|
+
}): DragOverlayMeasurement;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { translateRect } from "./rects.js";
|
|
2
|
+
export function getOverlayRect(input) {
|
|
3
|
+
const deltaX = input.pointerPosition.x - input.initialPointerPosition.x;
|
|
4
|
+
const deltaY = input.pointerPosition.y - input.initialPointerPosition.y;
|
|
5
|
+
if (input.overlayMeasurement) {
|
|
6
|
+
const left = input.sourceRect.left + deltaX + input.overlayMeasurement.offsetX;
|
|
7
|
+
const top = input.sourceRect.top + deltaY + input.overlayMeasurement.offsetY;
|
|
8
|
+
return {
|
|
9
|
+
x: left,
|
|
10
|
+
y: top,
|
|
11
|
+
left,
|
|
12
|
+
top,
|
|
13
|
+
width: input.overlayMeasurement.width,
|
|
14
|
+
height: input.overlayMeasurement.height,
|
|
15
|
+
right: left + input.overlayMeasurement.width,
|
|
16
|
+
bottom: top + input.overlayMeasurement.height,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return translateRect(input.sourceRect, deltaX, deltaY);
|
|
20
|
+
}
|
|
21
|
+
export function getOverlayMeasurement(input) {
|
|
22
|
+
const deltaX = input.pointerPosition.x - input.initialPointerPosition.x;
|
|
23
|
+
const deltaY = input.pointerPosition.y - input.initialPointerPosition.y;
|
|
24
|
+
const wrapperLeft = input.sourceRect.left + deltaX;
|
|
25
|
+
const wrapperTop = input.sourceRect.top + deltaY;
|
|
26
|
+
return {
|
|
27
|
+
offsetX: input.overlayRect.left - wrapperLeft,
|
|
28
|
+
offsetY: input.overlayRect.top - wrapperTop,
|
|
29
|
+
width: input.overlayRect.width,
|
|
30
|
+
height: input.overlayRect.height,
|
|
31
|
+
};
|
|
32
|
+
}
|