@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.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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
+
}
|
|
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
|
+
};
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/controller/registry.ts
|
|
147
|
+
/**
|
|
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).
|
|
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
|
+
};
|
|
15
196
|
}
|
|
16
197
|
//#endregion
|
|
17
|
-
//#region src/dragFlow.ts
|
|
198
|
+
//#region src/dnd/dragFlow.ts
|
|
18
199
|
/**
|
|
19
|
-
* Pure decision helpers for the drag interaction
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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.
|
|
22
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,30 @@ 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
|
-
/** Read snapgrid's payload off a dnd-kit drag source. */
|
|
115
|
-
function dragData(event) {
|
|
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
|
-
};
|
|
130
|
-
}
|
|
131
325
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
326
|
+
* The grid's brain: owns the {@link GridController}, runs the dnd-kit drag/resize
|
|
327
|
+
* monitor for this grid, and writes per-grid config to the controller each render.
|
|
328
|
+
* Created by {@link useGridContainer}; items resolve the same controller by their
|
|
329
|
+
* `group` (= this grid's id) from the per-manager registry. Consumes the ambient
|
|
330
|
+
* `DragDropProvider` — it does not mint one.
|
|
136
331
|
*/
|
|
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) {
|
|
332
|
+
function useGridController(opts) {
|
|
146
333
|
const autoId = (0, react.useId)();
|
|
147
|
-
const containerId =
|
|
334
|
+
const containerId = opts.id ?? autoId;
|
|
148
335
|
const gridConfig = (0, react.useMemo)(() => ({
|
|
149
336
|
..._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
|
-
|
|
337
|
+
...opts.gridConfig
|
|
338
|
+
}), [opts.gridConfig]);
|
|
339
|
+
const positionParams = (0, react.useMemo)(() => (0, _snapgridjs_core.toPositionParams)(gridConfig, opts.width), [gridConfig, opts.width]);
|
|
340
|
+
const compactor = opts.compactor ?? _snapgridjs_core.verticalCompactor;
|
|
341
|
+
const manager = (0, _dnd_kit_react.useDragDropManager)();
|
|
342
|
+
const controller = (0, _dnd_kit_react.useInstance)((m) => new GridController(containerId, opts.layout, m ?? void 0));
|
|
343
|
+
controller.setCommitted(opts.layout);
|
|
344
|
+
if (controller.id !== containerId) controller.setId(containerId);
|
|
345
|
+
const optsRef = (0, react.useRef)(opts);
|
|
346
|
+
optsRef.current = opts;
|
|
158
347
|
const ppRef = (0, react.useRef)(positionParams);
|
|
159
348
|
ppRef.current = positionParams;
|
|
160
349
|
const gridRef = (0, react.useRef)(gridConfig);
|
|
@@ -163,39 +352,44 @@ function SnapGridRuntime(props) {
|
|
|
163
352
|
compactorRef.current = compactor;
|
|
164
353
|
const containerIdRef = (0, react.useRef)(containerId);
|
|
165
354
|
containerIdRef.current = containerId;
|
|
355
|
+
const managerRef = (0, react.useRef)(manager);
|
|
356
|
+
managerRef.current = manager;
|
|
166
357
|
const sessionRef = (0, react.useRef)(null);
|
|
167
358
|
const containerElRef = (0, react.useRef)(null);
|
|
168
359
|
const keyboardRef = (0, react.useRef)(false);
|
|
169
360
|
const dropSpecRef = (0, react.useRef)(null);
|
|
170
361
|
const dropCounterRef = (0, react.useRef)(0);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const committedById = (0, react.useMemo)(() => new Map(
|
|
362
|
+
registerController(manager, containerId, controller);
|
|
363
|
+
(0, react.useEffect)(() => registerController(manager, containerId, controller), [
|
|
364
|
+
manager,
|
|
365
|
+
containerId,
|
|
366
|
+
controller
|
|
367
|
+
]);
|
|
368
|
+
const committedById = (0, react.useMemo)(() => new Map(opts.layout.map((it) => [it.i, it])), [opts.layout]);
|
|
178
369
|
const committedByIdRef = (0, react.useRef)(committedById);
|
|
179
370
|
committedByIdRef.current = committedById;
|
|
180
371
|
const setSessionBoth = (0, react.useCallback)((next) => {
|
|
181
372
|
sessionRef.current = next;
|
|
182
|
-
setSession(next);
|
|
183
|
-
}, []);
|
|
373
|
+
controller.setSession(next);
|
|
374
|
+
}, [controller]);
|
|
375
|
+
const setKeyboard = (0, react.useCallback)((value) => {
|
|
376
|
+
keyboardRef.current = value;
|
|
377
|
+
controller.setKeyboard(value);
|
|
378
|
+
}, [controller]);
|
|
184
379
|
const setContainerElement = (0, react.useCallback)((element) => {
|
|
185
380
|
containerElRef.current = element;
|
|
186
381
|
}, []);
|
|
187
382
|
/**
|
|
188
|
-
* Is THIS grid the
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* grid wins when grids overlap (`gridAt` returns a single first match).
|
|
383
|
+
* Is THIS grid the drop target dnd-kit's collision observer resolved? Both the
|
|
384
|
+
* move-phase preview and the drop-phase commit read `operation.target`, so they
|
|
385
|
+
* always agree on which grid wins (one oracle), including when grids overlap.
|
|
192
386
|
*/
|
|
193
|
-
const overMe = (0, react.useCallback)((
|
|
387
|
+
const overMe = (0, react.useCallback)((target) => target?.id === containerIdRef.current, []);
|
|
194
388
|
/** Map a client-space pointer to a grid cell within THIS grid (see {@link receiveCell}). */
|
|
195
389
|
const cellFromPointer = (0, react.useCallback)((p, item) => {
|
|
196
390
|
const el = containerElRef.current;
|
|
197
391
|
if (!el) return null;
|
|
198
|
-
return receiveCell(p, el.getBoundingClientRect(),
|
|
392
|
+
return receiveCell(p, el.getBoundingClientRect(), getGrabOffset(managerRef.current), item.w, item.h, ppRef.current);
|
|
199
393
|
}, []);
|
|
200
394
|
const ctx = (0, react.useCallback)(() => ({
|
|
201
395
|
positionParams: ppRef.current,
|
|
@@ -203,10 +397,10 @@ function SnapGridRuntime(props) {
|
|
|
203
397
|
cols: gridRef.current.cols
|
|
204
398
|
}), []);
|
|
205
399
|
const handleDragStart = (0, react.useCallback)((event) => {
|
|
206
|
-
|
|
400
|
+
setKeyboard(false);
|
|
207
401
|
const data = dragData(event);
|
|
208
402
|
if (!data) {
|
|
209
|
-
const spec = externalDropSpec(event.operation.source,
|
|
403
|
+
const spec = externalDropSpec(event.operation.source, optsRef.current.dropConfig);
|
|
210
404
|
if (spec) {
|
|
211
405
|
dropCounterRef.current += 1;
|
|
212
406
|
dropSpecRef.current = {
|
|
@@ -218,7 +412,7 @@ function SnapGridRuntime(props) {
|
|
|
218
412
|
return;
|
|
219
413
|
}
|
|
220
414
|
dropSpecRef.current = null;
|
|
221
|
-
const layout =
|
|
415
|
+
const layout = optsRef.current.layout;
|
|
222
416
|
const item = layout.find((it) => it.i === data.itemId);
|
|
223
417
|
const p = event.operation.position.current;
|
|
224
418
|
const pointer = {
|
|
@@ -232,11 +426,11 @@ function SnapGridRuntime(props) {
|
|
|
232
426
|
rect: (0, _snapgridjs_core.calcGridItemPosition)(ppRef.current, item.x, item.y, item.w, item.h),
|
|
233
427
|
pointer
|
|
234
428
|
}, data.handle));
|
|
235
|
-
|
|
429
|
+
optsRef.current.onResizeStart?.(layout, item, item, item, event.operation.activatorEvent, null);
|
|
236
430
|
return;
|
|
237
431
|
}
|
|
238
432
|
if (item) {
|
|
239
|
-
|
|
433
|
+
setKeyboard(event.operation.activatorEvent instanceof KeyboardEvent);
|
|
240
434
|
const rect = (0, _snapgridjs_core.calcGridItemPosition)(ppRef.current, item.x, item.y, item.w, item.h);
|
|
241
435
|
setSessionBoth((0, _snapgridjs_core.beginDrag)(layout, {
|
|
242
436
|
item,
|
|
@@ -245,22 +439,13 @@ function SnapGridRuntime(props) {
|
|
|
245
439
|
pointer
|
|
246
440
|
}));
|
|
247
441
|
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);
|
|
442
|
+
if (cr) setGrabOffset(managerRef.current, {
|
|
443
|
+
x: pointer.x - cr.left,
|
|
444
|
+
y: pointer.y - cr.top
|
|
445
|
+
});
|
|
446
|
+
optsRef.current.onDragStart?.(layout, item, item, item, event.operation.activatorEvent, null);
|
|
262
447
|
}
|
|
263
|
-
}, [setSessionBoth]);
|
|
448
|
+
}, [setSessionBoth, setKeyboard]);
|
|
264
449
|
const handleDragMove = (0, react.useCallback)((event) => {
|
|
265
450
|
if (keyboardRef.current) return;
|
|
266
451
|
const p = event.operation.position.current;
|
|
@@ -272,13 +457,14 @@ function SnapGridRuntime(props) {
|
|
|
272
457
|
if (current?.kind === "resize") {
|
|
273
458
|
const next = (0, _snapgridjs_core.dragResize)(current, pointer, ctx());
|
|
274
459
|
setSessionBoth(next);
|
|
275
|
-
|
|
460
|
+
optsRef.current.onResize?.(next.preview, next.anchor.item, next.placeholder, next.placeholder, event.operation.activatorEvent, null);
|
|
276
461
|
return;
|
|
277
462
|
}
|
|
463
|
+
const target = event.operation.target;
|
|
278
464
|
const data = dragData(event);
|
|
279
465
|
if (!data) {
|
|
280
466
|
const spec = dropSpecRef.current;
|
|
281
|
-
if (spec && overMe(
|
|
467
|
+
if (spec && overMe(target)) {
|
|
282
468
|
const foreign = {
|
|
283
469
|
i: spec.i,
|
|
284
470
|
x: 0,
|
|
@@ -286,7 +472,7 @@ function SnapGridRuntime(props) {
|
|
|
286
472
|
w: spec.w,
|
|
287
473
|
h: spec.h
|
|
288
474
|
};
|
|
289
|
-
const committed =
|
|
475
|
+
const committed = optsRef.current.layout;
|
|
290
476
|
const cell = cellFromPointer(pointer, foreign) ?? {
|
|
291
477
|
x: 0,
|
|
292
478
|
y: 0
|
|
@@ -296,7 +482,7 @@ function SnapGridRuntime(props) {
|
|
|
296
482
|
return;
|
|
297
483
|
}
|
|
298
484
|
if (data.kind !== "move") return;
|
|
299
|
-
const here = overMe(
|
|
485
|
+
const here = overMe(target);
|
|
300
486
|
if (committedByIdRef.current.has(data.itemId)) {
|
|
301
487
|
const source = current?.kind === "move" ? current : null;
|
|
302
488
|
if (!source) return;
|
|
@@ -308,28 +494,12 @@ function SnapGridRuntime(props) {
|
|
|
308
494
|
placeholder: null
|
|
309
495
|
};
|
|
310
496
|
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);
|
|
497
|
+
optsRef.current.onDrag?.(next.preview, next.anchor.item, next.placeholder, next.placeholder, event.operation.activatorEvent, null);
|
|
328
498
|
return;
|
|
329
499
|
}
|
|
330
500
|
if (here) {
|
|
331
501
|
const foreign = data.item;
|
|
332
|
-
const committed =
|
|
502
|
+
const committed = optsRef.current.layout;
|
|
333
503
|
const cell = cellFromPointer(pointer, foreign) ?? {
|
|
334
504
|
x: 0,
|
|
335
505
|
y: 0
|
|
@@ -345,17 +515,16 @@ function SnapGridRuntime(props) {
|
|
|
345
515
|
const handleDragEnd = (0, react.useCallback)((event) => {
|
|
346
516
|
const current = sessionRef.current;
|
|
347
517
|
const data = dragData(event);
|
|
348
|
-
const p = event.operation.position.current;
|
|
349
518
|
const myId = containerIdRef.current;
|
|
350
|
-
const dest =
|
|
351
|
-
|
|
352
|
-
|
|
519
|
+
const dest = dropDestination({
|
|
520
|
+
keyboard: keyboardRef.current,
|
|
521
|
+
targetId: event.operation.target?.id,
|
|
522
|
+
myId
|
|
353
523
|
});
|
|
354
524
|
const ownsItem = data ? committedByIdRef.current.has(data.itemId) : false;
|
|
355
|
-
|
|
356
|
-
registryRef.current.setGrabOffset(null);
|
|
525
|
+
setGrabOffset(managerRef.current, null);
|
|
357
526
|
const native = event.nativeEvent ?? null;
|
|
358
|
-
const
|
|
527
|
+
const o = optsRef.current;
|
|
359
528
|
const action = classifyDrop({
|
|
360
529
|
kind: current?.kind ?? null,
|
|
361
530
|
canceled: event.canceled,
|
|
@@ -366,74 +535,59 @@ function SnapGridRuntime(props) {
|
|
|
366
535
|
});
|
|
367
536
|
switch (action) {
|
|
368
537
|
case "cancel-resize":
|
|
369
|
-
|
|
538
|
+
o.onResizeStop?.(o.layout, current?.anchor.item ?? null, null, null, native, null);
|
|
370
539
|
break;
|
|
371
540
|
case "cancel-move":
|
|
372
|
-
|
|
541
|
+
o.onDragStop?.(o.layout, current?.anchor.item ?? null, null, null, native, null);
|
|
373
542
|
break;
|
|
374
543
|
case "commit-resize":
|
|
375
544
|
if (current) {
|
|
376
|
-
|
|
377
|
-
|
|
545
|
+
o.onLayoutChange?.((0, _snapgridjs_core.commitLayout)(current));
|
|
546
|
+
o.onResizeStop?.(current.preview, current.anchor.item, current.placeholder, current.placeholder, native, null);
|
|
378
547
|
}
|
|
379
548
|
break;
|
|
380
549
|
case "commit-in-grid":
|
|
381
550
|
case "remove-source":
|
|
382
551
|
case "revert":
|
|
383
|
-
if (action === "commit-in-grid" && current)
|
|
552
|
+
if (action === "commit-in-grid" && current) o.onLayoutChange?.((0, _snapgridjs_core.commitLayout)(current));
|
|
384
553
|
else if (action === "remove-source" && data) {
|
|
385
554
|
const { compactor: c, cols } = ctx();
|
|
386
|
-
|
|
555
|
+
o.onLayoutChange?.((0, _snapgridjs_core.removeItemWithCompactor)(o.layout, data.itemId, {
|
|
387
556
|
compactor: c,
|
|
388
557
|
cols
|
|
389
558
|
}));
|
|
390
559
|
}
|
|
391
|
-
|
|
560
|
+
o.onDragStop?.(current?.preview ?? o.layout, current?.anchor.item ?? null, current?.placeholder ?? null, current?.placeholder ?? null, native, null);
|
|
392
561
|
break;
|
|
393
562
|
case "commit-dest":
|
|
394
|
-
if (current)
|
|
563
|
+
if (current) o.onLayoutChange?.((0, _snapgridjs_core.commitLayout)(current));
|
|
395
564
|
break;
|
|
396
565
|
case "external-drop":
|
|
397
566
|
if (current) {
|
|
398
567
|
const committed = (0, _snapgridjs_core.commitLayout)(current);
|
|
399
568
|
const dropped = committed.find((it) => it.i === current.activeId);
|
|
400
|
-
if (dropped)
|
|
569
|
+
if (dropped) o.onDrop?.(committed, dropped, native);
|
|
401
570
|
}
|
|
402
571
|
break;
|
|
403
572
|
}
|
|
404
573
|
dropSpecRef.current = null;
|
|
405
|
-
|
|
574
|
+
setKeyboard(false);
|
|
406
575
|
setSessionBoth(null);
|
|
407
|
-
}, [
|
|
576
|
+
}, [
|
|
577
|
+
setSessionBoth,
|
|
578
|
+
setKeyboard,
|
|
579
|
+
ctx
|
|
580
|
+
]);
|
|
408
581
|
(0, react.useEffect)(() => {
|
|
409
|
-
const STEP = {
|
|
410
|
-
ArrowLeft: [-1, 0],
|
|
411
|
-
ArrowRight: [1, 0],
|
|
412
|
-
ArrowUp: [0, -1],
|
|
413
|
-
ArrowDown: [0, 1]
|
|
414
|
-
};
|
|
415
582
|
const onKeyDown = (e) => {
|
|
416
583
|
if (!keyboardRef.current) return;
|
|
417
584
|
const session = sessionRef.current;
|
|
418
585
|
if (!session || session.kind !== "move") return;
|
|
419
|
-
const step =
|
|
586
|
+
const step = arrowStep(e.key);
|
|
420
587
|
if (!step) return;
|
|
421
588
|
e.preventDefault();
|
|
422
589
|
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
|
-
}
|
|
590
|
+
setSessionBoth((0, _snapgridjs_core.nudge)(session, step[0], step[1], ctx()));
|
|
437
591
|
};
|
|
438
592
|
window.addEventListener("keydown", onKeyDown, true);
|
|
439
593
|
return () => window.removeEventListener("keydown", onKeyDown, true);
|
|
@@ -447,12 +601,14 @@ function SnapGridRuntime(props) {
|
|
|
447
601
|
handleDragMove,
|
|
448
602
|
handleDragEnd
|
|
449
603
|
]));
|
|
450
|
-
const dragThreshold =
|
|
451
|
-
const itemSensors = (0, react.useMemo)(() => buildItemSensors(dragThreshold, () =>
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
604
|
+
const dragThreshold = opts.dragConfig?.threshold ?? 3;
|
|
605
|
+
const itemSensors = (0, react.useMemo)(() => buildItemSensors(dragThreshold, () => optsRef.current.dragConfig), [dragThreshold]);
|
|
606
|
+
const itemModifiers = (0, react.useMemo)(() => [SnapToGrid.configure({
|
|
607
|
+
getPositionParams: () => ppRef.current,
|
|
608
|
+
isEnabled: () => optsRef.current.dragConfig?.snapToGrid ?? false
|
|
609
|
+
})], []);
|
|
610
|
+
const gridDraggable = opts.isDraggable ?? true;
|
|
611
|
+
const dragEnabled = opts.dragConfig?.enabled ?? true;
|
|
456
612
|
const isItemDraggable = (0, react.useCallback)((id) => {
|
|
457
613
|
const it = committedById.get(id);
|
|
458
614
|
if (!it) return false;
|
|
@@ -462,8 +618,8 @@ function SnapGridRuntime(props) {
|
|
|
462
618
|
gridDraggable,
|
|
463
619
|
dragEnabled
|
|
464
620
|
]);
|
|
465
|
-
const gridResizable =
|
|
466
|
-
const resizeEnabled =
|
|
621
|
+
const gridResizable = opts.isResizable ?? true;
|
|
622
|
+
const resizeEnabled = opts.resizeConfig?.enabled ?? true;
|
|
467
623
|
const isItemResizable = (0, react.useCallback)((id) => {
|
|
468
624
|
const it = committedById.get(id);
|
|
469
625
|
if (!it) return false;
|
|
@@ -473,63 +629,25 @@ function SnapGridRuntime(props) {
|
|
|
473
629
|
gridResizable,
|
|
474
630
|
resizeEnabled
|
|
475
631
|
]);
|
|
476
|
-
const defaultHandles =
|
|
632
|
+
const defaultHandles = opts.resizeConfig?.handles;
|
|
477
633
|
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,
|
|
634
|
+
controller.setConfig({
|
|
483
635
|
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
636
|
gridConfig,
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
637
|
+
width: opts.width,
|
|
638
|
+
autoSize: opts.autoSize ?? true,
|
|
639
|
+
itemSensors,
|
|
640
|
+
itemModifiers,
|
|
502
641
|
isItemDraggable,
|
|
503
642
|
isItemResizable,
|
|
504
643
|
resizeHandlesFor,
|
|
505
|
-
|
|
506
|
-
setContainerElement,
|
|
507
|
-
overlay
|
|
508
|
-
]);
|
|
509
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GridContext.Provider, {
|
|
510
|
-
value: runtime,
|
|
511
|
-
children: props.children
|
|
644
|
+
setContainerElement
|
|
512
645
|
});
|
|
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
|
-
}) });
|
|
646
|
+
return controller;
|
|
530
647
|
}
|
|
531
648
|
//#endregion
|
|
532
649
|
//#region src/hooks/useGridContainer.ts
|
|
650
|
+
const GRID_COLLISION_PRIORITY = 10;
|
|
533
651
|
/** Total container height in pixels for the given number of occupied rows. */
|
|
534
652
|
function containerHeight(rows, grid) {
|
|
535
653
|
const padY = (grid.containerPadding ?? grid.margin)[1];
|
|
@@ -537,81 +655,166 @@ function containerHeight(rows, grid) {
|
|
|
537
655
|
return padY * 2 + rows * grid.rowHeight + (rows - 1) * grid.margin[1];
|
|
538
656
|
}
|
|
539
657
|
/**
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
* onto your own container element.
|
|
658
|
+
* The grid host: creates this grid's controller + drag monitor (see
|
|
659
|
+
* {@link useGridController}), registers the droppable surface, and returns props
|
|
660
|
+
* to spread onto your own container element. Render `useGridItem` tiles inside,
|
|
661
|
+
* passing `group` (this grid's id) so they resolve this controller.
|
|
543
662
|
*/
|
|
544
|
-
function useGridContainer() {
|
|
545
|
-
const
|
|
663
|
+
function useGridContainer(opts) {
|
|
664
|
+
const controller = useGridController(opts);
|
|
665
|
+
const { width, autoSize, gridConfig, setContainerElement } = controller.config;
|
|
546
666
|
const { ref, isDropTarget } = (0, _dnd_kit_react.useDroppable)({
|
|
547
|
-
id:
|
|
667
|
+
id: controller.id,
|
|
548
668
|
type: "grid",
|
|
549
|
-
accept:
|
|
669
|
+
accept: (source) => {
|
|
670
|
+
if (source.type === "grid-item") return true;
|
|
671
|
+
return source.data?.snapGridDrop != null;
|
|
672
|
+
},
|
|
673
|
+
collisionPriority: GRID_COLLISION_PRIORITY
|
|
550
674
|
});
|
|
551
|
-
const setContainerElement = rt.setContainerElement;
|
|
552
675
|
const setRef = (0, react.useCallback)((element) => {
|
|
553
676
|
ref(element);
|
|
554
677
|
setContainerElement(element);
|
|
555
678
|
}, [ref, setContainerElement]);
|
|
556
|
-
const
|
|
679
|
+
const renderedLayout = (0, react.useSyncExternalStore)(controller.subscribe, controller.renderedSnapshot, controller.renderedSnapshot);
|
|
557
680
|
return {
|
|
558
681
|
containerProps: {
|
|
559
682
|
ref: setRef,
|
|
560
683
|
style: {
|
|
561
684
|
position: "relative",
|
|
562
|
-
width
|
|
563
|
-
height
|
|
685
|
+
width,
|
|
686
|
+
height: autoSize ? containerHeight((0, _snapgridjs_core.bottom)(renderedLayout), gridConfig) : void 0
|
|
564
687
|
},
|
|
565
688
|
"data-drop-target": isDropTarget || void 0
|
|
566
689
|
},
|
|
567
|
-
isDropTarget
|
|
690
|
+
isDropTarget,
|
|
691
|
+
group: controller.id,
|
|
692
|
+
controller
|
|
568
693
|
};
|
|
569
694
|
}
|
|
695
|
+
const REFLOW_EASING = "ease";
|
|
696
|
+
const REFLOW_TRANSITION = `transform 150ms ${REFLOW_EASING}, width 150ms ${REFLOW_EASING}, height 150ms ${REFLOW_EASING}`;
|
|
697
|
+
//#endregion
|
|
698
|
+
//#region src/hooks/useResolveController.ts
|
|
699
|
+
/**
|
|
700
|
+
* Resolve a grid's controller by its `group` (= the grid's id), scoped to the
|
|
701
|
+
* ambient dnd-kit manager. Items declare `group` (mirroring useSortable); the
|
|
702
|
+
* container registered the controller under that id. Throws a helpful error if
|
|
703
|
+
* unresolved — almost always a missing `group` or a tile rendered outside any
|
|
704
|
+
* grid / `DragDropProvider`.
|
|
705
|
+
*/
|
|
706
|
+
function useResolveController(group) {
|
|
707
|
+
const controller = getController((0, _dnd_kit_react.useDragDropManager)(), group);
|
|
708
|
+
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).`);
|
|
709
|
+
return controller;
|
|
710
|
+
}
|
|
570
711
|
//#endregion
|
|
571
712
|
//#region src/hooks/useGridItem.ts
|
|
572
|
-
const
|
|
713
|
+
const ITEM_FEEDBACK = [_dnd_kit_dom.Feedback.configure({
|
|
714
|
+
feedback: (_source, manager) => (0, _dnd_kit_dom_utilities.isKeyboardEvent)(manager.dragOperation.activatorEvent) ? "none" : "default",
|
|
715
|
+
dropAnimation: null
|
|
716
|
+
})];
|
|
573
717
|
/**
|
|
574
|
-
* Headless hook for a single grid
|
|
575
|
-
*
|
|
576
|
-
*
|
|
718
|
+
* Headless hook for a single grid tile. The tile is a real `useSortable` (a
|
|
719
|
+
* draggable + droppable carrying `group`/`index`/`type`/`accept`), so it
|
|
720
|
+
* interoperates with the dnd-kit sortable ecosystem, yet it is positioned by RGL
|
|
721
|
+
* via the {@link GridController}. `group` is the owning grid's id (from its
|
|
722
|
+
* {@link useGridContainer}), mirroring `useSortable`'s `group`. Spread the returned
|
|
723
|
+
* `ref`, optional `handleRef`, positioning `style`, and drag state onto whatever
|
|
724
|
+
* element you render — you own the tag, className, content, and cosmetic styling.
|
|
725
|
+
*
|
|
726
|
+
* The dragged tile floats itself via dnd-kit's default feedback (no `<DragOverlay>`):
|
|
727
|
+
* the active tile renders at its committed origin so the float offset composes, and
|
|
728
|
+
* reflow is animated on the compositor via the Web Animations API — both so it stays
|
|
729
|
+
* smooth in Safari, where the float's popover top-layer repaint would jank a
|
|
730
|
+
* CSS-transition reflow.
|
|
577
731
|
*/
|
|
578
|
-
function useGridItem(id) {
|
|
579
|
-
const
|
|
580
|
-
const
|
|
581
|
-
const
|
|
732
|
+
function useGridItem(id, group) {
|
|
733
|
+
const controller = useResolveController(group);
|
|
734
|
+
const snap = (0, react.useSyncExternalStore)(controller.subscribe, () => controller.itemSnapshot(id), () => controller.itemSnapshot(id));
|
|
735
|
+
const item = snap.item;
|
|
736
|
+
const active = snap.isDragging;
|
|
737
|
+
const hidden = snap.hidden;
|
|
738
|
+
const config = controller.config;
|
|
739
|
+
const wasActive = (0, react.useRef)(false);
|
|
740
|
+
const justDropped = wasActive.current && !active;
|
|
741
|
+
wasActive.current = active;
|
|
742
|
+
const data = (0, react.useMemo)(() => ({ snapGrid: {
|
|
743
|
+
kind: "move",
|
|
744
|
+
itemId: id,
|
|
745
|
+
item
|
|
746
|
+
} }), [id, item]);
|
|
747
|
+
const { ref: sortableRef, handleRef, isDragging } = (0, _dnd_kit_react_sortable.useSortable)({
|
|
582
748
|
id,
|
|
749
|
+
index: controller.itemIndex(id),
|
|
750
|
+
group,
|
|
583
751
|
type: "grid-item",
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
item
|
|
591
|
-
} }
|
|
752
|
+
accept: "grid-item",
|
|
753
|
+
disabled: !config.isItemDraggable(id),
|
|
754
|
+
sensors: config.itemSensors,
|
|
755
|
+
modifiers: config.itemModifiers,
|
|
756
|
+
plugins: (defaults) => [...defaults, ...ITEM_FEEDBACK],
|
|
757
|
+
data
|
|
592
758
|
});
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
};
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
759
|
+
const elRef = (0, react.useRef)(null);
|
|
760
|
+
const setRef = (0, react.useCallback)((element) => {
|
|
761
|
+
sortableRef(element);
|
|
762
|
+
elRef.current = element;
|
|
763
|
+
}, [sortableRef]);
|
|
764
|
+
const session = controller.getSession();
|
|
765
|
+
const dragging = session != null;
|
|
766
|
+
const posItem = item ? active && hidden ? session?.anchor.item ?? item : item : void 0;
|
|
767
|
+
const pos = posItem ? (0, _snapgridjs_core.calcGridItemPosition)(config.positionParams, posItem.x, posItem.y, posItem.w, posItem.h) : void 0;
|
|
768
|
+
const posLeft = pos?.left;
|
|
769
|
+
const posTop = pos?.top;
|
|
770
|
+
const prev = (0, react.useRef)(null);
|
|
771
|
+
const reflowAnim = (0, react.useRef)(null);
|
|
772
|
+
(0, react.useLayoutEffect)(() => {
|
|
773
|
+
const cur = posLeft != null && posTop != null ? {
|
|
774
|
+
left: posLeft,
|
|
775
|
+
top: posTop
|
|
776
|
+
} : null;
|
|
777
|
+
const before = prev.current;
|
|
778
|
+
prev.current = cur;
|
|
779
|
+
const el = elRef.current;
|
|
780
|
+
if (!el || !cur || !before || active || justDropped || !dragging) return;
|
|
781
|
+
if (before.left === cur.left && before.top === cur.top) return;
|
|
782
|
+
let fromX = before.left;
|
|
783
|
+
let fromY = before.top;
|
|
784
|
+
if (reflowAnim.current?.playState === "running") {
|
|
785
|
+
const m = new DOMMatrix(getComputedStyle(el).transform);
|
|
786
|
+
fromX = m.m41;
|
|
787
|
+
fromY = m.m42;
|
|
788
|
+
}
|
|
789
|
+
reflowAnim.current?.cancel();
|
|
790
|
+
reflowAnim.current = el.animate([{ transform: `translate(${fromX}px, ${fromY}px)` }, { transform: `translate(${cur.left}px, ${cur.top}px)` }], {
|
|
791
|
+
duration: 150,
|
|
792
|
+
easing: REFLOW_EASING
|
|
793
|
+
});
|
|
794
|
+
}, [
|
|
795
|
+
posLeft,
|
|
796
|
+
posTop,
|
|
797
|
+
active,
|
|
798
|
+
justDropped,
|
|
799
|
+
dragging
|
|
800
|
+
]);
|
|
801
|
+
(0, react.useEffect)(() => () => reflowAnim.current?.cancel(), []);
|
|
802
|
+
return {
|
|
803
|
+
ref: setRef,
|
|
804
|
+
handleRef,
|
|
805
|
+
style: pos ? {
|
|
601
806
|
position: "absolute",
|
|
602
807
|
left: 0,
|
|
603
808
|
top: 0,
|
|
604
809
|
width: pos.width,
|
|
605
810
|
height: pos.height,
|
|
606
811
|
transform: `translate(${pos.left}px, ${pos.top}px)`,
|
|
607
|
-
|
|
608
|
-
transition: active ? "none" : REFLOW_TRANSITION,
|
|
812
|
+
transition: active || justDropped || dragging ? "none" : REFLOW_TRANSITION,
|
|
609
813
|
touchAction: "none"
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
style,
|
|
814
|
+
} : {
|
|
815
|
+
position: "absolute",
|
|
816
|
+
touchAction: "none"
|
|
817
|
+
},
|
|
615
818
|
isDragging,
|
|
616
819
|
item
|
|
617
820
|
};
|
|
@@ -620,13 +823,14 @@ function useGridItem(id) {
|
|
|
620
823
|
//#region src/hooks/useGridPlaceholder.ts
|
|
621
824
|
/**
|
|
622
825
|
* Headless hook returning where the drag placeholder should be rendered, or
|
|
623
|
-
* `null` when no drag is in progress.
|
|
826
|
+
* `null` when no drag is in progress. `group` is the owning grid's id (from its
|
|
827
|
+
* {@link useGridContainer}). You render the element however you like.
|
|
624
828
|
*/
|
|
625
|
-
function useGridPlaceholder() {
|
|
626
|
-
const
|
|
627
|
-
const placeholder =
|
|
829
|
+
function useGridPlaceholder(group) {
|
|
830
|
+
const controller = useResolveController(group);
|
|
831
|
+
const placeholder = (0, react.useSyncExternalStore)(controller.subscribe, controller.placeholderSnapshot, controller.placeholderSnapshot);
|
|
628
832
|
if (!placeholder) return null;
|
|
629
|
-
const pos = (0, _snapgridjs_core.calcGridItemPosition)(
|
|
833
|
+
const pos = (0, _snapgridjs_core.calcGridItemPosition)(controller.config.positionParams, placeholder.x, placeholder.y, placeholder.w, placeholder.h);
|
|
630
834
|
return {
|
|
631
835
|
item: placeholder,
|
|
632
836
|
style: {
|
|
@@ -644,14 +848,15 @@ function useGridPlaceholder() {
|
|
|
644
848
|
//#region src/hooks/useGridResizeHandle.ts
|
|
645
849
|
/**
|
|
646
850
|
* 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
|
-
*
|
|
851
|
+
* dragging it resizes the item from the given edge/corner. `group` is the owning
|
|
852
|
+
* grid's id (from its {@link useGridContainer}). Spread `ref` and `handleProps`
|
|
853
|
+
* onto the handle element you position/style.
|
|
649
854
|
*/
|
|
650
|
-
function useGridResizeHandle(itemId, handle) {
|
|
651
|
-
const
|
|
855
|
+
function useGridResizeHandle(itemId, handle, group) {
|
|
856
|
+
const controller = useResolveController(group);
|
|
652
857
|
const { ref } = (0, _dnd_kit_react.useDraggable)({
|
|
653
858
|
id: `${itemId}::resize::${handle}`,
|
|
654
|
-
disabled: !
|
|
859
|
+
disabled: !controller.config?.isItemResizable(itemId),
|
|
655
860
|
plugins: NO_FEEDBACK,
|
|
656
861
|
data: { snapGrid: {
|
|
657
862
|
kind: "resize",
|
|
@@ -659,7 +864,7 @@ function useGridResizeHandle(itemId, handle) {
|
|
|
659
864
|
handle
|
|
660
865
|
} }
|
|
661
866
|
});
|
|
662
|
-
const isResizing =
|
|
867
|
+
const { isResizing } = (0, react.useSyncExternalStore)(controller.subscribe, () => controller.resizeSnapshot(itemId), () => controller.resizeSnapshot(itemId));
|
|
663
868
|
return {
|
|
664
869
|
ref,
|
|
665
870
|
handleProps: { [RESIZE_HANDLE_ATTR]: true },
|
|
@@ -667,30 +872,6 @@ function useGridResizeHandle(itemId, handle) {
|
|
|
667
872
|
};
|
|
668
873
|
}
|
|
669
874
|
//#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
875
|
//#region src/hooks/useResponsiveLayout.ts
|
|
695
876
|
/** react-grid-layout's default breakpoints (px) and column counts. */
|
|
696
877
|
const DEFAULT_BREAKPOINTS = {
|
|
@@ -757,26 +938,7 @@ function useResponsiveLayout(options) {
|
|
|
757
938
|
};
|
|
758
939
|
}
|
|
759
940
|
//#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
|
|
941
|
+
//#region src/components/GridItem.tsx
|
|
780
942
|
const HANDLE_CURSOR = {
|
|
781
943
|
n: "ns-resize",
|
|
782
944
|
s: "ns-resize",
|
|
@@ -805,8 +967,8 @@ function handleStyle(handle) {
|
|
|
805
967
|
if (handle === "e" || handle === "w") s.top = `calc(50% - ${SIDE / 2}px)`;
|
|
806
968
|
return s;
|
|
807
969
|
}
|
|
808
|
-
function DefaultResizeHandle({ itemId, handle }) {
|
|
809
|
-
const { ref, handleProps } = useGridResizeHandle(itemId, handle);
|
|
970
|
+
function DefaultResizeHandle({ itemId, handle, group }) {
|
|
971
|
+
const { ref, handleProps } = useGridResizeHandle(itemId, handle, group);
|
|
810
972
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
811
973
|
ref,
|
|
812
974
|
...handleProps,
|
|
@@ -817,12 +979,19 @@ function DefaultResizeHandle({ itemId, handle }) {
|
|
|
817
979
|
/**
|
|
818
980
|
* Convenience wrapper over {@link useGridItem}: an absolutely-positioned `<div>`
|
|
819
981
|
* with stable hooks (`.snapgrid-item`, `data-grid-id`, `data-dragging`) and the
|
|
820
|
-
* configured resize handles. For full control,
|
|
982
|
+
* configured resize handles. `group` is the owning grid's id. For full control,
|
|
983
|
+
* use the hooks directly.
|
|
984
|
+
*
|
|
985
|
+
* Memoized so re-rendering the surface (e.g. its auto-height tracking the drag)
|
|
986
|
+
* doesn't re-render every tile — a tile re-renders only when its own slice
|
|
987
|
+
* changes (via useGridItem's subscription). Keeps a drag's React work scoped to
|
|
988
|
+
* the moved tile (see renderScope.test).
|
|
821
989
|
*/
|
|
822
|
-
function
|
|
823
|
-
const
|
|
824
|
-
const { ref, style: positionStyle, isDragging } = useGridItem(id);
|
|
825
|
-
const
|
|
990
|
+
function GridItemImpl({ id, group, children, className, style }) {
|
|
991
|
+
const controller = useResolveController(group);
|
|
992
|
+
const { ref, style: positionStyle, isDragging } = useGridItem(id, group);
|
|
993
|
+
const config = controller.config;
|
|
994
|
+
const handles = config?.isItemResizable(id) ? config.resizeHandlesFor(id) : [];
|
|
826
995
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
827
996
|
ref,
|
|
828
997
|
"data-grid-id": id,
|
|
@@ -834,27 +1003,29 @@ function GridItem({ id, children, className, style }) {
|
|
|
834
1003
|
} : positionStyle,
|
|
835
1004
|
children: [children, handles.map((handle) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DefaultResizeHandle, {
|
|
836
1005
|
itemId: id,
|
|
837
|
-
handle
|
|
1006
|
+
handle,
|
|
1007
|
+
group
|
|
838
1008
|
}, handle))]
|
|
839
1009
|
});
|
|
840
1010
|
}
|
|
1011
|
+
const GridItem = (0, react.memo)(GridItemImpl);
|
|
841
1012
|
//#endregion
|
|
842
|
-
//#region src/GridPlaceholder.tsx
|
|
1013
|
+
//#region src/components/GridPlaceholder.tsx
|
|
843
1014
|
const DEFAULT_LOOK = {
|
|
844
1015
|
background: "rgba(99, 102, 241, 0.2)",
|
|
845
1016
|
border: "1px dashed rgba(99, 102, 241, 0.6)",
|
|
846
1017
|
borderRadius: 4,
|
|
847
1018
|
boxSizing: "border-box",
|
|
848
1019
|
zIndex: 2,
|
|
849
|
-
transition:
|
|
1020
|
+
transition: REFLOW_TRANSITION
|
|
850
1021
|
};
|
|
851
1022
|
/**
|
|
852
1023
|
* Convenience placeholder rendered from {@link useGridPlaceholder}. Renders
|
|
853
1024
|
* nothing when no drag is active. For a custom placeholder, call the hook
|
|
854
1025
|
* directly and render your own element with the returned `style`.
|
|
855
1026
|
*/
|
|
856
|
-
function GridPlaceholder({ className, style }) {
|
|
857
|
-
const placeholder = useGridPlaceholder();
|
|
1027
|
+
function GridPlaceholder({ group, className, style }) {
|
|
1028
|
+
const placeholder = useGridPlaceholder(group);
|
|
858
1029
|
if (!placeholder) return null;
|
|
859
1030
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
860
1031
|
"aria-hidden": "true",
|
|
@@ -867,18 +1038,15 @@ function GridPlaceholder({ className, style }) {
|
|
|
867
1038
|
});
|
|
868
1039
|
}
|
|
869
1040
|
//#endregion
|
|
870
|
-
//#region src/GridLayout.tsx
|
|
1041
|
+
//#region src/components/GridLayout.tsx
|
|
1042
|
+
const InProvider = (0, react.createContext)(false);
|
|
871
1043
|
/** Strip the namespacing prefix React applies to keys inside `Children.map`. */
|
|
872
1044
|
function keyToId(key) {
|
|
873
1045
|
return key.startsWith(".$") ? key.slice(2) : key;
|
|
874
1046
|
}
|
|
875
1047
|
/** 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
|
-
});
|
|
1048
|
+
function GridSurface({ className, style, children, ...opts }) {
|
|
1049
|
+
const { containerProps, group } = useGridContainer(opts);
|
|
882
1050
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
883
1051
|
...containerProps,
|
|
884
1052
|
className: className ? `snapgrid ${className}` : "snapgrid",
|
|
@@ -886,38 +1054,50 @@ function GridSurface({ className, style, children }) {
|
|
|
886
1054
|
...containerProps.style,
|
|
887
1055
|
...style
|
|
888
1056
|
} : 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
|
-
]
|
|
1057
|
+
children: [react.Children.map(children, (child) => {
|
|
1058
|
+
if (!(0, react.isValidElement)(child) || child.key == null) return child;
|
|
1059
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GridItem, {
|
|
1060
|
+
id: keyToId(String(child.key)),
|
|
1061
|
+
group,
|
|
1062
|
+
children: child
|
|
1063
|
+
}, child.key);
|
|
1064
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GridPlaceholder, { group })]
|
|
900
1065
|
});
|
|
901
1066
|
}
|
|
902
1067
|
/**
|
|
903
1068
|
* Drop-in grid component: a controlled, react-grid-layout v2-compatible layout
|
|
904
|
-
* backed by dnd-kit. A thin shell over {@link
|
|
1069
|
+
* backed by dnd-kit. A thin shell over {@link useGridContainer} and the headless
|
|
905
1070
|
* hooks — children are keyed by their layout item's `i`. For full control over
|
|
906
|
-
* markup/styling, use the
|
|
1071
|
+
* markup/styling, use the hooks directly.
|
|
1072
|
+
*
|
|
1073
|
+
* Supplies the dnd-kit `DragDropProvider` for the turnkey case so consumers
|
|
1074
|
+
* don't manage one. Nest multiple `GridLayout`s and they share one provider
|
|
1075
|
+
* (the seam for cross-grid drags); a consumer's own provider is also honored.
|
|
907
1076
|
*/
|
|
908
1077
|
function GridLayout(props) {
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1078
|
+
const inProvider = (0, react.useContext)(InProvider);
|
|
1079
|
+
const surface = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GridSurface, { ...props });
|
|
1080
|
+
if (inProvider) return surface;
|
|
1081
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_dnd_kit_react.DragDropProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InProvider.Provider, {
|
|
1082
|
+
value: true,
|
|
1083
|
+
children: surface
|
|
1084
|
+
}) });
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* Share one dnd-kit `DragDropProvider` across several sibling grids so tiles can
|
|
1088
|
+
* be dragged between them. (Nested `GridLayout`s already share a provider; this
|
|
1089
|
+
* is for siblings.) A thin wrapper over `DragDropProvider` — the cross-grid seam
|
|
1090
|
+
* is the shared manager + collision target.
|
|
1091
|
+
*/
|
|
1092
|
+
function SnapGridGroup({ children }) {
|
|
1093
|
+
if ((0, react.useContext)(InProvider)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children });
|
|
1094
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_dnd_kit_react.DragDropProvider, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InProvider.Provider, {
|
|
1095
|
+
value: true,
|
|
1096
|
+
children
|
|
1097
|
+
}) });
|
|
918
1098
|
}
|
|
919
1099
|
//#endregion
|
|
920
|
-
//#region src/ResponsiveGridLayout.tsx
|
|
1100
|
+
//#region src/components/ResponsiveGridLayout.tsx
|
|
921
1101
|
/**
|
|
922
1102
|
* A responsive grid: switches column count and layout by breakpoint as `width`
|
|
923
1103
|
* changes, generating a breakpoint's layout from the nearest one when absent.
|
|
@@ -997,13 +1177,18 @@ function useContainerWidth(options = {}) {
|
|
|
997
1177
|
//#endregion
|
|
998
1178
|
exports.DEFAULT_BREAKPOINTS = DEFAULT_BREAKPOINTS;
|
|
999
1179
|
exports.DEFAULT_BREAKPOINT_COLS = DEFAULT_BREAKPOINT_COLS;
|
|
1180
|
+
Object.defineProperty(exports, "DragOverlay", {
|
|
1181
|
+
enumerable: true,
|
|
1182
|
+
get: function() {
|
|
1183
|
+
return _dnd_kit_react.DragOverlay;
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1000
1186
|
Object.defineProperty(exports, "Feedback", {
|
|
1001
1187
|
enumerable: true,
|
|
1002
1188
|
get: function() {
|
|
1003
1189
|
return _dnd_kit_dom.Feedback;
|
|
1004
1190
|
}
|
|
1005
1191
|
});
|
|
1006
|
-
exports.GridDragOverlay = GridDragOverlay;
|
|
1007
1192
|
exports.GridItem = GridItem;
|
|
1008
1193
|
exports.GridLayout = GridLayout;
|
|
1009
1194
|
exports.GridPlaceholder = GridPlaceholder;
|
|
@@ -1021,7 +1206,6 @@ Object.defineProperty(exports, "PointerSensor", {
|
|
|
1021
1206
|
});
|
|
1022
1207
|
exports.ResponsiveGridLayout = ResponsiveGridLayout;
|
|
1023
1208
|
exports.SnapGridGroup = SnapGridGroup;
|
|
1024
|
-
exports.SnapGridProvider = SnapGridProvider;
|
|
1025
1209
|
Object.defineProperty(exports, "getCompactor", {
|
|
1026
1210
|
enumerable: true,
|
|
1027
1211
|
get: function() {
|
|
@@ -1054,7 +1238,6 @@ Object.defineProperty(exports, "useDroppable", {
|
|
|
1054
1238
|
}
|
|
1055
1239
|
});
|
|
1056
1240
|
exports.useGridContainer = useGridContainer;
|
|
1057
|
-
exports.useGridDragOverlay = useGridDragOverlay;
|
|
1058
1241
|
exports.useGridItem = useGridItem;
|
|
1059
1242
|
exports.useGridPlaceholder = useGridPlaceholder;
|
|
1060
1243
|
exports.useGridResizeHandle = useGridResizeHandle;
|