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