@mk-drag-and-drop/dom 0.4.4 → 0.4.5
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 +8 -1
- package/dist/modifiers/built-ins.js +18 -7
- package/dist/runtime/drag-runtime.js +2 -0
- package/dist/sortable/sortable-preview.d.ts +18 -0
- package/dist/sortable/sortable-preview.js +72 -18
- package/dist/sortable/sortable-registry.d.ts +8 -0
- package/dist/sortable/sortable-registry.js +7 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -374,6 +374,10 @@ sortable axis: above or left of midpoint places before, while below or right of
|
|
|
374
374
|
midpoint places after. Pointer-based targeting uses the pointer as that position;
|
|
375
375
|
rect-based targeting such as `centerToCenter` uses the overlay center.
|
|
376
376
|
|
|
377
|
+
A container-only preview keeps that cross-container entry state through active
|
|
378
|
+
recomputes until the first item target in the destination list receives a
|
|
379
|
+
before/after placement.
|
|
380
|
+
|
|
377
381
|
`placementBoundary` is used after a preview placement already has an established
|
|
378
382
|
movement direction to control same-target reversal and hysteresis thresholds. It
|
|
379
383
|
does not decide the initial side for cross-container sortable entry or delay the
|
|
@@ -388,7 +392,10 @@ Modifiers transform pointer movement before drag state updates. Built-ins:
|
|
|
388
392
|
- `restrictToContainer(resolver)`
|
|
389
393
|
|
|
390
394
|
`restrictToContainer` receives a resolver function with
|
|
391
|
-
`DragModifierSetupInput` and returns an `HTMLElement | null`.
|
|
395
|
+
`DragModifierSetupInput` and returns an `HTMLElement | null`. The resolver runs
|
|
396
|
+
at drag setup to choose the bounds element; the selected element's rect is
|
|
397
|
+
remeasured during movement so growing or shrinking containers keep constraining
|
|
398
|
+
against current bounds.
|
|
392
399
|
|
|
393
400
|
Custom modifiers can provide:
|
|
394
401
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { rectToDragRect } from "../geometry/rects.js";
|
|
2
|
+
const restrictToContainerElements = new WeakMap();
|
|
2
3
|
export function lockToXAxis() {
|
|
3
4
|
return {
|
|
4
5
|
transform: (input) => ({
|
|
@@ -19,33 +20,43 @@ export function restrictToContainer(getContainer) {
|
|
|
19
20
|
return {
|
|
20
21
|
setup: (input) => {
|
|
21
22
|
const container = getContainer(input);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
if (!container) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const rect = rectToDragRect(container.getBoundingClientRect());
|
|
27
|
+
restrictToContainerElements.set(rect, container);
|
|
28
|
+
return rect;
|
|
25
29
|
},
|
|
26
30
|
transform: (input) => {
|
|
27
31
|
if (input.state === null) {
|
|
28
32
|
return input.pointerPosition;
|
|
29
33
|
}
|
|
34
|
+
const containerRect = getRestrictToContainerRect(input.state);
|
|
30
35
|
return {
|
|
31
36
|
x: clampPointerAxis({
|
|
32
37
|
pointerPosition: input.pointerPosition.x,
|
|
33
38
|
overlayStart: input.overlayRect.left,
|
|
34
39
|
overlayEnd: input.overlayRect.right,
|
|
35
|
-
containerStart:
|
|
36
|
-
containerEnd:
|
|
40
|
+
containerStart: containerRect.left,
|
|
41
|
+
containerEnd: containerRect.right,
|
|
37
42
|
}),
|
|
38
43
|
y: clampPointerAxis({
|
|
39
44
|
pointerPosition: input.pointerPosition.y,
|
|
40
45
|
overlayStart: input.overlayRect.top,
|
|
41
46
|
overlayEnd: input.overlayRect.bottom,
|
|
42
|
-
containerStart:
|
|
43
|
-
containerEnd:
|
|
47
|
+
containerStart: containerRect.top,
|
|
48
|
+
containerEnd: containerRect.bottom,
|
|
44
49
|
}),
|
|
45
50
|
};
|
|
46
51
|
},
|
|
47
52
|
};
|
|
48
53
|
}
|
|
54
|
+
function getRestrictToContainerRect(state) {
|
|
55
|
+
const container = restrictToContainerElements.get(state);
|
|
56
|
+
return container
|
|
57
|
+
? rectToDragRect(container.getBoundingClientRect())
|
|
58
|
+
: state;
|
|
59
|
+
}
|
|
49
60
|
function clampPointerAxis(input) {
|
|
50
61
|
const overlaySize = input.overlayEnd - input.overlayStart;
|
|
51
62
|
const containerSize = input.containerEnd - input.containerStart;
|
|
@@ -235,6 +235,7 @@ export class DragRuntime {
|
|
|
235
235
|
this.notifyDragUpdate(updateEvent, {
|
|
236
236
|
...updateEvent,
|
|
237
237
|
placementPosition: sortablePlacementPosition,
|
|
238
|
+
movementPosition: rawPointerPosition,
|
|
238
239
|
});
|
|
239
240
|
}
|
|
240
241
|
updatePointerNowOrRelease(rawPointerPosition) {
|
|
@@ -465,6 +466,7 @@ export class DragRuntime {
|
|
|
465
466
|
pointerPosition: effectivePointerPosition,
|
|
466
467
|
overlayRect: startOverlayRect,
|
|
467
468
|
}),
|
|
469
|
+
movementPosition: rawPointerPosition,
|
|
468
470
|
};
|
|
469
471
|
this.notifyDragStartSubscriptions(dragStartSubscriptionEvent);
|
|
470
472
|
this.updateOverlayHost({
|
|
@@ -7,6 +7,10 @@ export type SortablePointerMovementState = {
|
|
|
7
7
|
x: number;
|
|
8
8
|
y: number;
|
|
9
9
|
};
|
|
10
|
+
previousPlacementPosition?: {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
};
|
|
10
14
|
};
|
|
11
15
|
export type SortablePreviewPlacementState = {
|
|
12
16
|
activeDropTargetId: string | null;
|
|
@@ -26,10 +30,16 @@ export declare function isSortablePreviewTarget(input: {
|
|
|
26
30
|
export declare function initializeSortablePointerMovement(registry: SortableRegistry, pointerPosition: {
|
|
27
31
|
x: number;
|
|
28
32
|
y: number;
|
|
33
|
+
}, placementPosition?: {
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
29
36
|
}): void;
|
|
30
37
|
export declare function updateSortablePointerMovement(registry: SortableRegistry, pointerPosition: {
|
|
31
38
|
x: number;
|
|
32
39
|
y: number;
|
|
40
|
+
}, placementPosition?: {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
33
43
|
}): void;
|
|
34
44
|
export declare function clearSortablePointerMovement(registry: SortableRegistry): void;
|
|
35
45
|
export declare function clearSortablePreviewPlacement(registry: SortableRegistry): void;
|
|
@@ -42,6 +52,10 @@ export declare function moveSortablePreview(input: {
|
|
|
42
52
|
x: number;
|
|
43
53
|
y: number;
|
|
44
54
|
};
|
|
55
|
+
movementPosition: {
|
|
56
|
+
x: number;
|
|
57
|
+
y: number;
|
|
58
|
+
};
|
|
45
59
|
options: NormalizedSortableOptions;
|
|
46
60
|
}): void;
|
|
47
61
|
export declare function getSortablePreviewPlacement(input: {
|
|
@@ -51,6 +65,10 @@ export declare function getSortablePreviewPlacement(input: {
|
|
|
51
65
|
x: number;
|
|
52
66
|
y: number;
|
|
53
67
|
};
|
|
68
|
+
movementPosition?: {
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
};
|
|
54
72
|
movement: SortablePointerMovementState | null;
|
|
55
73
|
previewPlacement: SortablePreviewPlacementState | null;
|
|
56
74
|
options: NormalizedSortableOptions;
|
|
@@ -17,19 +17,21 @@ export function snapshotSortableElement(registry, draggableId) {
|
|
|
17
17
|
export function isSortablePreviewTarget(input) {
|
|
18
18
|
return input.activeDropTargetId !== null;
|
|
19
19
|
}
|
|
20
|
-
export function initializeSortablePointerMovement(registry, pointerPosition) {
|
|
20
|
+
export function initializeSortablePointerMovement(registry, pointerPosition, placementPosition = pointerPosition) {
|
|
21
21
|
registry.activeDrag.pointerMovement = {
|
|
22
22
|
previousPointerPosition: { ...pointerPosition },
|
|
23
|
+
previousPlacementPosition: { ...placementPosition },
|
|
23
24
|
};
|
|
24
25
|
registry.activeDrag.previewPlacement = null;
|
|
25
26
|
}
|
|
26
|
-
export function updateSortablePointerMovement(registry, pointerPosition) {
|
|
27
|
+
export function updateSortablePointerMovement(registry, pointerPosition, placementPosition = pointerPosition) {
|
|
27
28
|
const movement = registry.activeDrag.pointerMovement;
|
|
28
29
|
if (!movement) {
|
|
29
|
-
initializeSortablePointerMovement(registry, pointerPosition);
|
|
30
|
+
initializeSortablePointerMovement(registry, pointerPosition, placementPosition);
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
33
|
movement.previousPointerPosition = { ...pointerPosition };
|
|
34
|
+
movement.previousPlacementPosition = { ...placementPosition };
|
|
33
35
|
}
|
|
34
36
|
export function clearSortablePointerMovement(registry) {
|
|
35
37
|
registry.activeDrag.pointerMovement = null;
|
|
@@ -75,10 +77,17 @@ export function moveSortablePreview(input) {
|
|
|
75
77
|
addRegistrationContainerId(measurementIds, draggedRegistration);
|
|
76
78
|
addRegistrationContainerId(measurementIds, target);
|
|
77
79
|
if (target.capabilities.container) {
|
|
80
|
+
const sourceListElement = draggedElement.parentElement;
|
|
78
81
|
measurementIds.add(target.id);
|
|
79
82
|
const previousListElement = input.registry.activeDrag.previewPlacement?.containerElement ?? null;
|
|
80
83
|
const pendingMidpointInitialPlacement = draggedElement.parentElement !== target.element ||
|
|
81
84
|
(previousListElement !== null && previousListElement !== target.element);
|
|
85
|
+
if (!moveSortablePreviewIntoContainer({
|
|
86
|
+
draggedElement,
|
|
87
|
+
targetElement: target.element,
|
|
88
|
+
})) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
82
91
|
setSortablePreviewPlacementState({
|
|
83
92
|
registry: input.registry,
|
|
84
93
|
activeDropTargetId: target.id,
|
|
@@ -87,17 +96,18 @@ export function moveSortablePreview(input) {
|
|
|
87
96
|
pendingMidpointInitialPlacement,
|
|
88
97
|
movementDirection: getCurrentAxisMovementDirection({
|
|
89
98
|
placementPosition: input.placementPosition,
|
|
99
|
+
movementPosition: input.movementPosition,
|
|
90
100
|
movement: input.registry.activeDrag.pointerMovement,
|
|
91
101
|
axis: input.options.axis,
|
|
92
102
|
}),
|
|
93
103
|
});
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
104
|
+
if (sourceListElement && sourceListElement !== target.element) {
|
|
105
|
+
addSortableMeasurementIdsInContainer(measurementIds, input.registry, sourceListElement);
|
|
106
|
+
addSortableMeasurementIdsInContainer(measurementIds, input.registry, target.element);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
addSortableMeasurementIdsAroundElement(measurementIds, input.registry, draggedElement);
|
|
99
110
|
}
|
|
100
|
-
addSortableMeasurementIdsAroundElement(measurementIds, input.registry, draggedElement);
|
|
101
111
|
refreshSortableDropTargetMeasurements({
|
|
102
112
|
registry: input.registry,
|
|
103
113
|
runtime: input.runtime,
|
|
@@ -108,6 +118,7 @@ export function moveSortablePreview(input) {
|
|
|
108
118
|
}
|
|
109
119
|
const targetElement = target.element;
|
|
110
120
|
const listElement = targetElement.parentElement;
|
|
121
|
+
const sourceListElement = draggedElement.parentElement;
|
|
111
122
|
if (!listElement) {
|
|
112
123
|
return;
|
|
113
124
|
}
|
|
@@ -129,6 +140,7 @@ export function moveSortablePreview(input) {
|
|
|
129
140
|
activeDropTargetId,
|
|
130
141
|
targetElement,
|
|
131
142
|
placementPosition: input.placementPosition,
|
|
143
|
+
movementPosition: input.movementPosition,
|
|
132
144
|
movement: input.registry.activeDrag.pointerMovement,
|
|
133
145
|
previewPlacement: input.registry.activeDrag.previewPlacement,
|
|
134
146
|
options: input.options,
|
|
@@ -156,8 +168,14 @@ export function moveSortablePreview(input) {
|
|
|
156
168
|
else {
|
|
157
169
|
targetElement.before(draggedElement);
|
|
158
170
|
}
|
|
159
|
-
|
|
160
|
-
|
|
171
|
+
if (sourceListElement && sourceListElement !== listElement) {
|
|
172
|
+
addSortableMeasurementIdsInContainer(measurementIds, input.registry, sourceListElement);
|
|
173
|
+
addSortableMeasurementIdsInContainer(measurementIds, input.registry, listElement);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
addSortableMeasurementIdsAroundElement(measurementIds, input.registry, draggedElement);
|
|
177
|
+
addSortableMeasurementIdsAroundElement(measurementIds, input.registry, targetElement);
|
|
178
|
+
}
|
|
161
179
|
refreshSortableDropTargetMeasurements({
|
|
162
180
|
registry: input.registry,
|
|
163
181
|
runtime: input.runtime,
|
|
@@ -171,14 +189,14 @@ export function getSortablePreviewPlacement(input) {
|
|
|
171
189
|
const targetRect = input.targetElement.getBoundingClientRect();
|
|
172
190
|
const currentDirection = getCurrentAxisMovementDirection({
|
|
173
191
|
placementPosition: input.placementPosition,
|
|
192
|
+
movementPosition: input.movementPosition,
|
|
174
193
|
movement: input.movement,
|
|
175
194
|
axis,
|
|
176
195
|
});
|
|
177
196
|
const previousPlacement = input.previewPlacement;
|
|
178
197
|
if (!previousPlacement ||
|
|
179
198
|
previousPlacement.activeDropTargetId !== input.activeDropTargetId ||
|
|
180
|
-
previousPlacement.placement === null
|
|
181
|
-
previousPlacement.movementDirection === "none") {
|
|
199
|
+
previousPlacement.placement === null) {
|
|
182
200
|
if (input.useMidpointInitialPlacement) {
|
|
183
201
|
const placement = getPlacementSideFromTargetMidpoint({
|
|
184
202
|
axis,
|
|
@@ -200,6 +218,32 @@ export function getSortablePreviewPlacement(input) {
|
|
|
200
218
|
movementDirection: currentDirection,
|
|
201
219
|
};
|
|
202
220
|
}
|
|
221
|
+
if (previousPlacement.movementDirection === "none") {
|
|
222
|
+
if (currentDirection === "none") {
|
|
223
|
+
return {
|
|
224
|
+
placement: previousPlacement.placement,
|
|
225
|
+
movementDirection: previousPlacement.movementDirection,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const boundary = getPlacementBoundary({
|
|
229
|
+
targetRect,
|
|
230
|
+
axis,
|
|
231
|
+
direction: currentDirection,
|
|
232
|
+
startRatio: input.options.placementBoundary.start,
|
|
233
|
+
endRatio: input.options.placementBoundary.end,
|
|
234
|
+
});
|
|
235
|
+
const reversalPlacement = getPlacementSideFromBoundary(axisPosition, boundary);
|
|
236
|
+
if (reversalPlacement === previousPlacement.placement) {
|
|
237
|
+
return {
|
|
238
|
+
placement: previousPlacement.placement,
|
|
239
|
+
movementDirection: previousPlacement.movementDirection,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
placement: reversalPlacement,
|
|
244
|
+
movementDirection: currentDirection,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
203
247
|
if (currentDirection === "none" ||
|
|
204
248
|
currentDirection === previousPlacement.movementDirection) {
|
|
205
249
|
return {
|
|
@@ -257,11 +301,16 @@ function getPlacementSideFromTargetMidpoint(input) {
|
|
|
257
301
|
return input.axisPosition < midpoint ? "before" : "after";
|
|
258
302
|
}
|
|
259
303
|
function getCurrentAxisMovementDirection(input) {
|
|
260
|
-
const
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
304
|
+
const movementPosition = input.movementPosition ?? input.placementPosition;
|
|
305
|
+
const previousMovementPosition = input.movement?.previousPointerPosition ?? movementPosition;
|
|
306
|
+
const inputDirection = getAxisMovementDirection(getAxisPosition(previousMovementPosition, input.axis), getAxisPosition(movementPosition, input.axis));
|
|
307
|
+
if (inputDirection === "none") {
|
|
308
|
+
return "none";
|
|
309
|
+
}
|
|
310
|
+
const previousPlacementPosition = input.movement?.previousPlacementPosition ??
|
|
311
|
+
input.movement?.previousPointerPosition ??
|
|
312
|
+
input.placementPosition;
|
|
313
|
+
return getAxisMovementDirection(getAxisPosition(previousPlacementPosition, input.axis), getAxisPosition(input.placementPosition, input.axis));
|
|
265
314
|
}
|
|
266
315
|
export function isSortablePreviewAlreadyPlaced(input) {
|
|
267
316
|
return input.placement === "before"
|
|
@@ -308,6 +357,11 @@ function addSortableMeasurementIdsAroundElement(dropTargetIds, registry, element
|
|
|
308
357
|
addSortableMeasurementId(dropTargetIds, registry, element.previousElementSibling);
|
|
309
358
|
addSortableMeasurementId(dropTargetIds, registry, element.nextElementSibling);
|
|
310
359
|
}
|
|
360
|
+
function addSortableMeasurementIdsInContainer(dropTargetIds, registry, containerElement) {
|
|
361
|
+
for (let index = 0; index < containerElement.children.length; index += 1) {
|
|
362
|
+
addSortableMeasurementId(dropTargetIds, registry, containerElement.children.item(index));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
311
365
|
function addSortableMeasurementId(dropTargetIds, registry, element) {
|
|
312
366
|
if (!(element instanceof HTMLElement)) {
|
|
313
367
|
return;
|
|
@@ -32,6 +32,10 @@ export type DomSortableRuntime = DomDraggableRuntime & {
|
|
|
32
32
|
x: number;
|
|
33
33
|
y: number;
|
|
34
34
|
};
|
|
35
|
+
movementPosition?: {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
};
|
|
35
39
|
}) => void;
|
|
36
40
|
onDragUpdate?: (event: {
|
|
37
41
|
draggableId: string;
|
|
@@ -44,6 +48,10 @@ export type DomSortableRuntime = DomDraggableRuntime & {
|
|
|
44
48
|
x: number;
|
|
45
49
|
y: number;
|
|
46
50
|
};
|
|
51
|
+
movementPosition?: {
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
};
|
|
47
55
|
activeDropTargetId: string | null;
|
|
48
56
|
previousDropTargetId: string | null;
|
|
49
57
|
}) => void;
|
|
@@ -22,26 +22,30 @@ export function getSortableRegistry(runtime) {
|
|
|
22
22
|
};
|
|
23
23
|
runtime.subscribe({
|
|
24
24
|
onDragStart: (event) => {
|
|
25
|
-
|
|
25
|
+
const placementPosition = event.placementPosition ?? event.pointerPosition;
|
|
26
|
+
initializeSortablePointerMovement(registry, event.movementPosition ?? placementPosition, placementPosition);
|
|
26
27
|
snapshotSortableElement(registry, event.draggableId);
|
|
27
28
|
},
|
|
28
29
|
onDragUpdate: (event) => {
|
|
29
30
|
if (isSortablePreviewTarget({
|
|
30
31
|
activeDropTargetId: event.activeDropTargetId,
|
|
31
32
|
})) {
|
|
33
|
+
const placementPosition = event.placementPosition ?? event.pointerPosition;
|
|
32
34
|
moveSortablePreview({
|
|
33
35
|
registry,
|
|
34
36
|
runtime,
|
|
35
37
|
draggedDraggableId: event.draggableId,
|
|
36
38
|
activeDropTargetId: event.activeDropTargetId,
|
|
37
|
-
placementPosition
|
|
39
|
+
placementPosition,
|
|
40
|
+
movementPosition: event.movementPosition ?? placementPosition,
|
|
38
41
|
options: getSortableOptions(registry, event.draggableId),
|
|
39
42
|
});
|
|
40
43
|
}
|
|
41
44
|
else {
|
|
42
45
|
clearSortablePreviewPlacement(registry);
|
|
43
46
|
}
|
|
44
|
-
|
|
47
|
+
const placementPosition = event.placementPosition ?? event.pointerPosition;
|
|
48
|
+
updateSortablePointerMovement(registry, event.movementPosition ?? placementPosition, placementPosition);
|
|
45
49
|
},
|
|
46
50
|
onDragEnd: (event) => {
|
|
47
51
|
releaseSortableActiveDragState(registry, event.draggableId);
|
package/package.json
CHANGED