@snapgridjs/react 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 +45 -9
- package/dist/index.cjs +560 -377
- package/dist/index.d.cts +156 -95
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +156 -95
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +559 -379
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,24 +1,222 @@
|
|
|
1
|
-
import { DragDropProvider, useDragDropMonitor, useDraggable, useDraggable as useDraggable$1, useDroppable, useDroppable as useDroppable$1 } from "@dnd-kit/react";
|
|
2
|
-
import { beginDrag, beginReceive, beginResize, bottom, calcGridItemPosition, calcXY, commitLayout, defaultGridConfig, dragResize, dragTo, findOrGenerateResponsiveLayout, getBreakpointFromWidth, getColsFromBreakpoint, getCompactor, horizontalCompactor, noCompactor, nudge, removeItemWithCompactor, toPositionParams, verticalCompactor, verticalCompactor as verticalCompactor$1 } from "@snapgridjs/core";
|
|
3
|
-
import { Children, createContext, isValidElement, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
1
|
+
import { DragDropProvider, DragOverlay, useDragDropManager, useDragDropMonitor, useDraggable, useDraggable as useDraggable$1, useDroppable, useDroppable as useDroppable$1, useInstance } from "@dnd-kit/react";
|
|
2
|
+
import { beginDrag, beginReceive, beginResize, bottom, calcGridColWidth, calcGridItemPosition, calcXY, commitLayout, defaultGridConfig, dragResize, dragTo, findOrGenerateResponsiveLayout, getBreakpointFromWidth, getColsFromBreakpoint, getCompactor, horizontalCompactor, noCompactor, nudge, removeItemWithCompactor, toPositionParams, verticalCompactor, verticalCompactor as verticalCompactor$1 } from "@snapgridjs/core";
|
|
3
|
+
import { Children, createContext, isValidElement, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { Modifier } from "@dnd-kit/abstract";
|
|
4
5
|
import { Feedback, Feedback as Feedback$1, KeyboardSensor, KeyboardSensor as KeyboardSensor$1, PointerActivationConstraints, PointerSensor, PointerSensor as PointerSensor$1 } from "@dnd-kit/dom";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
import { isKeyboardEvent } from "@dnd-kit/dom/utilities";
|
|
7
|
+
import { useSortable } from "@dnd-kit/react/sortable";
|
|
8
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
//#region src/controller/GridController.ts
|
|
10
|
+
function sameItem(a, b) {
|
|
11
|
+
if (a === b) return true;
|
|
12
|
+
if (!a || !b) return false;
|
|
13
|
+
return a.i === b.i && a.x === b.x && a.y === b.y && a.w === b.w && a.h === b.h;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Live per-grid drag/resize state as a plain observable: the provider writes
|
|
17
|
+
* (`setSession`/`setKeyboard`/`setCommitted`), hooks subscribe to just their own
|
|
18
|
+
* slice via `useSyncExternalStore`. Value-cached snapshots mean a drag re-renders
|
|
19
|
+
* only the tiles whose slice changed, not the whole subtree (the old
|
|
20
|
+
* context-value model re-rendered every tile every frame).
|
|
21
|
+
*/
|
|
22
|
+
var GridController = class {
|
|
23
|
+
id;
|
|
24
|
+
#committed;
|
|
25
|
+
#session = null;
|
|
26
|
+
#keyboard = false;
|
|
27
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
28
|
+
config = null;
|
|
29
|
+
#itemCache = /* @__PURE__ */ new Map();
|
|
30
|
+
#resizeCache = /* @__PURE__ */ new Map();
|
|
31
|
+
#placeholderCache = null;
|
|
32
|
+
#renderedMap = null;
|
|
33
|
+
#renderedMapSource = null;
|
|
34
|
+
#indexById = /* @__PURE__ */ new Map();
|
|
35
|
+
#nextIndex = 0;
|
|
36
|
+
/** The dnd-kit manager this grid is registered with (set by useInstance). */
|
|
37
|
+
manager;
|
|
38
|
+
constructor(id, committed = [], manager) {
|
|
39
|
+
this.id = id;
|
|
40
|
+
this.#committed = committed;
|
|
41
|
+
this.manager = manager;
|
|
42
|
+
}
|
|
43
|
+
/** Replace the per-grid config (called by the container host during render). */
|
|
44
|
+
setConfig(config) {
|
|
45
|
+
this.config = config;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Re-point this grid's id. The container host syncs it (during render, before
|
|
49
|
+
* the droppable/group read it) when the controlled `id` prop changes, so the
|
|
50
|
+
* returned `group`, the droppable id, and the registry key never drift apart.
|
|
51
|
+
*/
|
|
52
|
+
setId(id) {
|
|
53
|
+
this.id = id;
|
|
54
|
+
}
|
|
55
|
+
register = () => {};
|
|
56
|
+
subscribe = (listener) => {
|
|
57
|
+
this.#listeners.add(listener);
|
|
58
|
+
return () => {
|
|
59
|
+
this.#listeners.delete(listener);
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
#emit() {
|
|
63
|
+
for (const listener of this.#listeners) listener();
|
|
64
|
+
}
|
|
65
|
+
/** The layout currently shown: the drag preview while dragging, else committed. */
|
|
66
|
+
#rendered() {
|
|
67
|
+
return this.#session ? this.#session.preview : this.#committed;
|
|
68
|
+
}
|
|
69
|
+
#renderedById() {
|
|
70
|
+
const rendered = this.#rendered();
|
|
71
|
+
if (this.#renderedMapSource !== rendered) {
|
|
72
|
+
this.#renderedMap = new Map(rendered.map((it) => [it.i, it]));
|
|
73
|
+
this.#renderedMapSource = rendered;
|
|
74
|
+
}
|
|
75
|
+
return this.#renderedMap;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Sync the committed layout from the controlled `layout` prop. Called during
|
|
79
|
+
* the provider's render, so it must NOT notify — emitting here would update
|
|
80
|
+
* subscribed GridItems mid-render (a React "setState while rendering" error).
|
|
81
|
+
* No notify is needed: a `layout` prop change already re-renders the whole
|
|
82
|
+
* provider subtree, so every GridItem re-reads its snapshot on that pass.
|
|
83
|
+
*/
|
|
84
|
+
setCommitted(layout) {
|
|
85
|
+
if (this.#committed === layout) return;
|
|
86
|
+
this.#committed = layout;
|
|
87
|
+
const present = new Set(layout.map((it) => it.i));
|
|
88
|
+
for (const id of this.#indexById.keys()) if (!present.has(id)) this.#indexById.delete(id);
|
|
89
|
+
for (const id of this.#itemCache.keys()) if (!present.has(id)) this.#itemCache.delete(id);
|
|
90
|
+
for (const id of this.#resizeCache.keys()) if (!present.has(id)) this.#resizeCache.delete(id);
|
|
91
|
+
}
|
|
92
|
+
setSession(next) {
|
|
93
|
+
this.#session = next;
|
|
94
|
+
this.#emit();
|
|
95
|
+
}
|
|
96
|
+
getSession() {
|
|
97
|
+
return this.#session;
|
|
98
|
+
}
|
|
99
|
+
/** Record whether the active drag is keyboard-driven (drives `hidden`). */
|
|
100
|
+
setKeyboard(value) {
|
|
101
|
+
if (this.#keyboard === value) return;
|
|
102
|
+
this.#keyboard = value;
|
|
103
|
+
this.#emit();
|
|
104
|
+
}
|
|
105
|
+
itemSnapshot = (id) => {
|
|
106
|
+
const item = this.#renderedById().get(id);
|
|
107
|
+
const isDragging = this.#session?.activeId === id;
|
|
108
|
+
const hidden = isDragging && this.#session?.kind === "move" && !this.#keyboard;
|
|
109
|
+
const prev = this.#itemCache.get(id);
|
|
110
|
+
if (prev && prev.isDragging === isDragging && prev.hidden === hidden && sameItem(prev.item, item)) return prev;
|
|
111
|
+
const snap = {
|
|
112
|
+
item,
|
|
113
|
+
isDragging,
|
|
114
|
+
hidden
|
|
115
|
+
};
|
|
116
|
+
this.#itemCache.set(id, snap);
|
|
117
|
+
return snap;
|
|
118
|
+
};
|
|
119
|
+
placeholderSnapshot = () => {
|
|
120
|
+
const next = this.#session?.placeholder ?? null;
|
|
121
|
+
if (sameItem(this.#placeholderCache ?? void 0, next ?? void 0)) return this.#placeholderCache;
|
|
122
|
+
this.#placeholderCache = next;
|
|
123
|
+
return next;
|
|
124
|
+
};
|
|
125
|
+
resizeSnapshot = (itemId) => {
|
|
126
|
+
const isResizing = this.#session?.kind === "resize" && this.#session.activeId === itemId;
|
|
127
|
+
const prev = this.#resizeCache.get(itemId);
|
|
128
|
+
if (prev && prev.isResizing === isResizing) return prev;
|
|
129
|
+
const snap = { isResizing };
|
|
130
|
+
this.#resizeCache.set(itemId, snap);
|
|
131
|
+
return snap;
|
|
132
|
+
};
|
|
133
|
+
renderedSnapshot = () => this.#rendered();
|
|
134
|
+
/** A stable index for `id` (see {@link GridController.#indexById}). */
|
|
135
|
+
itemIndex(id) {
|
|
136
|
+
let i = this.#indexById.get(id);
|
|
137
|
+
if (i === void 0) {
|
|
138
|
+
i = this.#nextIndex++;
|
|
139
|
+
this.#indexById.set(id, i);
|
|
140
|
+
}
|
|
141
|
+
return i;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/controller/registry.ts
|
|
146
|
+
/**
|
|
147
|
+
* Resolves a grid's {@link GridController} by its id, scoped to the dnd-kit
|
|
148
|
+
* manager the grid is registered with. A container registers its controller
|
|
149
|
+
* here (during render, so child items resolve it on their first render); items
|
|
150
|
+
* look it up by their `group` (= the grid id). Replaces the old geometry
|
|
151
|
+
* `GridRegistry` — which grid the pointer is over now comes from the collision
|
|
152
|
+
* target, so the registry's only job is id → controller resolution.
|
|
153
|
+
*
|
|
154
|
+
* Keyed by manager so two apps (or two providers) never collide, and grids in
|
|
155
|
+
* one provider share a map (the cross-grid seam).
|
|
156
|
+
*/
|
|
157
|
+
const byManager = /* @__PURE__ */ new WeakMap();
|
|
158
|
+
const noManager = /* @__PURE__ */ new Map();
|
|
159
|
+
function mapFor(manager) {
|
|
160
|
+
if (!manager) return noManager;
|
|
161
|
+
let map = byManager.get(manager);
|
|
162
|
+
if (!map) {
|
|
163
|
+
map = /* @__PURE__ */ new Map();
|
|
164
|
+
byManager.set(manager, map);
|
|
165
|
+
}
|
|
166
|
+
return map;
|
|
167
|
+
}
|
|
168
|
+
/** Register a controller under `id` for `manager`. Returns an unregister fn. */
|
|
169
|
+
function registerController(manager, id, controller) {
|
|
170
|
+
const map = mapFor(manager);
|
|
171
|
+
map.set(id, controller);
|
|
172
|
+
return () => {
|
|
173
|
+
if (map.get(id) === controller) map.delete(id);
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/** The controller registered under `id` for `manager`, or undefined. */
|
|
177
|
+
function getController(manager, id) {
|
|
178
|
+
return mapFor(manager).get(id);
|
|
179
|
+
}
|
|
180
|
+
const grabOffsets = /* @__PURE__ */ new WeakMap();
|
|
181
|
+
const noManagerGrab = { current: null };
|
|
182
|
+
function setGrabOffset(manager, offset) {
|
|
183
|
+
if (!manager) {
|
|
184
|
+
noManagerGrab.current = offset;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (offset) grabOffsets.set(manager, offset);
|
|
188
|
+
else grabOffsets.delete(manager);
|
|
189
|
+
}
|
|
190
|
+
function getGrabOffset(manager) {
|
|
191
|
+
return (manager ? grabOffsets.get(manager) : noManagerGrab.current) ?? {
|
|
192
|
+
x: 0,
|
|
193
|
+
y: 0
|
|
194
|
+
};
|
|
14
195
|
}
|
|
15
196
|
//#endregion
|
|
16
|
-
//#region src/dragFlow.ts
|
|
197
|
+
//#region src/dnd/dragFlow.ts
|
|
17
198
|
/**
|
|
18
|
-
* Pure decision helpers for the drag interaction
|
|
19
|
-
*
|
|
20
|
-
*
|
|
199
|
+
* Pure decision helpers for the drag interaction so the tricky bits — grab-offset
|
|
200
|
+
* cell mapping, the cross-grid drop lifecycle, and external-drop acceptance — are
|
|
201
|
+
* unit-testable without a DOM or dnd-kit.
|
|
21
202
|
*/
|
|
203
|
+
/** Read snapgrid's payload off a dnd-kit drag source. */
|
|
204
|
+
function dragData(event) {
|
|
205
|
+
return (event.operation.source?.data)?.snapGrid;
|
|
206
|
+
}
|
|
207
|
+
/** Size/id spec for an external (non-grid) draggable the grid may accept, or null. */
|
|
208
|
+
function externalDropSpec(source, dropConfig) {
|
|
209
|
+
if (!dropConfig?.enabled || !source) return null;
|
|
210
|
+
const data = source.data;
|
|
211
|
+
if (data?.snapGrid) return null;
|
|
212
|
+
if (dropConfig.accept && !dropConfig.accept(source)) return null;
|
|
213
|
+
const spec = data?.snapGridDrop;
|
|
214
|
+
return {
|
|
215
|
+
i: spec?.i,
|
|
216
|
+
w: spec?.w ?? dropConfig.defaultItem?.w ?? 1,
|
|
217
|
+
h: spec?.h ?? dropConfig.defaultItem?.h ?? 1
|
|
218
|
+
};
|
|
219
|
+
}
|
|
22
220
|
/**
|
|
23
221
|
* Map a client-space pointer to a grid cell, accounting for where *within* the
|
|
24
222
|
* dragged tile the pointer grabbed it. Subtracting the grab offset means the
|
|
@@ -29,6 +227,29 @@ function useGridRuntime() {
|
|
|
29
227
|
function receiveCell(pointer, gridRect, grabOffset, w, h, pp) {
|
|
30
228
|
return calcXY(pp, pointer.y - grabOffset.y - gridRect.top, pointer.x - grabOffset.x - gridRect.left, w, h);
|
|
31
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Map a keyboard event key to a one-cell grid step while a keyboard drag is
|
|
232
|
+
* active, or null for keys snapgrid doesn't own — Enter/Space (drop) and Escape
|
|
233
|
+
* (cancel) fall through to dnd-kit's KeyboardSensor.
|
|
234
|
+
*/
|
|
235
|
+
function arrowStep(key) {
|
|
236
|
+
switch (key) {
|
|
237
|
+
case "ArrowLeft": return [-1, 0];
|
|
238
|
+
case "ArrowRight": return [1, 0];
|
|
239
|
+
case "ArrowUp": return [0, -1];
|
|
240
|
+
case "ArrowDown": return [0, 1];
|
|
241
|
+
default: return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Which grid a drop commits to, as fed to {@link classifyDrop} as `dest`. A
|
|
246
|
+
* keyboard drag has no pointer, so it can only ever land in its own grid; a
|
|
247
|
+
* pointer drag lands in whichever grid the collision observer resolved (or none).
|
|
248
|
+
*/
|
|
249
|
+
function dropDestination(opts) {
|
|
250
|
+
if (opts.keyboard) return opts.myId;
|
|
251
|
+
return opts.targetId != null ? String(opts.targetId) : null;
|
|
252
|
+
}
|
|
32
253
|
/** Pure classification of a drag end. See {@link DropAction}. */
|
|
33
254
|
function classifyDrop(s) {
|
|
34
255
|
if (s.canceled) {
|
|
@@ -46,37 +267,27 @@ function classifyDrop(s) {
|
|
|
46
267
|
return "noop";
|
|
47
268
|
}
|
|
48
269
|
//#endregion
|
|
49
|
-
//#region src/
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
getGrabOffset() {
|
|
71
|
-
return grabOffset ?? {
|
|
72
|
-
x: 0,
|
|
73
|
-
y: 0
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
/** Non-null when grids are wrapped in a `<SnapGridGroup>` (shared cross-grid registry). */
|
|
79
|
-
const SnapGridGroupContext = createContext(null);
|
|
270
|
+
//#region src/dnd/snapToGrid.ts
|
|
271
|
+
/**
|
|
272
|
+
* Quantizes the dragged item's transform to whole grid cells, so the floating
|
|
273
|
+
* <DragOverlay> clone jumps cell-to-cell in lockstep with the (always-snapped)
|
|
274
|
+
* placeholder instead of tracking the pointer smoothly. Applied on the item
|
|
275
|
+
* draggable; a no-op unless `dragConfig.snapToGrid` is set.
|
|
276
|
+
*/
|
|
277
|
+
var SnapToGrid = class extends Modifier {
|
|
278
|
+
apply({ transform }) {
|
|
279
|
+
const opts = this.options;
|
|
280
|
+
if (!opts?.isEnabled()) return transform;
|
|
281
|
+
const pp = opts.getPositionParams();
|
|
282
|
+
const colStep = calcGridColWidth(pp) + pp.margin[0];
|
|
283
|
+
const rowStep = pp.rowHeight + pp.margin[1];
|
|
284
|
+
if (colStep <= 0 || rowStep <= 0) return transform;
|
|
285
|
+
return {
|
|
286
|
+
x: Math.round(transform.x / colStep) * colStep,
|
|
287
|
+
y: Math.round(transform.y / rowStep) * rowStep
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
};
|
|
80
291
|
//#endregion
|
|
81
292
|
//#region src/hooks/dndShared.ts
|
|
82
293
|
/** Marker attribute placed on resize-handle elements. */
|
|
@@ -108,52 +319,30 @@ function buildItemSensors(threshold, getDragConfig) {
|
|
|
108
319
|
}), KeyboardSensor$1];
|
|
109
320
|
}
|
|
110
321
|
//#endregion
|
|
111
|
-
//#region src/
|
|
322
|
+
//#region src/hooks/useGridController.ts
|
|
112
323
|
const DEFAULT_HANDLES = ["se"];
|
|
113
|
-
/** Read snapgrid's payload off a dnd-kit drag source. */
|
|
114
|
-
function dragData(event) {
|
|
115
|
-
return (event.operation.source?.data)?.snapGrid;
|
|
116
|
-
}
|
|
117
|
-
/** Size/id spec for an external (non-grid) draggable the grid may accept, or null. */
|
|
118
|
-
function externalDropSpec(source, dropConfig) {
|
|
119
|
-
if (!dropConfig?.enabled || !source) return null;
|
|
120
|
-
const data = source.data;
|
|
121
|
-
if (data?.snapGrid) return null;
|
|
122
|
-
if (dropConfig.accept && !dropConfig.accept(source)) return null;
|
|
123
|
-
const spec = data?.snapGridDrop;
|
|
124
|
-
return {
|
|
125
|
-
i: spec?.i,
|
|
126
|
-
w: spec?.w ?? dropConfig.defaultItem?.w ?? 1,
|
|
127
|
-
h: spec?.h ?? dropConfig.defaultItem?.h ?? 1
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
324
|
/**
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
325
|
+
* The grid's brain: owns the {@link GridController}, runs the dnd-kit drag/resize
|
|
326
|
+
* monitor for this grid, and writes per-grid config to the controller each render.
|
|
327
|
+
* Created by {@link useGridContainer}; items resolve the same controller by their
|
|
328
|
+
* `group` (= this grid's id) from the per-manager registry. Consumes the ambient
|
|
329
|
+
* `DragDropProvider` — it does not mint one.
|
|
135
330
|
*/
|
|
136
|
-
function
|
|
137
|
-
const groupRegistry = useContext(SnapGridGroupContext);
|
|
138
|
-
const runtime = /* @__PURE__ */ jsx(SnapGridRuntime, {
|
|
139
|
-
groupRegistry,
|
|
140
|
-
...props
|
|
141
|
-
});
|
|
142
|
-
return groupRegistry ? runtime : /* @__PURE__ */ jsx(DragDropProvider, { children: runtime });
|
|
143
|
-
}
|
|
144
|
-
function SnapGridRuntime(props) {
|
|
331
|
+
function useGridController(opts) {
|
|
145
332
|
const autoId = useId();
|
|
146
|
-
const containerId =
|
|
333
|
+
const containerId = opts.id ?? autoId;
|
|
147
334
|
const gridConfig = useMemo(() => ({
|
|
148
335
|
...defaultGridConfig,
|
|
149
|
-
...
|
|
150
|
-
}), [
|
|
151
|
-
const positionParams = useMemo(() => toPositionParams(gridConfig,
|
|
152
|
-
const compactor =
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
336
|
+
...opts.gridConfig
|
|
337
|
+
}), [opts.gridConfig]);
|
|
338
|
+
const positionParams = useMemo(() => toPositionParams(gridConfig, opts.width), [gridConfig, opts.width]);
|
|
339
|
+
const compactor = opts.compactor ?? verticalCompactor$1;
|
|
340
|
+
const manager = useDragDropManager();
|
|
341
|
+
const controller = useInstance((m) => new GridController(containerId, opts.layout, m ?? void 0));
|
|
342
|
+
controller.setCommitted(opts.layout);
|
|
343
|
+
if (controller.id !== containerId) controller.setId(containerId);
|
|
344
|
+
const optsRef = useRef(opts);
|
|
345
|
+
optsRef.current = opts;
|
|
157
346
|
const ppRef = useRef(positionParams);
|
|
158
347
|
ppRef.current = positionParams;
|
|
159
348
|
const gridRef = useRef(gridConfig);
|
|
@@ -162,39 +351,44 @@ function SnapGridRuntime(props) {
|
|
|
162
351
|
compactorRef.current = compactor;
|
|
163
352
|
const containerIdRef = useRef(containerId);
|
|
164
353
|
containerIdRef.current = containerId;
|
|
354
|
+
const managerRef = useRef(manager);
|
|
355
|
+
managerRef.current = manager;
|
|
165
356
|
const sessionRef = useRef(null);
|
|
166
357
|
const containerElRef = useRef(null);
|
|
167
358
|
const keyboardRef = useRef(false);
|
|
168
359
|
const dropSpecRef = useRef(null);
|
|
169
360
|
const dropCounterRef = useRef(0);
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const committedById = useMemo(() => new Map(
|
|
361
|
+
registerController(manager, containerId, controller);
|
|
362
|
+
useEffect(() => registerController(manager, containerId, controller), [
|
|
363
|
+
manager,
|
|
364
|
+
containerId,
|
|
365
|
+
controller
|
|
366
|
+
]);
|
|
367
|
+
const committedById = useMemo(() => new Map(opts.layout.map((it) => [it.i, it])), [opts.layout]);
|
|
177
368
|
const committedByIdRef = useRef(committedById);
|
|
178
369
|
committedByIdRef.current = committedById;
|
|
179
370
|
const setSessionBoth = useCallback((next) => {
|
|
180
371
|
sessionRef.current = next;
|
|
181
|
-
setSession(next);
|
|
182
|
-
}, []);
|
|
372
|
+
controller.setSession(next);
|
|
373
|
+
}, [controller]);
|
|
374
|
+
const setKeyboard = useCallback((value) => {
|
|
375
|
+
keyboardRef.current = value;
|
|
376
|
+
controller.setKeyboard(value);
|
|
377
|
+
}, [controller]);
|
|
183
378
|
const setContainerElement = useCallback((element) => {
|
|
184
379
|
containerElRef.current = element;
|
|
185
380
|
}, []);
|
|
186
381
|
/**
|
|
187
|
-
* Is THIS grid the
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* grid wins when grids overlap (`gridAt` returns a single first match).
|
|
382
|
+
* Is THIS grid the drop target dnd-kit's collision observer resolved? Both the
|
|
383
|
+
* move-phase preview and the drop-phase commit read `operation.target`, so they
|
|
384
|
+
* always agree on which grid wins (one oracle), including when grids overlap.
|
|
191
385
|
*/
|
|
192
|
-
const overMe = useCallback((
|
|
386
|
+
const overMe = useCallback((target) => target?.id === containerIdRef.current, []);
|
|
193
387
|
/** Map a client-space pointer to a grid cell within THIS grid (see {@link receiveCell}). */
|
|
194
388
|
const cellFromPointer = useCallback((p, item) => {
|
|
195
389
|
const el = containerElRef.current;
|
|
196
390
|
if (!el) return null;
|
|
197
|
-
return receiveCell(p, el.getBoundingClientRect(),
|
|
391
|
+
return receiveCell(p, el.getBoundingClientRect(), getGrabOffset(managerRef.current), item.w, item.h, ppRef.current);
|
|
198
392
|
}, []);
|
|
199
393
|
const ctx = useCallback(() => ({
|
|
200
394
|
positionParams: ppRef.current,
|
|
@@ -202,10 +396,10 @@ function SnapGridRuntime(props) {
|
|
|
202
396
|
cols: gridRef.current.cols
|
|
203
397
|
}), []);
|
|
204
398
|
const handleDragStart = useCallback((event) => {
|
|
205
|
-
|
|
399
|
+
setKeyboard(false);
|
|
206
400
|
const data = dragData(event);
|
|
207
401
|
if (!data) {
|
|
208
|
-
const spec = externalDropSpec(event.operation.source,
|
|
402
|
+
const spec = externalDropSpec(event.operation.source, optsRef.current.dropConfig);
|
|
209
403
|
if (spec) {
|
|
210
404
|
dropCounterRef.current += 1;
|
|
211
405
|
dropSpecRef.current = {
|
|
@@ -217,7 +411,7 @@ function SnapGridRuntime(props) {
|
|
|
217
411
|
return;
|
|
218
412
|
}
|
|
219
413
|
dropSpecRef.current = null;
|
|
220
|
-
const layout =
|
|
414
|
+
const layout = optsRef.current.layout;
|
|
221
415
|
const item = layout.find((it) => it.i === data.itemId);
|
|
222
416
|
const p = event.operation.position.current;
|
|
223
417
|
const pointer = {
|
|
@@ -231,11 +425,11 @@ function SnapGridRuntime(props) {
|
|
|
231
425
|
rect: calcGridItemPosition(ppRef.current, item.x, item.y, item.w, item.h),
|
|
232
426
|
pointer
|
|
233
427
|
}, data.handle));
|
|
234
|
-
|
|
428
|
+
optsRef.current.onResizeStart?.(layout, item, item, item, event.operation.activatorEvent, null);
|
|
235
429
|
return;
|
|
236
430
|
}
|
|
237
431
|
if (item) {
|
|
238
|
-
|
|
432
|
+
setKeyboard(event.operation.activatorEvent instanceof KeyboardEvent);
|
|
239
433
|
const rect = calcGridItemPosition(ppRef.current, item.x, item.y, item.w, item.h);
|
|
240
434
|
setSessionBoth(beginDrag(layout, {
|
|
241
435
|
item,
|
|
@@ -244,22 +438,13 @@ function SnapGridRuntime(props) {
|
|
|
244
438
|
pointer
|
|
245
439
|
}));
|
|
246
440
|
const cr = (event.operation.source?.element)?.getBoundingClientRect();
|
|
247
|
-
if (cr) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
setOverlay({
|
|
253
|
-
item,
|
|
254
|
-
left: cr.left,
|
|
255
|
-
top: cr.top,
|
|
256
|
-
width: cr.width,
|
|
257
|
-
height: cr.height
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
propsRef.current.onDragStart?.(layout, item, item, item, event.operation.activatorEvent, null);
|
|
441
|
+
if (cr) setGrabOffset(managerRef.current, {
|
|
442
|
+
x: pointer.x - cr.left,
|
|
443
|
+
y: pointer.y - cr.top
|
|
444
|
+
});
|
|
445
|
+
optsRef.current.onDragStart?.(layout, item, item, item, event.operation.activatorEvent, null);
|
|
261
446
|
}
|
|
262
|
-
}, [setSessionBoth]);
|
|
447
|
+
}, [setSessionBoth, setKeyboard]);
|
|
263
448
|
const handleDragMove = useCallback((event) => {
|
|
264
449
|
if (keyboardRef.current) return;
|
|
265
450
|
const p = event.operation.position.current;
|
|
@@ -271,13 +456,14 @@ function SnapGridRuntime(props) {
|
|
|
271
456
|
if (current?.kind === "resize") {
|
|
272
457
|
const next = dragResize(current, pointer, ctx());
|
|
273
458
|
setSessionBoth(next);
|
|
274
|
-
|
|
459
|
+
optsRef.current.onResize?.(next.preview, next.anchor.item, next.placeholder, next.placeholder, event.operation.activatorEvent, null);
|
|
275
460
|
return;
|
|
276
461
|
}
|
|
462
|
+
const target = event.operation.target;
|
|
277
463
|
const data = dragData(event);
|
|
278
464
|
if (!data) {
|
|
279
465
|
const spec = dropSpecRef.current;
|
|
280
|
-
if (spec && overMe(
|
|
466
|
+
if (spec && overMe(target)) {
|
|
281
467
|
const foreign = {
|
|
282
468
|
i: spec.i,
|
|
283
469
|
x: 0,
|
|
@@ -285,7 +471,7 @@ function SnapGridRuntime(props) {
|
|
|
285
471
|
w: spec.w,
|
|
286
472
|
h: spec.h
|
|
287
473
|
};
|
|
288
|
-
const committed =
|
|
474
|
+
const committed = optsRef.current.layout;
|
|
289
475
|
const cell = cellFromPointer(pointer, foreign) ?? {
|
|
290
476
|
x: 0,
|
|
291
477
|
y: 0
|
|
@@ -295,7 +481,7 @@ function SnapGridRuntime(props) {
|
|
|
295
481
|
return;
|
|
296
482
|
}
|
|
297
483
|
if (data.kind !== "move") return;
|
|
298
|
-
const here = overMe(
|
|
484
|
+
const here = overMe(target);
|
|
299
485
|
if (committedByIdRef.current.has(data.itemId)) {
|
|
300
486
|
const source = current?.kind === "move" ? current : null;
|
|
301
487
|
if (!source) return;
|
|
@@ -307,28 +493,12 @@ function SnapGridRuntime(props) {
|
|
|
307
493
|
placeholder: null
|
|
308
494
|
};
|
|
309
495
|
setSessionBoth(next);
|
|
310
|
-
|
|
311
|
-
let oLeft = pointer.x - grab.x;
|
|
312
|
-
let oTop = pointer.y - grab.y;
|
|
313
|
-
if (propsRef.current.dragConfig?.snapToGrid && here && next.placeholder) {
|
|
314
|
-
const rect = containerElRef.current?.getBoundingClientRect();
|
|
315
|
-
if (rect) {
|
|
316
|
-
const cell = calcGridItemPosition(ppRef.current, next.placeholder.x, next.placeholder.y, next.placeholder.w, next.placeholder.h);
|
|
317
|
-
oLeft = rect.left + cell.left;
|
|
318
|
-
oTop = rect.top + cell.top;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
setOverlay((o) => o ? {
|
|
322
|
-
...o,
|
|
323
|
-
left: oLeft,
|
|
324
|
-
top: oTop
|
|
325
|
-
} : o);
|
|
326
|
-
propsRef.current.onDrag?.(next.preview, next.anchor.item, next.placeholder, next.placeholder, event.operation.activatorEvent, null);
|
|
496
|
+
optsRef.current.onDrag?.(next.preview, next.anchor.item, next.placeholder, next.placeholder, event.operation.activatorEvent, null);
|
|
327
497
|
return;
|
|
328
498
|
}
|
|
329
499
|
if (here) {
|
|
330
500
|
const foreign = data.item;
|
|
331
|
-
const committed =
|
|
501
|
+
const committed = optsRef.current.layout;
|
|
332
502
|
const cell = cellFromPointer(pointer, foreign) ?? {
|
|
333
503
|
x: 0,
|
|
334
504
|
y: 0
|
|
@@ -344,17 +514,16 @@ function SnapGridRuntime(props) {
|
|
|
344
514
|
const handleDragEnd = useCallback((event) => {
|
|
345
515
|
const current = sessionRef.current;
|
|
346
516
|
const data = dragData(event);
|
|
347
|
-
const p = event.operation.position.current;
|
|
348
517
|
const myId = containerIdRef.current;
|
|
349
|
-
const dest =
|
|
350
|
-
|
|
351
|
-
|
|
518
|
+
const dest = dropDestination({
|
|
519
|
+
keyboard: keyboardRef.current,
|
|
520
|
+
targetId: event.operation.target?.id,
|
|
521
|
+
myId
|
|
352
522
|
});
|
|
353
523
|
const ownsItem = data ? committedByIdRef.current.has(data.itemId) : false;
|
|
354
|
-
|
|
355
|
-
registryRef.current.setGrabOffset(null);
|
|
524
|
+
setGrabOffset(managerRef.current, null);
|
|
356
525
|
const native = event.nativeEvent ?? null;
|
|
357
|
-
const
|
|
526
|
+
const o = optsRef.current;
|
|
358
527
|
const action = classifyDrop({
|
|
359
528
|
kind: current?.kind ?? null,
|
|
360
529
|
canceled: event.canceled,
|
|
@@ -365,74 +534,59 @@ function SnapGridRuntime(props) {
|
|
|
365
534
|
});
|
|
366
535
|
switch (action) {
|
|
367
536
|
case "cancel-resize":
|
|
368
|
-
|
|
537
|
+
o.onResizeStop?.(o.layout, current?.anchor.item ?? null, null, null, native, null);
|
|
369
538
|
break;
|
|
370
539
|
case "cancel-move":
|
|
371
|
-
|
|
540
|
+
o.onDragStop?.(o.layout, current?.anchor.item ?? null, null, null, native, null);
|
|
372
541
|
break;
|
|
373
542
|
case "commit-resize":
|
|
374
543
|
if (current) {
|
|
375
|
-
|
|
376
|
-
|
|
544
|
+
o.onLayoutChange?.(commitLayout(current));
|
|
545
|
+
o.onResizeStop?.(current.preview, current.anchor.item, current.placeholder, current.placeholder, native, null);
|
|
377
546
|
}
|
|
378
547
|
break;
|
|
379
548
|
case "commit-in-grid":
|
|
380
549
|
case "remove-source":
|
|
381
550
|
case "revert":
|
|
382
|
-
if (action === "commit-in-grid" && current)
|
|
551
|
+
if (action === "commit-in-grid" && current) o.onLayoutChange?.(commitLayout(current));
|
|
383
552
|
else if (action === "remove-source" && data) {
|
|
384
553
|
const { compactor: c, cols } = ctx();
|
|
385
|
-
|
|
554
|
+
o.onLayoutChange?.(removeItemWithCompactor(o.layout, data.itemId, {
|
|
386
555
|
compactor: c,
|
|
387
556
|
cols
|
|
388
557
|
}));
|
|
389
558
|
}
|
|
390
|
-
|
|
559
|
+
o.onDragStop?.(current?.preview ?? o.layout, current?.anchor.item ?? null, current?.placeholder ?? null, current?.placeholder ?? null, native, null);
|
|
391
560
|
break;
|
|
392
561
|
case "commit-dest":
|
|
393
|
-
if (current)
|
|
562
|
+
if (current) o.onLayoutChange?.(commitLayout(current));
|
|
394
563
|
break;
|
|
395
564
|
case "external-drop":
|
|
396
565
|
if (current) {
|
|
397
566
|
const committed = commitLayout(current);
|
|
398
567
|
const dropped = committed.find((it) => it.i === current.activeId);
|
|
399
|
-
if (dropped)
|
|
568
|
+
if (dropped) o.onDrop?.(committed, dropped, native);
|
|
400
569
|
}
|
|
401
570
|
break;
|
|
402
571
|
}
|
|
403
572
|
dropSpecRef.current = null;
|
|
404
|
-
|
|
573
|
+
setKeyboard(false);
|
|
405
574
|
setSessionBoth(null);
|
|
406
|
-
}, [
|
|
575
|
+
}, [
|
|
576
|
+
setSessionBoth,
|
|
577
|
+
setKeyboard,
|
|
578
|
+
ctx
|
|
579
|
+
]);
|
|
407
580
|
useEffect(() => {
|
|
408
|
-
const STEP = {
|
|
409
|
-
ArrowLeft: [-1, 0],
|
|
410
|
-
ArrowRight: [1, 0],
|
|
411
|
-
ArrowUp: [0, -1],
|
|
412
|
-
ArrowDown: [0, 1]
|
|
413
|
-
};
|
|
414
581
|
const onKeyDown = (e) => {
|
|
415
582
|
if (!keyboardRef.current) return;
|
|
416
583
|
const session = sessionRef.current;
|
|
417
584
|
if (!session || session.kind !== "move") return;
|
|
418
|
-
const step =
|
|
585
|
+
const step = arrowStep(e.key);
|
|
419
586
|
if (!step) return;
|
|
420
587
|
e.preventDefault();
|
|
421
588
|
e.stopImmediatePropagation();
|
|
422
|
-
|
|
423
|
-
setSessionBoth(next);
|
|
424
|
-
const cell = next.placeholder;
|
|
425
|
-
const rect = containerElRef.current?.getBoundingClientRect();
|
|
426
|
-
if (cell && rect) {
|
|
427
|
-
const pos = calcGridItemPosition(ppRef.current, cell.x, cell.y, cell.w, cell.h);
|
|
428
|
-
setOverlay((o) => o ? {
|
|
429
|
-
...o,
|
|
430
|
-
left: rect.left + pos.left,
|
|
431
|
-
top: rect.top + pos.top,
|
|
432
|
-
width: pos.width,
|
|
433
|
-
height: pos.height
|
|
434
|
-
} : o);
|
|
435
|
-
}
|
|
589
|
+
setSessionBoth(nudge(session, step[0], step[1], ctx()));
|
|
436
590
|
};
|
|
437
591
|
window.addEventListener("keydown", onKeyDown, true);
|
|
438
592
|
return () => window.removeEventListener("keydown", onKeyDown, true);
|
|
@@ -446,12 +600,14 @@ function SnapGridRuntime(props) {
|
|
|
446
600
|
handleDragMove,
|
|
447
601
|
handleDragEnd
|
|
448
602
|
]));
|
|
449
|
-
const dragThreshold =
|
|
450
|
-
const itemSensors = useMemo(() => buildItemSensors(dragThreshold, () =>
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
603
|
+
const dragThreshold = opts.dragConfig?.threshold ?? 3;
|
|
604
|
+
const itemSensors = useMemo(() => buildItemSensors(dragThreshold, () => optsRef.current.dragConfig), [dragThreshold]);
|
|
605
|
+
const itemModifiers = useMemo(() => [SnapToGrid.configure({
|
|
606
|
+
getPositionParams: () => ppRef.current,
|
|
607
|
+
isEnabled: () => optsRef.current.dragConfig?.snapToGrid ?? false
|
|
608
|
+
})], []);
|
|
609
|
+
const gridDraggable = opts.isDraggable ?? true;
|
|
610
|
+
const dragEnabled = opts.dragConfig?.enabled ?? true;
|
|
455
611
|
const isItemDraggable = useCallback((id) => {
|
|
456
612
|
const it = committedById.get(id);
|
|
457
613
|
if (!it) return false;
|
|
@@ -461,8 +617,8 @@ function SnapGridRuntime(props) {
|
|
|
461
617
|
gridDraggable,
|
|
462
618
|
dragEnabled
|
|
463
619
|
]);
|
|
464
|
-
const gridResizable =
|
|
465
|
-
const resizeEnabled =
|
|
620
|
+
const gridResizable = opts.isResizable ?? true;
|
|
621
|
+
const resizeEnabled = opts.resizeConfig?.enabled ?? true;
|
|
466
622
|
const isItemResizable = useCallback((id) => {
|
|
467
623
|
const it = committedById.get(id);
|
|
468
624
|
if (!it) return false;
|
|
@@ -472,63 +628,25 @@ function SnapGridRuntime(props) {
|
|
|
472
628
|
gridResizable,
|
|
473
629
|
resizeEnabled
|
|
474
630
|
]);
|
|
475
|
-
const defaultHandles =
|
|
631
|
+
const defaultHandles = opts.resizeConfig?.handles;
|
|
476
632
|
const resizeHandlesFor = useCallback((id) => committedById.get(id)?.resizeHandles ?? defaultHandles ?? DEFAULT_HANDLES, [committedById, defaultHandles]);
|
|
477
|
-
|
|
478
|
-
containerId,
|
|
479
|
-
width: props.width,
|
|
480
|
-
autoSize: props.autoSize ?? true,
|
|
481
|
-
gridConfig,
|
|
633
|
+
controller.setConfig({
|
|
482
634
|
positionParams,
|
|
483
|
-
renderedLayout,
|
|
484
|
-
itemsById,
|
|
485
|
-
session,
|
|
486
|
-
isItemDraggable,
|
|
487
|
-
isItemResizable,
|
|
488
|
-
resizeHandlesFor,
|
|
489
|
-
itemSensors,
|
|
490
|
-
setContainerElement,
|
|
491
|
-
overlay
|
|
492
|
-
}), [
|
|
493
|
-
containerId,
|
|
494
|
-
props.width,
|
|
495
|
-
props.autoSize,
|
|
496
635
|
gridConfig,
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
636
|
+
width: opts.width,
|
|
637
|
+
autoSize: opts.autoSize ?? true,
|
|
638
|
+
itemSensors,
|
|
639
|
+
itemModifiers,
|
|
501
640
|
isItemDraggable,
|
|
502
641
|
isItemResizable,
|
|
503
642
|
resizeHandlesFor,
|
|
504
|
-
|
|
505
|
-
setContainerElement,
|
|
506
|
-
overlay
|
|
507
|
-
]);
|
|
508
|
-
return /* @__PURE__ */ jsx(GridContext.Provider, {
|
|
509
|
-
value: runtime,
|
|
510
|
-
children: props.children
|
|
643
|
+
setContainerElement
|
|
511
644
|
});
|
|
512
|
-
|
|
513
|
-
//#endregion
|
|
514
|
-
//#region src/SnapGridGroup.tsx
|
|
515
|
-
/**
|
|
516
|
-
* Wrap multiple grids to let tiles be dragged **between** them. Provides one
|
|
517
|
-
* shared dnd-kit `DragDropProvider` and a registry so each grid can tell which
|
|
518
|
-
* grid the pointer is over.
|
|
519
|
-
*
|
|
520
|
-
* Item ids must be unique across all grids in a group (they share one manager).
|
|
521
|
-
*/
|
|
522
|
-
function SnapGridGroup({ children }) {
|
|
523
|
-
const registryRef = useRef(null);
|
|
524
|
-
if (!registryRef.current) registryRef.current = createGridRegistry();
|
|
525
|
-
return /* @__PURE__ */ jsx(DragDropProvider, { children: /* @__PURE__ */ jsx(SnapGridGroupContext.Provider, {
|
|
526
|
-
value: registryRef.current,
|
|
527
|
-
children
|
|
528
|
-
}) });
|
|
645
|
+
return controller;
|
|
529
646
|
}
|
|
530
647
|
//#endregion
|
|
531
648
|
//#region src/hooks/useGridContainer.ts
|
|
649
|
+
const GRID_COLLISION_PRIORITY = 10;
|
|
532
650
|
/** Total container height in pixels for the given number of occupied rows. */
|
|
533
651
|
function containerHeight(rows, grid) {
|
|
534
652
|
const padY = (grid.containerPadding ?? grid.margin)[1];
|
|
@@ -536,81 +654,166 @@ function containerHeight(rows, grid) {
|
|
|
536
654
|
return padY * 2 + rows * grid.rowHeight + (rows - 1) * grid.margin[1];
|
|
537
655
|
}
|
|
538
656
|
/**
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
* onto your own container element.
|
|
657
|
+
* The grid host: creates this grid's controller + drag monitor (see
|
|
658
|
+
* {@link useGridController}), registers the droppable surface, and returns props
|
|
659
|
+
* to spread onto your own container element. Render `useGridItem` tiles inside,
|
|
660
|
+
* passing `group` (this grid's id) so they resolve this controller.
|
|
542
661
|
*/
|
|
543
|
-
function useGridContainer() {
|
|
544
|
-
const
|
|
662
|
+
function useGridContainer(opts) {
|
|
663
|
+
const controller = useGridController(opts);
|
|
664
|
+
const { width, autoSize, gridConfig, setContainerElement } = controller.config;
|
|
545
665
|
const { ref, isDropTarget } = useDroppable$1({
|
|
546
|
-
id:
|
|
666
|
+
id: controller.id,
|
|
547
667
|
type: "grid",
|
|
548
|
-
accept:
|
|
668
|
+
accept: (source) => {
|
|
669
|
+
if (source.type === "grid-item") return true;
|
|
670
|
+
return source.data?.snapGridDrop != null;
|
|
671
|
+
},
|
|
672
|
+
collisionPriority: GRID_COLLISION_PRIORITY
|
|
549
673
|
});
|
|
550
|
-
const setContainerElement = rt.setContainerElement;
|
|
551
674
|
const setRef = useCallback((element) => {
|
|
552
675
|
ref(element);
|
|
553
676
|
setContainerElement(element);
|
|
554
677
|
}, [ref, setContainerElement]);
|
|
555
|
-
const
|
|
678
|
+
const renderedLayout = useSyncExternalStore(controller.subscribe, controller.renderedSnapshot, controller.renderedSnapshot);
|
|
556
679
|
return {
|
|
557
680
|
containerProps: {
|
|
558
681
|
ref: setRef,
|
|
559
682
|
style: {
|
|
560
683
|
position: "relative",
|
|
561
|
-
width
|
|
562
|
-
height
|
|
684
|
+
width,
|
|
685
|
+
height: autoSize ? containerHeight(bottom(renderedLayout), gridConfig) : void 0
|
|
563
686
|
},
|
|
564
687
|
"data-drop-target": isDropTarget || void 0
|
|
565
688
|
},
|
|
566
|
-
isDropTarget
|
|
689
|
+
isDropTarget,
|
|
690
|
+
group: controller.id,
|
|
691
|
+
controller
|
|
567
692
|
};
|
|
568
693
|
}
|
|
694
|
+
const REFLOW_EASING = "ease";
|
|
695
|
+
const REFLOW_TRANSITION = `transform 150ms ${REFLOW_EASING}, width 150ms ${REFLOW_EASING}, height 150ms ${REFLOW_EASING}`;
|
|
696
|
+
//#endregion
|
|
697
|
+
//#region src/hooks/useResolveController.ts
|
|
698
|
+
/**
|
|
699
|
+
* Resolve a grid's controller by its `group` (= the grid's id), scoped to the
|
|
700
|
+
* ambient dnd-kit manager. Items declare `group` (mirroring useSortable); the
|
|
701
|
+
* container registered the controller under that id. Throws a helpful error if
|
|
702
|
+
* unresolved — almost always a missing `group` or a tile rendered outside any
|
|
703
|
+
* grid / `DragDropProvider`.
|
|
704
|
+
*/
|
|
705
|
+
function useResolveController(group) {
|
|
706
|
+
const controller = getController(useDragDropManager(), group);
|
|
707
|
+
if (!controller) throw new Error(`snapgrid: no grid found for group "${group}". A grid item must pass the group returned by its grid's useGridContainer, and render inside a <DragDropProvider> (or use <GridLayout>, which wires this for you).`);
|
|
708
|
+
return controller;
|
|
709
|
+
}
|
|
569
710
|
//#endregion
|
|
570
711
|
//#region src/hooks/useGridItem.ts
|
|
571
|
-
const
|
|
712
|
+
const ITEM_FEEDBACK = [Feedback$1.configure({
|
|
713
|
+
feedback: (_source, manager) => isKeyboardEvent(manager.dragOperation.activatorEvent) ? "none" : "default",
|
|
714
|
+
dropAnimation: null
|
|
715
|
+
})];
|
|
572
716
|
/**
|
|
573
|
-
* Headless hook for a single grid
|
|
574
|
-
*
|
|
575
|
-
*
|
|
717
|
+
* Headless hook for a single grid tile. The tile is a real `useSortable` (a
|
|
718
|
+
* draggable + droppable carrying `group`/`index`/`type`/`accept`), so it
|
|
719
|
+
* interoperates with the dnd-kit sortable ecosystem, yet it is positioned by RGL
|
|
720
|
+
* via the {@link GridController}. `group` is the owning grid's id (from its
|
|
721
|
+
* {@link useGridContainer}), mirroring `useSortable`'s `group`. Spread the returned
|
|
722
|
+
* `ref`, optional `handleRef`, positioning `style`, and drag state onto whatever
|
|
723
|
+
* element you render — you own the tag, className, content, and cosmetic styling.
|
|
724
|
+
*
|
|
725
|
+
* The dragged tile floats itself via dnd-kit's default feedback (no `<DragOverlay>`):
|
|
726
|
+
* the active tile renders at its committed origin so the float offset composes, and
|
|
727
|
+
* reflow is animated on the compositor via the Web Animations API — both so it stays
|
|
728
|
+
* smooth in Safari, where the float's popover top-layer repaint would jank a
|
|
729
|
+
* CSS-transition reflow.
|
|
576
730
|
*/
|
|
577
|
-
function useGridItem(id) {
|
|
578
|
-
const
|
|
579
|
-
const
|
|
580
|
-
const
|
|
731
|
+
function useGridItem(id, group) {
|
|
732
|
+
const controller = useResolveController(group);
|
|
733
|
+
const snap = useSyncExternalStore(controller.subscribe, () => controller.itemSnapshot(id), () => controller.itemSnapshot(id));
|
|
734
|
+
const item = snap.item;
|
|
735
|
+
const active = snap.isDragging;
|
|
736
|
+
const hidden = snap.hidden;
|
|
737
|
+
const config = controller.config;
|
|
738
|
+
const wasActive = useRef(false);
|
|
739
|
+
const justDropped = wasActive.current && !active;
|
|
740
|
+
wasActive.current = active;
|
|
741
|
+
const data = useMemo(() => ({ snapGrid: {
|
|
742
|
+
kind: "move",
|
|
743
|
+
itemId: id,
|
|
744
|
+
item
|
|
745
|
+
} }), [id, item]);
|
|
746
|
+
const { ref: sortableRef, handleRef, isDragging } = useSortable({
|
|
581
747
|
id,
|
|
748
|
+
index: controller.itemIndex(id),
|
|
749
|
+
group,
|
|
582
750
|
type: "grid-item",
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
item
|
|
590
|
-
} }
|
|
751
|
+
accept: "grid-item",
|
|
752
|
+
disabled: !config.isItemDraggable(id),
|
|
753
|
+
sensors: config.itemSensors,
|
|
754
|
+
modifiers: config.itemModifiers,
|
|
755
|
+
plugins: (defaults) => [...defaults, ...ITEM_FEEDBACK],
|
|
756
|
+
data
|
|
591
757
|
});
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
};
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
758
|
+
const elRef = useRef(null);
|
|
759
|
+
const setRef = useCallback((element) => {
|
|
760
|
+
sortableRef(element);
|
|
761
|
+
elRef.current = element;
|
|
762
|
+
}, [sortableRef]);
|
|
763
|
+
const session = controller.getSession();
|
|
764
|
+
const dragging = session != null;
|
|
765
|
+
const posItem = item ? active && hidden ? session?.anchor.item ?? item : item : void 0;
|
|
766
|
+
const pos = posItem ? calcGridItemPosition(config.positionParams, posItem.x, posItem.y, posItem.w, posItem.h) : void 0;
|
|
767
|
+
const posLeft = pos?.left;
|
|
768
|
+
const posTop = pos?.top;
|
|
769
|
+
const prev = useRef(null);
|
|
770
|
+
const reflowAnim = useRef(null);
|
|
771
|
+
useLayoutEffect(() => {
|
|
772
|
+
const cur = posLeft != null && posTop != null ? {
|
|
773
|
+
left: posLeft,
|
|
774
|
+
top: posTop
|
|
775
|
+
} : null;
|
|
776
|
+
const before = prev.current;
|
|
777
|
+
prev.current = cur;
|
|
778
|
+
const el = elRef.current;
|
|
779
|
+
if (!el || !cur || !before || active || justDropped || !dragging) return;
|
|
780
|
+
if (before.left === cur.left && before.top === cur.top) return;
|
|
781
|
+
let fromX = before.left;
|
|
782
|
+
let fromY = before.top;
|
|
783
|
+
if (reflowAnim.current?.playState === "running") {
|
|
784
|
+
const m = new DOMMatrix(getComputedStyle(el).transform);
|
|
785
|
+
fromX = m.m41;
|
|
786
|
+
fromY = m.m42;
|
|
787
|
+
}
|
|
788
|
+
reflowAnim.current?.cancel();
|
|
789
|
+
reflowAnim.current = el.animate([{ transform: `translate(${fromX}px, ${fromY}px)` }, { transform: `translate(${cur.left}px, ${cur.top}px)` }], {
|
|
790
|
+
duration: 150,
|
|
791
|
+
easing: REFLOW_EASING
|
|
792
|
+
});
|
|
793
|
+
}, [
|
|
794
|
+
posLeft,
|
|
795
|
+
posTop,
|
|
796
|
+
active,
|
|
797
|
+
justDropped,
|
|
798
|
+
dragging
|
|
799
|
+
]);
|
|
800
|
+
useEffect(() => () => reflowAnim.current?.cancel(), []);
|
|
801
|
+
return {
|
|
802
|
+
ref: setRef,
|
|
803
|
+
handleRef,
|
|
804
|
+
style: pos ? {
|
|
600
805
|
position: "absolute",
|
|
601
806
|
left: 0,
|
|
602
807
|
top: 0,
|
|
603
808
|
width: pos.width,
|
|
604
809
|
height: pos.height,
|
|
605
810
|
transform: `translate(${pos.left}px, ${pos.top}px)`,
|
|
606
|
-
|
|
607
|
-
transition: active ? "none" : REFLOW_TRANSITION,
|
|
811
|
+
transition: active || justDropped || dragging ? "none" : REFLOW_TRANSITION,
|
|
608
812
|
touchAction: "none"
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
style,
|
|
813
|
+
} : {
|
|
814
|
+
position: "absolute",
|
|
815
|
+
touchAction: "none"
|
|
816
|
+
},
|
|
614
817
|
isDragging,
|
|
615
818
|
item
|
|
616
819
|
};
|
|
@@ -619,13 +822,14 @@ function useGridItem(id) {
|
|
|
619
822
|
//#region src/hooks/useGridPlaceholder.ts
|
|
620
823
|
/**
|
|
621
824
|
* Headless hook returning where the drag placeholder should be rendered, or
|
|
622
|
-
* `null` when no drag is in progress.
|
|
825
|
+
* `null` when no drag is in progress. `group` is the owning grid's id (from its
|
|
826
|
+
* {@link useGridContainer}). You render the element however you like.
|
|
623
827
|
*/
|
|
624
|
-
function useGridPlaceholder() {
|
|
625
|
-
const
|
|
626
|
-
const placeholder =
|
|
828
|
+
function useGridPlaceholder(group) {
|
|
829
|
+
const controller = useResolveController(group);
|
|
830
|
+
const placeholder = useSyncExternalStore(controller.subscribe, controller.placeholderSnapshot, controller.placeholderSnapshot);
|
|
627
831
|
if (!placeholder) return null;
|
|
628
|
-
const pos = calcGridItemPosition(
|
|
832
|
+
const pos = calcGridItemPosition(controller.config.positionParams, placeholder.x, placeholder.y, placeholder.w, placeholder.h);
|
|
629
833
|
return {
|
|
630
834
|
item: placeholder,
|
|
631
835
|
style: {
|
|
@@ -643,14 +847,15 @@ function useGridPlaceholder() {
|
|
|
643
847
|
//#region src/hooks/useGridResizeHandle.ts
|
|
644
848
|
/**
|
|
645
849
|
* Headless hook for a single resize handle. Model a handle as its own draggable;
|
|
646
|
-
* dragging it resizes the item from the given edge/corner.
|
|
647
|
-
*
|
|
850
|
+
* dragging it resizes the item from the given edge/corner. `group` is the owning
|
|
851
|
+
* grid's id (from its {@link useGridContainer}). Spread `ref` and `handleProps`
|
|
852
|
+
* onto the handle element you position/style.
|
|
648
853
|
*/
|
|
649
|
-
function useGridResizeHandle(itemId, handle) {
|
|
650
|
-
const
|
|
854
|
+
function useGridResizeHandle(itemId, handle, group) {
|
|
855
|
+
const controller = useResolveController(group);
|
|
651
856
|
const { ref } = useDraggable$1({
|
|
652
857
|
id: `${itemId}::resize::${handle}`,
|
|
653
|
-
disabled: !
|
|
858
|
+
disabled: !controller.config?.isItemResizable(itemId),
|
|
654
859
|
plugins: NO_FEEDBACK,
|
|
655
860
|
data: { snapGrid: {
|
|
656
861
|
kind: "resize",
|
|
@@ -658,7 +863,7 @@ function useGridResizeHandle(itemId, handle) {
|
|
|
658
863
|
handle
|
|
659
864
|
} }
|
|
660
865
|
});
|
|
661
|
-
const isResizing =
|
|
866
|
+
const { isResizing } = useSyncExternalStore(controller.subscribe, () => controller.resizeSnapshot(itemId), () => controller.resizeSnapshot(itemId));
|
|
662
867
|
return {
|
|
663
868
|
ref,
|
|
664
869
|
handleProps: { [RESIZE_HANDLE_ATTR]: true },
|
|
@@ -666,30 +871,6 @@ function useGridResizeHandle(itemId, handle) {
|
|
|
666
871
|
};
|
|
667
872
|
}
|
|
668
873
|
//#endregion
|
|
669
|
-
//#region src/hooks/useGridDragOverlay.ts
|
|
670
|
-
/**
|
|
671
|
-
* Headless hook for the floating drag preview. Returns `null` unless this grid
|
|
672
|
-
* is the source of an in-progress drag. Render the returned `item` with `style`
|
|
673
|
-
* in a portal at `document.body` so it can float across grids unclipped (see
|
|
674
|
-
* {@link GridDragOverlay} for the convenience component).
|
|
675
|
-
*/
|
|
676
|
-
function useGridDragOverlay() {
|
|
677
|
-
const o = useGridRuntime().overlay;
|
|
678
|
-
if (!o) return null;
|
|
679
|
-
return {
|
|
680
|
-
item: o.item,
|
|
681
|
-
style: {
|
|
682
|
-
position: "fixed",
|
|
683
|
-
left: o.left,
|
|
684
|
-
top: o.top,
|
|
685
|
-
width: o.width,
|
|
686
|
-
height: o.height,
|
|
687
|
-
pointerEvents: "none",
|
|
688
|
-
zIndex: 1e3
|
|
689
|
-
}
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
//#endregion
|
|
693
874
|
//#region src/hooks/useResponsiveLayout.ts
|
|
694
875
|
/** react-grid-layout's default breakpoints (px) and column counts. */
|
|
695
876
|
const DEFAULT_BREAKPOINTS = {
|
|
@@ -756,26 +937,7 @@ function useResponsiveLayout(options) {
|
|
|
756
937
|
};
|
|
757
938
|
}
|
|
758
939
|
//#endregion
|
|
759
|
-
//#region src/
|
|
760
|
-
/**
|
|
761
|
-
* Renders the floating drag preview in a portal at `document.body` — so it
|
|
762
|
-
* follows the pointer across grids without being clipped by any container.
|
|
763
|
-
* Renders nothing when this grid isn't the drag source.
|
|
764
|
-
*/
|
|
765
|
-
function GridDragOverlay({ children, className, style }) {
|
|
766
|
-
const overlay = useGridDragOverlay();
|
|
767
|
-
if (typeof document === "undefined" || !overlay) return null;
|
|
768
|
-
return createPortal(/* @__PURE__ */ jsx("div", {
|
|
769
|
-
className: className ? `snapgrid-overlay ${className}` : "snapgrid-overlay",
|
|
770
|
-
style: style ? {
|
|
771
|
-
...overlay.style,
|
|
772
|
-
...style
|
|
773
|
-
} : overlay.style,
|
|
774
|
-
children: children(overlay.item)
|
|
775
|
-
}), document.body);
|
|
776
|
-
}
|
|
777
|
-
//#endregion
|
|
778
|
-
//#region src/GridItem.tsx
|
|
940
|
+
//#region src/components/GridItem.tsx
|
|
779
941
|
const HANDLE_CURSOR = {
|
|
780
942
|
n: "ns-resize",
|
|
781
943
|
s: "ns-resize",
|
|
@@ -804,8 +966,8 @@ function handleStyle(handle) {
|
|
|
804
966
|
if (handle === "e" || handle === "w") s.top = `calc(50% - ${SIDE / 2}px)`;
|
|
805
967
|
return s;
|
|
806
968
|
}
|
|
807
|
-
function DefaultResizeHandle({ itemId, handle }) {
|
|
808
|
-
const { ref, handleProps } = useGridResizeHandle(itemId, handle);
|
|
969
|
+
function DefaultResizeHandle({ itemId, handle, group }) {
|
|
970
|
+
const { ref, handleProps } = useGridResizeHandle(itemId, handle, group);
|
|
809
971
|
return /* @__PURE__ */ jsx("span", {
|
|
810
972
|
ref,
|
|
811
973
|
...handleProps,
|
|
@@ -816,12 +978,19 @@ function DefaultResizeHandle({ itemId, handle }) {
|
|
|
816
978
|
/**
|
|
817
979
|
* Convenience wrapper over {@link useGridItem}: an absolutely-positioned `<div>`
|
|
818
980
|
* with stable hooks (`.snapgrid-item`, `data-grid-id`, `data-dragging`) and the
|
|
819
|
-
* configured resize handles. For full control,
|
|
981
|
+
* configured resize handles. `group` is the owning grid's id. For full control,
|
|
982
|
+
* use the hooks directly.
|
|
983
|
+
*
|
|
984
|
+
* Memoized so re-rendering the surface (e.g. its auto-height tracking the drag)
|
|
985
|
+
* doesn't re-render every tile — a tile re-renders only when its own slice
|
|
986
|
+
* changes (via useGridItem's subscription). Keeps a drag's React work scoped to
|
|
987
|
+
* the moved tile (see renderScope.test).
|
|
820
988
|
*/
|
|
821
|
-
function
|
|
822
|
-
const
|
|
823
|
-
const { ref, style: positionStyle, isDragging } = useGridItem(id);
|
|
824
|
-
const
|
|
989
|
+
function GridItemImpl({ id, group, children, className, style }) {
|
|
990
|
+
const controller = useResolveController(group);
|
|
991
|
+
const { ref, style: positionStyle, isDragging } = useGridItem(id, group);
|
|
992
|
+
const config = controller.config;
|
|
993
|
+
const handles = config?.isItemResizable(id) ? config.resizeHandlesFor(id) : [];
|
|
825
994
|
return /* @__PURE__ */ jsxs("div", {
|
|
826
995
|
ref,
|
|
827
996
|
"data-grid-id": id,
|
|
@@ -833,27 +1002,29 @@ function GridItem({ id, children, className, style }) {
|
|
|
833
1002
|
} : positionStyle,
|
|
834
1003
|
children: [children, handles.map((handle) => /* @__PURE__ */ jsx(DefaultResizeHandle, {
|
|
835
1004
|
itemId: id,
|
|
836
|
-
handle
|
|
1005
|
+
handle,
|
|
1006
|
+
group
|
|
837
1007
|
}, handle))]
|
|
838
1008
|
});
|
|
839
1009
|
}
|
|
1010
|
+
const GridItem = memo(GridItemImpl);
|
|
840
1011
|
//#endregion
|
|
841
|
-
//#region src/GridPlaceholder.tsx
|
|
1012
|
+
//#region src/components/GridPlaceholder.tsx
|
|
842
1013
|
const DEFAULT_LOOK = {
|
|
843
1014
|
background: "rgba(99, 102, 241, 0.2)",
|
|
844
1015
|
border: "1px dashed rgba(99, 102, 241, 0.6)",
|
|
845
1016
|
borderRadius: 4,
|
|
846
1017
|
boxSizing: "border-box",
|
|
847
1018
|
zIndex: 2,
|
|
848
|
-
transition:
|
|
1019
|
+
transition: REFLOW_TRANSITION
|
|
849
1020
|
};
|
|
850
1021
|
/**
|
|
851
1022
|
* Convenience placeholder rendered from {@link useGridPlaceholder}. Renders
|
|
852
1023
|
* nothing when no drag is active. For a custom placeholder, call the hook
|
|
853
1024
|
* directly and render your own element with the returned `style`.
|
|
854
1025
|
*/
|
|
855
|
-
function GridPlaceholder({ className, style }) {
|
|
856
|
-
const placeholder = useGridPlaceholder();
|
|
1026
|
+
function GridPlaceholder({ group, className, style }) {
|
|
1027
|
+
const placeholder = useGridPlaceholder(group);
|
|
857
1028
|
if (!placeholder) return null;
|
|
858
1029
|
return /* @__PURE__ */ jsx("div", {
|
|
859
1030
|
"aria-hidden": "true",
|
|
@@ -866,18 +1037,15 @@ function GridPlaceholder({ className, style }) {
|
|
|
866
1037
|
});
|
|
867
1038
|
}
|
|
868
1039
|
//#endregion
|
|
869
|
-
//#region src/GridLayout.tsx
|
|
1040
|
+
//#region src/components/GridLayout.tsx
|
|
1041
|
+
const InProvider = createContext(false);
|
|
870
1042
|
/** Strip the namespacing prefix React applies to keys inside `Children.map`. */
|
|
871
1043
|
function keyToId(key) {
|
|
872
1044
|
return key.startsWith(".$") ? key.slice(2) : key;
|
|
873
1045
|
}
|
|
874
1046
|
/** The default surface: positioned container + mapped items + placeholder. */
|
|
875
|
-
function GridSurface({ className, style, children }) {
|
|
876
|
-
const { containerProps } = useGridContainer();
|
|
877
|
-
const childById = /* @__PURE__ */ new Map();
|
|
878
|
-
Children.forEach(children, (child) => {
|
|
879
|
-
if (isValidElement(child) && child.key != null) childById.set(keyToId(String(child.key)), child);
|
|
880
|
-
});
|
|
1047
|
+
function GridSurface({ className, style, children, ...opts }) {
|
|
1048
|
+
const { containerProps, group } = useGridContainer(opts);
|
|
881
1049
|
return /* @__PURE__ */ jsxs("div", {
|
|
882
1050
|
...containerProps,
|
|
883
1051
|
className: className ? `snapgrid ${className}` : "snapgrid",
|
|
@@ -885,38 +1053,50 @@ function GridSurface({ className, style, children }) {
|
|
|
885
1053
|
...containerProps.style,
|
|
886
1054
|
...style
|
|
887
1055
|
} : containerProps.style,
|
|
888
|
-
children: [
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
/* @__PURE__ */ jsx(GridPlaceholder, {}),
|
|
897
|
-
/* @__PURE__ */ jsx(GridDragOverlay, { children: (item) => childById.get(item.i) ?? null })
|
|
898
|
-
]
|
|
1056
|
+
children: [Children.map(children, (child) => {
|
|
1057
|
+
if (!isValidElement(child) || child.key == null) return child;
|
|
1058
|
+
return /* @__PURE__ */ jsx(GridItem, {
|
|
1059
|
+
id: keyToId(String(child.key)),
|
|
1060
|
+
group,
|
|
1061
|
+
children: child
|
|
1062
|
+
}, child.key);
|
|
1063
|
+
}), /* @__PURE__ */ jsx(GridPlaceholder, { group })]
|
|
899
1064
|
});
|
|
900
1065
|
}
|
|
901
1066
|
/**
|
|
902
1067
|
* Drop-in grid component: a controlled, react-grid-layout v2-compatible layout
|
|
903
|
-
* backed by dnd-kit. A thin shell over {@link
|
|
1068
|
+
* backed by dnd-kit. A thin shell over {@link useGridContainer} and the headless
|
|
904
1069
|
* hooks — children are keyed by their layout item's `i`. For full control over
|
|
905
|
-
* markup/styling, use the
|
|
1070
|
+
* markup/styling, use the hooks directly.
|
|
1071
|
+
*
|
|
1072
|
+
* Supplies the dnd-kit `DragDropProvider` for the turnkey case so consumers
|
|
1073
|
+
* don't manage one. Nest multiple `GridLayout`s and they share one provider
|
|
1074
|
+
* (the seam for cross-grid drags); a consumer's own provider is also honored.
|
|
906
1075
|
*/
|
|
907
1076
|
function GridLayout(props) {
|
|
908
|
-
const
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1077
|
+
const inProvider = useContext(InProvider);
|
|
1078
|
+
const surface = /* @__PURE__ */ jsx(GridSurface, { ...props });
|
|
1079
|
+
if (inProvider) return surface;
|
|
1080
|
+
return /* @__PURE__ */ jsx(DragDropProvider, { children: /* @__PURE__ */ jsx(InProvider.Provider, {
|
|
1081
|
+
value: true,
|
|
1082
|
+
children: surface
|
|
1083
|
+
}) });
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Share one dnd-kit `DragDropProvider` across several sibling grids so tiles can
|
|
1087
|
+
* be dragged between them. (Nested `GridLayout`s already share a provider; this
|
|
1088
|
+
* is for siblings.) A thin wrapper over `DragDropProvider` — the cross-grid seam
|
|
1089
|
+
* is the shared manager + collision target.
|
|
1090
|
+
*/
|
|
1091
|
+
function SnapGridGroup({ children }) {
|
|
1092
|
+
if (useContext(InProvider)) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
1093
|
+
return /* @__PURE__ */ jsx(DragDropProvider, { children: /* @__PURE__ */ jsx(InProvider.Provider, {
|
|
1094
|
+
value: true,
|
|
1095
|
+
children
|
|
1096
|
+
}) });
|
|
917
1097
|
}
|
|
918
1098
|
//#endregion
|
|
919
|
-
//#region src/ResponsiveGridLayout.tsx
|
|
1099
|
+
//#region src/components/ResponsiveGridLayout.tsx
|
|
920
1100
|
/**
|
|
921
1101
|
* A responsive grid: switches column count and layout by breakpoint as `width`
|
|
922
1102
|
* changes, generating a breakpoint's layout from the nearest one when absent.
|
|
@@ -994,6 +1174,6 @@ function useContainerWidth(options = {}) {
|
|
|
994
1174
|
};
|
|
995
1175
|
}
|
|
996
1176
|
//#endregion
|
|
997
|
-
export { DEFAULT_BREAKPOINTS, DEFAULT_BREAKPOINT_COLS,
|
|
1177
|
+
export { DEFAULT_BREAKPOINTS, DEFAULT_BREAKPOINT_COLS, DragOverlay, Feedback, GridItem, GridLayout, GridPlaceholder, KeyboardSensor, PointerSensor, ResponsiveGridLayout, SnapGridGroup, getCompactor, horizontalCompactor, noCompactor, useContainerWidth, useDraggable, useDroppable, useGridContainer, useGridItem, useGridPlaceholder, useGridResizeHandle, useResponsiveLayout, verticalCompactor };
|
|
998
1178
|
|
|
999
1179
|
//# sourceMappingURL=index.mjs.map
|