@mk-drag-and-drop/react 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -34
- package/dist/drag-context.d.ts +3 -2
- package/dist/drag-overlay.d.ts +10 -3
- package/dist/drag-overlay.js +20 -10
- package/dist/drag-provider.d.ts +3 -3
- package/dist/drag-provider.js +37 -22
- package/dist/hooks/use-drop-container.js +2 -2
- package/dist/hooks/use-droppable.js +11 -1
- package/dist/hooks/use-remeasure-overlay.d.ts +1 -0
- package/dist/hooks/use-remeasure-overlay.js +12 -0
- package/dist/hooks/use-sortable.js +9 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ callbacks, and optional announcements.
|
|
|
22
22
|
The React package wraps `@mk-drag-and-drop/dom`.
|
|
23
23
|
|
|
24
24
|
`DragProvider` creates and configures the DOM runtime through the package
|
|
25
|
-
integration
|
|
25
|
+
integration scope.
|
|
26
26
|
Hooks such as `useDraggable`, `useDroppable`, `useSortable`, and
|
|
27
27
|
`useDropContainer` register DOM nodes with that runtime through React refs.
|
|
28
28
|
React hooks are adapters over DOM behavior, not a separate implementation.
|
|
@@ -52,11 +52,13 @@ The React root export includes:
|
|
|
52
52
|
- `useSortable`, `UseSortableOptions`, `UseSortableResult`
|
|
53
53
|
- `useDragHandle`, `UseDragHandleResult`
|
|
54
54
|
- `useRemeasureDropTargets`
|
|
55
|
+
- `useRemeasureOverlay`
|
|
55
56
|
- `composeRefs`
|
|
56
57
|
- React-friendly `restrictToContainer`, `ReactRestrictToContainerInput`
|
|
57
58
|
- DOM targeting helpers, modifier helpers, lifecycle/source/result types,
|
|
58
59
|
sortable placement types, keyboard/pointer configuration types, and geometry
|
|
59
60
|
types re-exported from the DOM package
|
|
61
|
+
- `OverlayReleaseMode`
|
|
60
62
|
- `DragState` and `DragOverlayPhase` from the DOM integration layer
|
|
61
63
|
|
|
62
64
|
## Core Mental Model
|
|
@@ -72,11 +74,28 @@ The package reports drag/drop operations. Your app owns the data and the final
|
|
|
72
74
|
UI commit. React state is one common way to commit the result, but it is not
|
|
73
75
|
required by the package.
|
|
74
76
|
|
|
77
|
+
## Lifetime Model
|
|
78
|
+
|
|
79
|
+
`DragProvider` is a drag-and-drop scope for a React subtree. It does not expose
|
|
80
|
+
runtime lifecycle controls, and application code does not dispose of the
|
|
81
|
+
runtime. The internal runtime is normal JavaScript memory and is released by
|
|
82
|
+
garbage collection when unreachable.
|
|
83
|
+
|
|
84
|
+
Hooks are adapters over the DOM behavior layer. They register committed DOM
|
|
85
|
+
nodes through refs/effects, update those registrations when ids, groups, or
|
|
86
|
+
container ids change, and unregister current nodes on ref/effect unmount.
|
|
87
|
+
Stale disconnected DOM registrations are still pruned by the shared DOM runtime
|
|
88
|
+
while the scope remains alive.
|
|
89
|
+
|
|
90
|
+
Pending pointer activation resources and active drag resources belong to the
|
|
91
|
+
input/drag lifecycle. Provider unmount releases active resources through the
|
|
92
|
+
same active-drag resource path used by DOM drag cancellation; it is not a
|
|
93
|
+
terminal runtime destructor. React StrictMode replay is supported for
|
|
94
|
+
draggable, droppable, sortable, overlay, callback, and announcement behavior.
|
|
95
|
+
|
|
75
96
|
## Installation
|
|
76
97
|
|
|
77
|
-
|
|
78
|
-
React and DOM packages from your workspace or package registry when they are
|
|
79
|
-
available:
|
|
98
|
+
Install the React and DOM packages from your workspace or package registry:
|
|
80
99
|
|
|
81
100
|
- `@mk-drag-and-drop/react`
|
|
82
101
|
- `@mk-drag-and-drop/dom`
|
|
@@ -153,9 +172,11 @@ Real apps own the markup, layout, styling, state updates, and persistence.
|
|
|
153
172
|
|
|
154
173
|
## DragProvider
|
|
155
174
|
|
|
156
|
-
`DragProvider`
|
|
157
|
-
|
|
158
|
-
|
|
175
|
+
`DragProvider` creates a DOM runtime scope for the React subtree, configures it
|
|
176
|
+
from props, exposes it through React context, and renders an optional drag
|
|
177
|
+
overlay. Provider effect cleanup is not a runtime destructor; runtime objects
|
|
178
|
+
are ordinary JavaScript objects and active drag resources are released by drag
|
|
179
|
+
drop/cancel paths.
|
|
159
180
|
|
|
160
181
|
Key props:
|
|
161
182
|
|
|
@@ -171,8 +192,8 @@ Key props:
|
|
|
171
192
|
- `pointerConfiguration`: configures pointer activation delay and distance.
|
|
172
193
|
- `keyboardConfiguration`: enables and configures keyboard drag commands.
|
|
173
194
|
- `announcements`: optional callbacks that return live-region messages.
|
|
174
|
-
- `
|
|
175
|
-
|
|
195
|
+
- `overlayRelease`: `"auto"` removes overlays on drag end; `"manual"` keeps a
|
|
196
|
+
released overlay until its `removeOverlay` callback is called.
|
|
176
197
|
|
|
177
198
|
Callbacks receive operation information and helper methods from the runtime.
|
|
178
199
|
Use those callbacks to commit app data. Do not mutate package internals.
|
|
@@ -202,6 +223,7 @@ type DragUpdateEvent = {
|
|
|
202
223
|
draggableId: string;
|
|
203
224
|
source: DragSource;
|
|
204
225
|
pointerPosition: DragPoint;
|
|
226
|
+
overlayRect: DragRect | null;
|
|
205
227
|
activeDropTargetId: string | null;
|
|
206
228
|
previousDropTargetId: string | null;
|
|
207
229
|
};
|
|
@@ -211,6 +233,7 @@ type DragEndEvent = {
|
|
|
211
233
|
source: DragSource;
|
|
212
234
|
result: DragEndResult;
|
|
213
235
|
dropTargetId: string | null;
|
|
236
|
+
overlayRect: DragRect | null;
|
|
214
237
|
};
|
|
215
238
|
|
|
216
239
|
type DropEvent = {
|
|
@@ -221,6 +244,15 @@ type DropEvent = {
|
|
|
221
244
|
};
|
|
222
245
|
```
|
|
223
246
|
|
|
247
|
+
`DragUpdateEvent.overlayRect` is the current viewport-space rectangle for the
|
|
248
|
+
drag overlay. `DragEndEvent.overlayRect` is the final viewport-space rectangle
|
|
249
|
+
at the moment the drag ends. React receives these DOM lifecycle event types
|
|
250
|
+
unchanged. The field is `null` when no drag overlay is configured. When an
|
|
251
|
+
overlay exists, it is based on cached overlay measurement plus modified pointer
|
|
252
|
+
movement, with the translated source rect as the fallback before overlay content
|
|
253
|
+
has been measured. React does not add overlay DOM measurement on every pointer
|
|
254
|
+
move.
|
|
255
|
+
|
|
224
256
|
`onDrop` only runs for a valid successful drop. `onDragEnd.result` distinguishes
|
|
225
257
|
successful drops, normal releases with no target, stale/invalid targets, and
|
|
226
258
|
cancellations such as Escape or pointercancel. Plain drops are id-only.
|
|
@@ -365,9 +397,26 @@ requestAnimationFrame(() => {
|
|
|
365
397
|
|
|
366
398
|
It accepts no argument, a target id, an array of target ids, or `{ group }`.
|
|
367
399
|
Use it intentionally for expand/collapse, grouped trees, or drag-state layout
|
|
368
|
-
changes. It is not needed for normal
|
|
369
|
-
Sortable preview movement does not automatically remeasure a
|
|
370
|
-
function when an app-owned layout change needs to affect
|
|
400
|
+
changes. It is not needed for normal resource release and should not run on
|
|
401
|
+
every render. Sortable preview movement does not automatically remeasure a
|
|
402
|
+
group; call this function when an app-owned layout change needs to affect
|
|
403
|
+
targeting.
|
|
404
|
+
|
|
405
|
+
### useRemeasureOverlay
|
|
406
|
+
|
|
407
|
+
`useRemeasureOverlay` returns a function for manually refreshing the current
|
|
408
|
+
overlay measurement. It must be used inside `DragProvider`. Calling the returned
|
|
409
|
+
function with no mounted overlay is safe and does nothing.
|
|
410
|
+
|
|
411
|
+
```tsx
|
|
412
|
+
const remeasureOverlay = useRemeasureOverlay();
|
|
413
|
+
|
|
414
|
+
remeasureOverlay();
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Normal overlay size changes are observed automatically with `ResizeObserver`
|
|
418
|
+
when available. Use this hook when the app knows visual geometry changed outside
|
|
419
|
+
that automatic path, such as after a CSS transform or transition.
|
|
371
420
|
|
|
372
421
|
## Sortable Behavior
|
|
373
422
|
|
|
@@ -408,8 +457,10 @@ shape.
|
|
|
408
457
|
|
|
409
458
|
```tsx
|
|
410
459
|
<DragProvider
|
|
411
|
-
dragOverlay={({ dragState }) => (
|
|
412
|
-
<div className="dragOverlay"
|
|
460
|
+
dragOverlay={({ dragState, remeasureOverlay }) => (
|
|
461
|
+
<div className="dragOverlay" onTransitionEnd={remeasureOverlay}>
|
|
462
|
+
{dragState.draggableId}
|
|
463
|
+
</div>
|
|
413
464
|
)}
|
|
414
465
|
>
|
|
415
466
|
{children}
|
|
@@ -420,19 +471,37 @@ The overlay input includes:
|
|
|
420
471
|
|
|
421
472
|
- `dragState`: item id, group, source rect, start pointer, and current pointer.
|
|
422
473
|
- `phase`: `"dragging"` or `"released"`.
|
|
423
|
-
- `
|
|
474
|
+
- `remeasureOverlay`: manually refreshes the cached overlay measurement.
|
|
475
|
+
- `removeOverlay`: present only for `"released"` overlays in manual release
|
|
476
|
+
mode; call it when app-owned release animation should remove the overlay.
|
|
424
477
|
|
|
425
478
|
Overlay rendering is app-owned. `DragProvider` mounts overlay content for the
|
|
426
|
-
dragging phase
|
|
427
|
-
|
|
428
|
-
|
|
479
|
+
dragging phase. Default `overlayRelease: "auto"` removes the overlay
|
|
480
|
+
immediately on drag end. Manual release replaces it once for the released phase
|
|
481
|
+
and leaves removal to `removeOverlay`. Pointer movement updates the
|
|
482
|
+
package-owned overlay host imperatively and does not call `dragOverlay` by
|
|
483
|
+
default.
|
|
484
|
+
|
|
485
|
+
`removeOverlay` is idempotent and belongs to overlay release only. It is not a
|
|
486
|
+
provider, controller, or runtime teardown API.
|
|
429
487
|
|
|
430
|
-
The package owns overlay hosting, movement,
|
|
488
|
+
The package owns overlay hosting, movement, release, and measurement for
|
|
431
489
|
targeting and modifiers. It measures overlay content on mount/replacement and
|
|
432
|
-
uses `ResizeObserver` when available to remeasure content size changes.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
490
|
+
uses `ResizeObserver` when available to remeasure content size changes. The
|
|
491
|
+
React package mirrors DOM behavior: overlay movement remains package-managed and
|
|
492
|
+
imperative, so pointer movement should not require React state updates on every
|
|
493
|
+
move.
|
|
494
|
+
|
|
495
|
+
Use the overlay input `remeasureOverlay` helper when overlay-local UI knows its
|
|
496
|
+
visual bounds changed. It uses the same provider/runtime path as
|
|
497
|
+
`useRemeasureOverlay` and avoids reaching into internal runtime or provider
|
|
498
|
+
state. CSS transforms can change visual bounds without necessarily triggering
|
|
499
|
+
`ResizeObserver`.
|
|
500
|
+
|
|
501
|
+
Dynamic overlay content should use overlay-local state, component effects,
|
|
502
|
+
lifecycle callbacks feeding a subscription/external store, or another app-owned
|
|
503
|
+
update path rather than relying on `dragOverlay` being called for every pointer
|
|
504
|
+
move.
|
|
436
505
|
|
|
437
506
|
## Targeting And Modifiers
|
|
438
507
|
|
|
@@ -451,7 +520,10 @@ The React package re-exports targeting helpers such as `pointerToCenter`,
|
|
|
451
520
|
`maxPointerDistanceToRect` / `maxOverlayCenterDistanceToRect`, plus modifier
|
|
452
521
|
helpers. Sortable placement remains separate from targeting: the configured
|
|
453
522
|
targeting algorithm chooses the active target, and sortable only chooses
|
|
454
|
-
before/after placement relative to that target.
|
|
523
|
+
before/after placement relative to that target. For sortable groups, the shared
|
|
524
|
+
DOM registry may narrow measured candidates to relevant sortable item, neighbor,
|
|
525
|
+
or container entries before the targeting algorithm runs; the algorithm still
|
|
526
|
+
chooses from that narrowed measured list.
|
|
455
527
|
|
|
456
528
|
- `lockToXAxis()`
|
|
457
529
|
- `lockToYAxis()`
|
|
@@ -507,19 +579,20 @@ Accessibility still depends on the app's markup and product behavior:
|
|
|
507
579
|
- Do not assume this package alone provides complete screen-reader drag-and-drop
|
|
508
580
|
support for your domain.
|
|
509
581
|
|
|
510
|
-
##
|
|
582
|
+
## Lifetime And Effects
|
|
511
583
|
|
|
512
584
|
Hooks attach input props and register DOM nodes through refs and lifecycle
|
|
513
|
-
effects. Users should not manually
|
|
585
|
+
effects. Users should not manually release runtime entries for normal React
|
|
514
586
|
mount/unmount.
|
|
515
587
|
|
|
516
|
-
`DragProvider`
|
|
517
|
-
`useDropContainer`
|
|
588
|
+
`DragProvider` does not call a terminal runtime destructor when it unmounts.
|
|
589
|
+
`useDropContainer` releases its current DOM registration from effect cleanup.
|
|
518
590
|
`useDraggable`, `useDroppable`, and `useSortable` rely on React prop/ref updates
|
|
519
591
|
for normal mount, unmount, and element replacement behavior.
|
|
520
592
|
|
|
521
|
-
`useRemeasureDropTargets`
|
|
522
|
-
|
|
593
|
+
`useRemeasureDropTargets` and `useRemeasureOverlay` are for geometry changes,
|
|
594
|
+
not resource release. The runtime also prunes disconnected targets on
|
|
595
|
+
drag-critical paths such as remeasurement.
|
|
523
596
|
|
|
524
597
|
## Examples
|
|
525
598
|
|
|
@@ -540,7 +613,7 @@ React examples live in `apps/react-web/src/react`:
|
|
|
540
613
|
- `DragProvider`: creates/configures the DOM runtime for a React subtree.
|
|
541
614
|
Important props include lifecycle callbacks, overlay rendering, targeting,
|
|
542
615
|
constraints, modifiers, pointer/keyboard configuration, announcements, and
|
|
543
|
-
`
|
|
616
|
+
`overlayRelease`.
|
|
544
617
|
- `useDraggable(options)`: registers a drag source. Important options:
|
|
545
618
|
`draggableId`, `group`. Returns ref and input props.
|
|
546
619
|
- `useDroppable(options)`: registers a drop target. Important options:
|
|
@@ -553,6 +626,8 @@ React examples live in `apps/react-web/src/react`:
|
|
|
553
626
|
- `useDragHandle()`: returns the handle attribute props for a handle element.
|
|
554
627
|
- `useRemeasureDropTargets()`: returns a function for remeasuring all targets,
|
|
555
628
|
one target, multiple targets, or a group.
|
|
629
|
+
- `useRemeasureOverlay()`: returns a function for manually refreshing the
|
|
630
|
+
currently mounted overlay measurement.
|
|
556
631
|
- `composeRefs`: helper for combining the refs returned by hooks with app refs.
|
|
557
632
|
- `lockToXAxis`, `lockToYAxis`: DOM movement modifiers re-exported by React.
|
|
558
633
|
- `restrictToContainer`: React-friendly container-bound modifier.
|
|
@@ -561,8 +636,9 @@ Important event/helper types are re-exported from the React package, including
|
|
|
561
636
|
`DragStartEvent`, `DragUpdateEvent`, `DragEndEvent`, `DropEvent`,
|
|
562
637
|
`DragSource`, `DragEndResult`, `DragLifecycleHelpers`, `DragState`,
|
|
563
638
|
`DragOverlayPhase`, `SortableDropPlacement`, `RemeasureDropTargetsInput`,
|
|
564
|
-
`
|
|
565
|
-
targeting types, geometry types, and modifier
|
|
639
|
+
`OverlayReleaseMode`, `PointerConfiguration`, `KeyboardConfiguration`,
|
|
640
|
+
`KeyboardCommand`, `DropTarget`, targeting types, geometry types, and modifier
|
|
641
|
+
types.
|
|
566
642
|
|
|
567
643
|
## When To Use DOM Directly
|
|
568
644
|
|
|
@@ -577,7 +653,7 @@ Use `@mk-drag-and-drop/react` when:
|
|
|
577
653
|
|
|
578
654
|
- Your UI is rendered with React.
|
|
579
655
|
- You want ref-based hooks for registration.
|
|
580
|
-
- You want provider-based runtime
|
|
656
|
+
- You want provider-based runtime scoping and lifecycle integration.
|
|
581
657
|
|
|
582
658
|
## Development
|
|
583
659
|
|
package/dist/drag-context.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DragRuntimeScope } from "@mk-drag-and-drop/dom/integration";
|
|
2
2
|
export type DragContextValue = {
|
|
3
|
-
runtime:
|
|
3
|
+
runtime: DragRuntimeScope;
|
|
4
4
|
keyboardDragEnabled: boolean;
|
|
5
|
+
remeasureOverlay: () => void;
|
|
5
6
|
};
|
|
6
7
|
export declare const DragContext: import("react").Context<DragContextValue | null>;
|
package/dist/drag-overlay.d.ts
CHANGED
|
@@ -3,11 +3,18 @@ import type { DragRect } from "@mk-drag-and-drop/dom";
|
|
|
3
3
|
import type { DragOverlayPhase, DragState } from "@mk-drag-and-drop/dom/integration";
|
|
4
4
|
export type DragOverlayInput = {
|
|
5
5
|
dragState: DragState;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
} & ({
|
|
7
|
+
phase: Extract<DragOverlayPhase, "dragging">;
|
|
8
|
+
remeasureOverlay: () => void;
|
|
9
|
+
removeOverlay?: never;
|
|
10
|
+
} | {
|
|
11
|
+
phase: Extract<DragOverlayPhase, "released">;
|
|
12
|
+
remeasureOverlay: () => void;
|
|
13
|
+
removeOverlay: () => void;
|
|
14
|
+
});
|
|
9
15
|
export type DragOverlayHostHandle = {
|
|
10
16
|
move: (dragState: DragState) => void;
|
|
17
|
+
remeasure: () => void;
|
|
11
18
|
};
|
|
12
19
|
export declare const DragOverlayHost: import("react").NamedExoticComponent<{
|
|
13
20
|
dragState: DragState;
|
package/dist/drag-overlay.js
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { memo, useLayoutEffect, useRef, } from "react";
|
|
2
|
+
import { memo, useCallback, useLayoutEffect, useRef, } from "react";
|
|
3
3
|
export const DragOverlayHost = memo(function DragOverlayHost({ dragState, children, contentId, onHostReady, onOverlayRectChange, }) {
|
|
4
4
|
const overlayWrapperRef = useRef(null);
|
|
5
|
+
const measureOverlayElement = useCallback(() => {
|
|
6
|
+
const wrapper = overlayWrapperRef.current;
|
|
7
|
+
if (!wrapper) {
|
|
8
|
+
onOverlayRectChange?.(null);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const measuredElement = getMeasuredOverlayElement(wrapper);
|
|
12
|
+
if (!measuredElement.isConnected) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
onOverlayRectChange?.(domRectToDragRect(measuredElement.getBoundingClientRect()));
|
|
16
|
+
}, [onOverlayRectChange]);
|
|
5
17
|
useLayoutEffect(() => {
|
|
6
18
|
const wrapper = overlayWrapperRef.current;
|
|
7
19
|
if (!wrapper) {
|
|
@@ -12,26 +24,21 @@ export const DragOverlayHost = memo(function DragOverlayHost({ dragState, childr
|
|
|
12
24
|
move: (nextDragState) => {
|
|
13
25
|
moveOverlayWrapper(wrapper, nextDragState);
|
|
14
26
|
},
|
|
27
|
+
remeasure: measureOverlayElement,
|
|
15
28
|
};
|
|
16
29
|
onHostReady?.(host);
|
|
17
30
|
host.move(dragState);
|
|
18
31
|
return () => {
|
|
19
32
|
onHostReady?.(null);
|
|
20
33
|
};
|
|
21
|
-
}, [dragState, onHostReady]);
|
|
34
|
+
}, [dragState, measureOverlayElement, onHostReady]);
|
|
22
35
|
useLayoutEffect(() => {
|
|
23
36
|
const wrapper = overlayWrapperRef.current;
|
|
24
37
|
if (!wrapper) {
|
|
25
38
|
onOverlayRectChange?.(null);
|
|
26
39
|
return;
|
|
27
40
|
}
|
|
28
|
-
const measuredElement = wrapper
|
|
29
|
-
const measureOverlayElement = () => {
|
|
30
|
-
if (!measuredElement.isConnected) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
onOverlayRectChange?.(domRectToDragRect(measuredElement.getBoundingClientRect()));
|
|
34
|
-
};
|
|
41
|
+
const measuredElement = getMeasuredOverlayElement(wrapper);
|
|
35
42
|
measureOverlayElement();
|
|
36
43
|
if (typeof ResizeObserver === "undefined") {
|
|
37
44
|
return () => {
|
|
@@ -46,7 +53,7 @@ export const DragOverlayHost = memo(function DragOverlayHost({ dragState, childr
|
|
|
46
53
|
resizeObserver.disconnect();
|
|
47
54
|
onOverlayRectChange?.(null);
|
|
48
55
|
};
|
|
49
|
-
}, [contentId, onOverlayRectChange]);
|
|
56
|
+
}, [contentId, measureOverlayElement, onOverlayRectChange]);
|
|
50
57
|
return (_jsx("div", { ref: overlayWrapperRef, style: {
|
|
51
58
|
position: "fixed",
|
|
52
59
|
left: dragState.sourceRect.left,
|
|
@@ -58,6 +65,9 @@ export const DragOverlayHost = memo(function DragOverlayHost({ dragState, childr
|
|
|
58
65
|
transform: getOverlayTransform(dragState),
|
|
59
66
|
}, children: children }));
|
|
60
67
|
});
|
|
68
|
+
function getMeasuredOverlayElement(wrapper) {
|
|
69
|
+
return wrapper.firstElementChild ?? wrapper;
|
|
70
|
+
}
|
|
61
71
|
function moveOverlayWrapper(wrapper, dragState) {
|
|
62
72
|
wrapper.style.transform = getOverlayTransform(dragState);
|
|
63
73
|
}
|
package/dist/drag-provider.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import { type DragEndEvent, type DragLifecycleCallbacks, type DragModifierInput, type DragStartEvent, type DragUpdateEvent, type DropEvent, type KeyboardConfiguration, type PointerConfiguration, type TargetingAlgorithm, type TargetingConstraint } from "@mk-drag-and-drop/dom";
|
|
2
|
+
import { type DragEndEvent, type DragLifecycleCallbacks, type DragModifierInput, type DragStartEvent, type DragUpdateEvent, type DropEvent, type KeyboardConfiguration, type OverlayReleaseMode, type PointerConfiguration, type TargetingAlgorithm, type TargetingConstraint } from "@mk-drag-and-drop/dom";
|
|
3
3
|
import { type DragOverlayInput } from "./drag-overlay.js";
|
|
4
4
|
export type DragAnnouncements = {
|
|
5
5
|
onDragStart?: (event: DragStartEvent) => string | null;
|
|
@@ -12,10 +12,10 @@ export type DragProviderProps = {
|
|
|
12
12
|
announcements?: DragAnnouncements;
|
|
13
13
|
dragOverlay?: (overlay: DragOverlayInput) => ReactNode;
|
|
14
14
|
keyboardConfiguration?: KeyboardConfiguration;
|
|
15
|
-
keepOverlayOnDrop?: boolean;
|
|
16
15
|
modifiers?: readonly DragModifierInput[];
|
|
16
|
+
overlayRelease?: OverlayReleaseMode;
|
|
17
17
|
pointerConfiguration?: PointerConfiguration;
|
|
18
18
|
targetingAlgorithm?: TargetingAlgorithm;
|
|
19
19
|
targetingConstraint?: TargetingConstraint;
|
|
20
20
|
} & DragLifecycleCallbacks;
|
|
21
|
-
export declare function DragProvider({ children, announcements, dragOverlay, keyboardConfiguration,
|
|
21
|
+
export declare function DragProvider({ children, announcements, dragOverlay, keyboardConfiguration, modifiers, overlayRelease, pointerConfiguration, targetingAlgorithm, targetingConstraint, onDragStart, onDragUpdate, onDragEnd, onDrop, }: DragProviderProps): import("react").JSX.Element;
|
package/dist/drag-provider.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
3
3
|
import { pointerToCenter, } from "@mk-drag-and-drop/dom";
|
|
4
|
-
import {
|
|
4
|
+
import { createDragRuntimeScope, } from "@mk-drag-and-drop/dom/integration";
|
|
5
5
|
import { DragContext } from "./drag-context.js";
|
|
6
6
|
import { DragOverlayHost, } from "./drag-overlay.js";
|
|
7
7
|
const visuallyHiddenStyle = {
|
|
@@ -15,7 +15,7 @@ const visuallyHiddenStyle = {
|
|
|
15
15
|
whiteSpace: "nowrap",
|
|
16
16
|
border: 0,
|
|
17
17
|
};
|
|
18
|
-
export function DragProvider({ children, announcements, dragOverlay, keyboardConfiguration,
|
|
18
|
+
export function DragProvider({ children, announcements, dragOverlay, keyboardConfiguration, modifiers, overlayRelease = "auto", pointerConfiguration, targetingAlgorithm = pointerToCenter, targetingConstraint, onDragStart, onDragUpdate, onDragEnd, onDrop, }) {
|
|
19
19
|
const [overlayRequest, setOverlayRequest] = useState(null);
|
|
20
20
|
const [announcementState, setAnnouncementState] = useState({
|
|
21
21
|
id: 0,
|
|
@@ -38,7 +38,7 @@ export function DragProvider({ children, announcements, dragOverlay, keyboardCon
|
|
|
38
38
|
const lifecycleCallbacksRef = useRef(null);
|
|
39
39
|
const runtimeConfigurationSnapshotRef = useRef(null);
|
|
40
40
|
const keyboardDragEnabled = isKeyboardDragEnabledFromConfiguration(keyboardConfiguration);
|
|
41
|
-
const
|
|
41
|
+
const clearOverlayHost = useCallback(() => {
|
|
42
42
|
pendingOverlayDragStateRef.current = null;
|
|
43
43
|
overlayHostRef.current = null;
|
|
44
44
|
overlaySnapshotRef.current = null;
|
|
@@ -72,20 +72,29 @@ export function DragProvider({ children, announcements, dragOverlay, keyboardCon
|
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
74
|
if (runtimeRef.current === null) {
|
|
75
|
-
runtimeRef.current =
|
|
75
|
+
runtimeRef.current = createDragRuntimeScope({
|
|
76
76
|
updateOverlayHost: handleOverlayHostUpdate,
|
|
77
77
|
});
|
|
78
78
|
}
|
|
79
79
|
const runtime = runtimeRef.current;
|
|
80
|
+
const remeasureOverlay = useCallback(() => {
|
|
81
|
+
overlayHostRef.current?.remeasure();
|
|
82
|
+
}, []);
|
|
80
83
|
const contextValue = useMemo(() => ({
|
|
81
84
|
runtime,
|
|
82
85
|
keyboardDragEnabled,
|
|
83
|
-
|
|
86
|
+
remeasureOverlay,
|
|
87
|
+
}), [keyboardDragEnabled, remeasureOverlay, runtime]);
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
return () => {
|
|
90
|
+
runtime.releaseActiveDragResources();
|
|
91
|
+
};
|
|
92
|
+
}, [runtime]);
|
|
84
93
|
const nextRuntimeConfiguration = {
|
|
85
94
|
targetingAlgorithm,
|
|
86
95
|
targetingConstraint,
|
|
87
96
|
hasDragOverlay: dragOverlay !== undefined,
|
|
88
|
-
|
|
97
|
+
overlayRelease,
|
|
89
98
|
lifecycleCallbacks: lifecycleCallbacksRef.current,
|
|
90
99
|
keyboardConfiguration,
|
|
91
100
|
modifiers,
|
|
@@ -109,16 +118,11 @@ export function DragProvider({ children, announcements, dragOverlay, keyboardCon
|
|
|
109
118
|
runtimeConfigurationSnapshotRef.current =
|
|
110
119
|
createRuntimeConfigurationSnapshot(nextRuntimeConfiguration);
|
|
111
120
|
});
|
|
112
|
-
useEffect(() => {
|
|
113
|
-
return () => {
|
|
114
|
-
runtime.dispose();
|
|
115
|
-
};
|
|
116
|
-
}, [runtime]);
|
|
117
121
|
useEffect(() => {
|
|
118
122
|
if (!dragOverlay) {
|
|
119
|
-
|
|
123
|
+
clearOverlayHost();
|
|
120
124
|
}
|
|
121
|
-
}, [dragOverlay,
|
|
125
|
+
}, [dragOverlay, clearOverlayHost]);
|
|
122
126
|
useEffect(() => {
|
|
123
127
|
if (!announcements) {
|
|
124
128
|
lastAnnouncementRef.current = null;
|
|
@@ -154,13 +158,13 @@ export function DragProvider({ children, announcements, dragOverlay, keyboardCon
|
|
|
154
158
|
moveOverlay(update.dragState);
|
|
155
159
|
return;
|
|
156
160
|
}
|
|
157
|
-
|
|
161
|
+
clearOverlayHost();
|
|
158
162
|
}
|
|
159
163
|
function mountOverlay(state) {
|
|
160
164
|
pendingOverlayDragStateRef.current = state.dragState;
|
|
161
165
|
overlayHostRef.current?.move(state.dragState);
|
|
162
166
|
if (!dragOverlayRef.current) {
|
|
163
|
-
|
|
167
|
+
clearOverlayHost();
|
|
164
168
|
return;
|
|
165
169
|
}
|
|
166
170
|
overlayContentIdRef.current += 1;
|
|
@@ -184,21 +188,32 @@ export function DragProvider({ children, announcements, dragOverlay, keyboardCon
|
|
|
184
188
|
overlaySnapshotRef.current = {
|
|
185
189
|
id: overlayRequest.id,
|
|
186
190
|
state: overlayRequest.state,
|
|
187
|
-
content: dragOverlay(
|
|
188
|
-
dragState: overlayRequest.state.dragState,
|
|
189
|
-
phase: overlayRequest.state.phase,
|
|
190
|
-
finish: finishOverlay,
|
|
191
|
-
}),
|
|
191
|
+
content: dragOverlay(createOverlayInput(overlayRequest.state)),
|
|
192
192
|
};
|
|
193
193
|
return overlaySnapshotRef.current;
|
|
194
194
|
}
|
|
195
|
+
function createOverlayInput(overlayState) {
|
|
196
|
+
if (overlayState.phase === "released") {
|
|
197
|
+
return {
|
|
198
|
+
dragState: overlayState.dragState,
|
|
199
|
+
phase: "released",
|
|
200
|
+
remeasureOverlay,
|
|
201
|
+
removeOverlay: clearOverlayHost,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
dragState: overlayState.dragState,
|
|
206
|
+
phase: "dragging",
|
|
207
|
+
remeasureOverlay,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
195
210
|
}
|
|
196
211
|
function createRuntimeConfigurationSnapshot(input) {
|
|
197
212
|
return {
|
|
198
213
|
targetingAlgorithm: input.targetingAlgorithm,
|
|
199
214
|
targetingConstraint: input.targetingConstraint,
|
|
200
215
|
hasDragOverlay: input.hasDragOverlay,
|
|
201
|
-
|
|
216
|
+
overlayRelease: input.overlayRelease,
|
|
202
217
|
keyboardConfiguration: cloneKeyboardConfiguration(input.keyboardConfiguration),
|
|
203
218
|
modifiers: input.modifiers ? Array.from(input.modifiers) : undefined,
|
|
204
219
|
pointerConfiguration: input.pointerConfiguration
|
|
@@ -211,7 +226,7 @@ function areRuntimeConfigurationSnapshotsEqual(previous, next) {
|
|
|
211
226
|
previous.targetingAlgorithm === next.targetingAlgorithm &&
|
|
212
227
|
previous.targetingConstraint === next.targetingConstraint &&
|
|
213
228
|
previous.hasDragOverlay === next.hasDragOverlay &&
|
|
214
|
-
previous.
|
|
229
|
+
previous.overlayRelease === next.overlayRelease &&
|
|
215
230
|
areKeyboardConfigurationsEqual(previous.keyboardConfiguration, next.keyboardConfiguration) &&
|
|
216
231
|
arePointerConfigurationsEqual(previous.pointerConfiguration, next.pointerConfiguration) &&
|
|
217
232
|
areModifierInputsEqual(previous.modifiers, next.modifiers));
|
|
@@ -19,7 +19,7 @@ export function useDropContainer({ containerId, group = defaultDropContainerGrou
|
|
|
19
19
|
existingBehavior.group === group) {
|
|
20
20
|
return existingBehavior.behavior;
|
|
21
21
|
}
|
|
22
|
-
existingBehavior?.behavior.
|
|
22
|
+
existingBehavior?.behavior.releaseRegistration();
|
|
23
23
|
const behavior = createDomDropContainer({
|
|
24
24
|
runtime,
|
|
25
25
|
containerId,
|
|
@@ -44,7 +44,7 @@ export function useDropContainer({ containerId, group = defaultDropContainerGrou
|
|
|
44
44
|
behavior.setElement(nodeRef.current);
|
|
45
45
|
}
|
|
46
46
|
return () => {
|
|
47
|
-
behavior.
|
|
47
|
+
behavior.releaseRegistration();
|
|
48
48
|
if (behaviorRef.current?.behavior === behavior) {
|
|
49
49
|
behaviorRef.current = null;
|
|
50
50
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { useCallback, useContext, useMemo, } from "react";
|
|
1
|
+
import { useCallback, useContext, useEffect, useMemo, useRef, } from "react";
|
|
2
2
|
import { createDomDroppable } from "@mk-drag-and-drop/dom/integration";
|
|
3
3
|
import { DragContext } from "../drag-context.js";
|
|
4
4
|
const defaultDroppableGroup = "default";
|
|
5
5
|
export function useDroppable({ dropTargetId, group = defaultDroppableGroup, containerId = null, }) {
|
|
6
6
|
const context = useContext(DragContext);
|
|
7
|
+
const nodeRef = useRef(null);
|
|
7
8
|
if (!context) {
|
|
8
9
|
throw new Error("useDroppable must be used inside DragProvider");
|
|
9
10
|
}
|
|
@@ -15,8 +16,17 @@ export function useDroppable({ dropTargetId, group = defaultDroppableGroup, cont
|
|
|
15
16
|
containerId,
|
|
16
17
|
}), [containerId, dropTargetId, group, runtime]);
|
|
17
18
|
const setNodeRef = useCallback((node) => {
|
|
19
|
+
nodeRef.current = node;
|
|
18
20
|
behavior.setElement(node);
|
|
19
21
|
}, [behavior]);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (nodeRef.current) {
|
|
24
|
+
behavior.setElement(nodeRef.current);
|
|
25
|
+
}
|
|
26
|
+
return () => {
|
|
27
|
+
behavior.releaseRegistration();
|
|
28
|
+
};
|
|
29
|
+
}, [behavior]);
|
|
20
30
|
return useMemo(() => ({
|
|
21
31
|
ref: setNodeRef,
|
|
22
32
|
}), [setNodeRef]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useRemeasureOverlay(): () => void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useCallback, useContext } from "react";
|
|
2
|
+
import { DragContext } from "../drag-context.js";
|
|
3
|
+
export function useRemeasureOverlay() {
|
|
4
|
+
const context = useContext(DragContext);
|
|
5
|
+
if (!context) {
|
|
6
|
+
throw new Error("useRemeasureOverlay must be used inside DragProvider");
|
|
7
|
+
}
|
|
8
|
+
const { remeasureOverlay } = context;
|
|
9
|
+
return useCallback(() => {
|
|
10
|
+
remeasureOverlay();
|
|
11
|
+
}, [remeasureOverlay]);
|
|
12
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, useContext, useMemo, useRef, } from "react";
|
|
1
|
+
import { useCallback, useContext, useEffect, useMemo, useRef, } from "react";
|
|
2
2
|
import { createDomSortable, } from "@mk-drag-and-drop/dom/integration";
|
|
3
3
|
import { DragContext } from "../drag-context.js";
|
|
4
4
|
const defaultSortableGroup = "default";
|
|
@@ -33,6 +33,14 @@ export function useSortable({ draggableId, group = defaultSortableGroup, contain
|
|
|
33
33
|
nodeRef.current = node;
|
|
34
34
|
behavior.setElement(node);
|
|
35
35
|
}, [behavior]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (nodeRef.current) {
|
|
38
|
+
behavior.setElement(nodeRef.current);
|
|
39
|
+
}
|
|
40
|
+
return () => {
|
|
41
|
+
behavior.releaseRegistration();
|
|
42
|
+
};
|
|
43
|
+
}, [behavior]);
|
|
36
44
|
return useMemo(() => {
|
|
37
45
|
const sortableProps = {
|
|
38
46
|
ref: setNodeRef,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { DragProvider, type DragAnnouncements, type DragProviderProps, } from "./drag-provider.js";
|
|
2
2
|
export type { DragOverlayInput } from "./drag-overlay.js";
|
|
3
|
-
export { centerToCenter, getDistanceToRect, lockToXAxis, lockToYAxis, maxOverlayCenterDistanceToRect, maxPointerDistanceToRect, pointerToCenter, pointerToRectDistance, type DragEndResult, type DragEndEvent, type DragLifecycleCallbacks, type DragLifecycleHelpers, type DragModifier, type DragModifierInput, type DragModifierSetupInput, type DragModifierTransformInput, type DragPoint, type DragRect, type DragSource, type DragStartEvent, type DragUpdateEvent, type DropEvent, type DropTarget, type KeyboardCommand, type KeyboardConfiguration, type PointerConfiguration, type RemeasureDropTargetsInput, type SortableAxis, type SortableDropPlacement, type SortablePlacementBoundary, type TargetingAlgorithm, type TargetingAlgorithmInput, type TargetingConstraint, type TargetingConstraintInput, } from "@mk-drag-and-drop/dom";
|
|
3
|
+
export { centerToCenter, getDistanceToRect, lockToXAxis, lockToYAxis, maxOverlayCenterDistanceToRect, maxPointerDistanceToRect, pointerToCenter, pointerToRectDistance, type DragEndResult, type DragEndEvent, type DragLifecycleCallbacks, type DragLifecycleHelpers, type DragModifier, type DragModifierInput, type DragModifierSetupInput, type DragModifierTransformInput, type DragPoint, type DragRect, type DragSource, type DragStartEvent, type DragUpdateEvent, type DropEvent, type DropTarget, type KeyboardCommand, type KeyboardConfiguration, type OverlayReleaseMode, type PointerConfiguration, type RemeasureDropTargetsInput, type SortableAxis, type SortableDropPlacement, type SortablePlacementBoundary, type TargetingAlgorithm, type TargetingAlgorithmInput, type TargetingConstraint, type TargetingConstraintInput, } from "@mk-drag-and-drop/dom";
|
|
4
4
|
export { restrictToContainer, type ReactRestrictToContainerInput, } from "./modifiers/restrict-to-container.js";
|
|
5
5
|
export type { DragOverlayPhase, DragState, } from "@mk-drag-and-drop/dom/integration";
|
|
6
6
|
export { useDragHandle, type UseDragHandleResult, } from "./hooks/use-drag-handle.js";
|
|
@@ -8,5 +8,6 @@ export { useDraggable, type UseDraggableOptions, type UseDraggableResult, } from
|
|
|
8
8
|
export { useDropContainer, type UseDropContainerOptions, type UseDropContainerResult, } from "./hooks/use-drop-container.js";
|
|
9
9
|
export { useDroppable, type UseDroppableOptions, type UseDroppableResult, } from "./hooks/use-droppable.js";
|
|
10
10
|
export { useRemeasureDropTargets } from "./hooks/use-remeasure-drop-targets.js";
|
|
11
|
+
export { useRemeasureOverlay } from "./hooks/use-remeasure-overlay.js";
|
|
11
12
|
export { useSortable, type UseSortableOptions, type UseSortableResult, } from "./hooks/use-sortable.js";
|
|
12
13
|
export { composeRefs } from "./utils/compose-refs.js";
|
package/dist/index.js
CHANGED
|
@@ -6,5 +6,6 @@ export { useDraggable, } from "./hooks/use-draggable.js";
|
|
|
6
6
|
export { useDropContainer, } from "./hooks/use-drop-container.js";
|
|
7
7
|
export { useDroppable, } from "./hooks/use-droppable.js";
|
|
8
8
|
export { useRemeasureDropTargets } from "./hooks/use-remeasure-drop-targets.js";
|
|
9
|
+
export { useRemeasureOverlay } from "./hooks/use-remeasure-overlay.js";
|
|
9
10
|
export { useSortable, } from "./hooks/use-sortable.js";
|
|
10
11
|
export { composeRefs } from "./utils/compose-refs.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mk-drag-and-drop/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "React hooks and provider for the mk drag-and-drop DOM runtime.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://mkramer.dev",
|
|
@@ -37,8 +37,9 @@
|
|
|
37
37
|
"files": [
|
|
38
38
|
"dist"
|
|
39
39
|
],
|
|
40
|
+
"sideEffects": false,
|
|
40
41
|
"dependencies": {
|
|
41
|
-
"@mk-drag-and-drop/dom": "0.
|
|
42
|
+
"@mk-drag-and-drop/dom": "^0.3.0"
|
|
42
43
|
},
|
|
43
44
|
"peerDependencies": {
|
|
44
45
|
"react": "^19.0.0"
|