@qiiqa/bob-ui-react-layout 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/dist/index.js ADDED
@@ -0,0 +1,1238 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ BobForm: () => BobForm,
34
+ BobItem: () => BobItem,
35
+ BobPage: () => BobPage,
36
+ BobPanel: () => BobPanel,
37
+ BobResponsiveness: () => BobResponsiveness,
38
+ BobTab: () => BobTab,
39
+ BobTabPage: () => BobTabPage,
40
+ BobTabStrip: () => BobTabStrip,
41
+ DEFAULT_RESPONSIVENESS_CONFIG: () => DEFAULT_RESPONSIVENESS_CONFIG,
42
+ GapDropZone: () => GapDropZone,
43
+ LayoutProvider: () => LayoutProvider,
44
+ useLayoutContext: () => useLayoutContext
45
+ });
46
+ module.exports = __toCommonJS(index_exports);
47
+
48
+ // src/context/LayoutContext.tsx
49
+ var import_react = require("react");
50
+ var import_jsx_runtime = require("react/jsx-runtime");
51
+ var DEFAULT_RESPONSIVENESS_CONFIG = {
52
+ small: { page: 1, group: 1 },
53
+ medium: { page: 12, group: 12 },
54
+ large: { page: 18, group: 12 },
55
+ extraLarge: { page: 24, group: 12 }
56
+ };
57
+ var LayoutContext = (0, import_react.createContext)(void 0);
58
+ function useLayoutContext() {
59
+ const context = (0, import_react.useContext)(LayoutContext);
60
+ if (!context) {
61
+ throw new Error("useLayoutContext must be used within a LayoutProvider or BobForm");
62
+ }
63
+ return context;
64
+ }
65
+ var LayoutProvider = ({
66
+ children,
67
+ initialDesignMode = false,
68
+ initialDesignTabs = false,
69
+ initialDesignGroups = false,
70
+ initialDesignFields = false,
71
+ initialLayoutState = {},
72
+ initialPageCols = 12,
73
+ initialGroupCols = 12,
74
+ initialResponsivenessConfig = DEFAULT_RESPONSIVENESS_CONFIG,
75
+ onLayoutChange,
76
+ onPageColsChange,
77
+ onGroupColsChange,
78
+ onResponsivenessConfigChange
79
+ }) => {
80
+ const [designMode, setDesignMode] = (0, import_react.useState)(initialDesignMode);
81
+ const [designTabs, setDesignTabs] = (0, import_react.useState)(initialDesignTabs);
82
+ const [designGroups, setDesignGroups] = (0, import_react.useState)(initialDesignGroups);
83
+ const [designFields, setDesignFields] = (0, import_react.useState)(initialDesignFields);
84
+ const [layoutState, setLayoutState] = (0, import_react.useState)(initialLayoutState);
85
+ const [selectedItemIds, setSelectedItemIds] = (0, import_react.useState)([]);
86
+ const [isDraggingActive, setDraggingActive] = (0, import_react.useState)(false);
87
+ const [dragOverId, setDragOverId] = (0, import_react.useState)(null);
88
+ const [dragSide, setDragSide] = (0, import_react.useState)(null);
89
+ const initialPageColsRef = (0, import_react.useRef)(initialPageCols);
90
+ const initialGroupColsRef = (0, import_react.useRef)(initialGroupCols);
91
+ const initialLayoutStateRef = (0, import_react.useRef)(initialLayoutState);
92
+ (0, import_react.useEffect)(() => {
93
+ initialPageColsRef.current = initialPageCols;
94
+ }, [initialPageCols]);
95
+ (0, import_react.useEffect)(() => {
96
+ initialGroupColsRef.current = initialGroupCols;
97
+ }, [initialGroupCols]);
98
+ (0, import_react.useEffect)(() => {
99
+ initialLayoutStateRef.current = initialLayoutState;
100
+ }, [initialLayoutState]);
101
+ const [pageCols, setPageColsState] = (0, import_react.useState)(initialPageCols);
102
+ const [groupCols, setGroupColsState] = (0, import_react.useState)(initialGroupCols);
103
+ const [responsivenessConfig, setResponsivenessConfigState] = (0, import_react.useState)(initialResponsivenessConfig);
104
+ const [currentBreakpoint, setCurrentBreakpoint] = (0, import_react.useState)("");
105
+ (0, import_react.useEffect)(() => {
106
+ const handleResize = () => {
107
+ const width = document.documentElement.clientWidth;
108
+ let category = "small";
109
+ if (width < 600) category = "small";
110
+ else if (width < 1024) category = "medium";
111
+ else if (width < 1600) category = "large";
112
+ else category = "extraLarge";
113
+ if (category !== currentBreakpoint) {
114
+ setCurrentBreakpoint(category);
115
+ const tier = responsivenessConfig[category];
116
+ setPageColsState(tier.page);
117
+ setGroupColsState(tier.group);
118
+ }
119
+ };
120
+ window.addEventListener("resize", handleResize);
121
+ handleResize();
122
+ return () => window.removeEventListener("resize", handleResize);
123
+ }, [responsivenessConfig, currentBreakpoint]);
124
+ (0, import_react.useEffect)(() => {
125
+ if (onResponsivenessConfigChange) {
126
+ onResponsivenessConfigChange(responsivenessConfig);
127
+ }
128
+ }, [responsivenessConfig, onResponsivenessConfigChange]);
129
+ const setResponsivenessConfig = (newConfig) => {
130
+ setResponsivenessConfigState(newConfig);
131
+ };
132
+ const setDragOverState = (id, side) => {
133
+ setDragOverId(id);
134
+ setDragSide(side);
135
+ };
136
+ const toggleItemSelection = (id, multi) => {
137
+ setSelectedItemIds((prev) => {
138
+ if (multi) {
139
+ return prev.includes(id) ? prev.filter((i) => i !== id) : [...prev, id];
140
+ }
141
+ return prev.includes(id) && prev.length === 1 ? [] : [id];
142
+ });
143
+ };
144
+ const clearItemSelection = () => {
145
+ setSelectedItemIds([]);
146
+ };
147
+ (0, import_react.useEffect)(() => {
148
+ setDesignMode(initialDesignMode);
149
+ }, [initialDesignMode]);
150
+ (0, import_react.useEffect)(() => {
151
+ setDesignTabs(initialDesignTabs);
152
+ }, [initialDesignTabs]);
153
+ (0, import_react.useEffect)(() => {
154
+ setDesignGroups(initialDesignGroups);
155
+ }, [initialDesignGroups]);
156
+ (0, import_react.useEffect)(() => {
157
+ setDesignFields(initialDesignFields);
158
+ }, [initialDesignFields]);
159
+ (0, import_react.useEffect)(() => {
160
+ if (initialPageCols !== void 0 && initialPageCols !== pageCols) setPageColsState(initialPageCols);
161
+ }, [initialPageCols]);
162
+ (0, import_react.useEffect)(() => {
163
+ if (initialGroupCols !== void 0 && initialGroupCols !== groupCols) setGroupColsState(initialGroupCols);
164
+ }, [initialGroupCols]);
165
+ (0, import_react.useEffect)(() => {
166
+ if (initialLayoutState && Object.keys(initialLayoutState).length > 0) {
167
+ setLayoutState(initialLayoutState);
168
+ }
169
+ }, [initialLayoutState]);
170
+ (0, import_react.useEffect)(() => {
171
+ if (onLayoutChange && layoutState !== initialLayoutStateRef.current) {
172
+ onLayoutChange(layoutState);
173
+ }
174
+ }, [layoutState, onLayoutChange]);
175
+ (0, import_react.useEffect)(() => {
176
+ if (onPageColsChange && pageCols !== initialPageColsRef.current) {
177
+ onPageColsChange(pageCols);
178
+ }
179
+ }, [pageCols, onPageColsChange]);
180
+ (0, import_react.useEffect)(() => {
181
+ if (onGroupColsChange && groupCols !== initialGroupColsRef.current) {
182
+ onGroupColsChange(groupCols);
183
+ }
184
+ }, [groupCols, onGroupColsChange]);
185
+ const loadLayoutState = (state) => {
186
+ setLayoutState(state);
187
+ };
188
+ const reorderItemWithinContainer = (containerId, activeId, overId, fallbackItems, insertAfter) => {
189
+ setLayoutState((prev) => {
190
+ const items = prev[containerId] || fallbackItems || [];
191
+ const oldIndex = items.indexOf(activeId);
192
+ if (oldIndex === -1) return prev;
193
+ const newItems = [...items];
194
+ newItems.splice(oldIndex, 1);
195
+ if (overId === "__start__") {
196
+ newItems.unshift(activeId);
197
+ return { ...prev, [containerId]: newItems };
198
+ }
199
+ if (overId === "__end__") {
200
+ newItems.push(activeId);
201
+ return { ...prev, [containerId]: newItems };
202
+ }
203
+ const newIndexRaw = items.indexOf(overId);
204
+ if (newIndexRaw === -1) return prev;
205
+ const adjustedNewIndex = newItems.indexOf(overId);
206
+ const targetIndex = insertAfter ? adjustedNewIndex + 1 : adjustedNewIndex;
207
+ newItems.splice(targetIndex, 0, activeId);
208
+ return { ...prev, [containerId]: newItems };
209
+ });
210
+ };
211
+ const moveItemBetweenContainers = (sourceContainerId, destContainerId, activeId, overId, fallbackItems, insertAfter) => {
212
+ setLayoutState((prev) => {
213
+ const sourceItems = [...prev[sourceContainerId] || fallbackItems || []];
214
+ const destItems = [...prev[destContainerId] || []];
215
+ const sourceIndex = sourceItems.indexOf(activeId);
216
+ if (sourceIndex === -1) return prev;
217
+ sourceItems.splice(sourceIndex, 1);
218
+ if (overId === "__start__") {
219
+ destItems.unshift(activeId);
220
+ } else if (overId === "__end__") {
221
+ destItems.push(activeId);
222
+ } else {
223
+ const destIndex = destItems.indexOf(overId);
224
+ if (destIndex === -1) {
225
+ destItems.push(activeId);
226
+ } else {
227
+ const targetIndex = insertAfter ? destIndex + 1 : destIndex;
228
+ destItems.splice(targetIndex, 0, activeId);
229
+ }
230
+ }
231
+ return {
232
+ ...prev,
233
+ [sourceContainerId]: sourceItems,
234
+ [destContainerId]: destItems
235
+ };
236
+ });
237
+ };
238
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LayoutContext.Provider, { value: {
239
+ designMode,
240
+ designTabs,
241
+ designGroups,
242
+ designFields,
243
+ layoutState,
244
+ selectedItemIds,
245
+ isDraggingActive,
246
+ dragOverId,
247
+ dragSide,
248
+ setLayoutState,
249
+ toggleItemSelection,
250
+ clearItemSelection,
251
+ setDraggingActive,
252
+ setDragOverState,
253
+ loadLayoutState,
254
+ reorderItemWithinContainer,
255
+ moveItemBetweenContainers,
256
+ setDesignMode,
257
+ setDesignTabs,
258
+ setDesignGroups,
259
+ setDesignFields,
260
+ pageCols,
261
+ groupCols,
262
+ setPageCols: setPageColsState,
263
+ setGroupCols: setGroupColsState,
264
+ responsivenessConfig,
265
+ setResponsivenessConfig,
266
+ currentBreakpoint
267
+ }, children });
268
+ };
269
+
270
+ // src/components/BobForm.tsx
271
+ var import_react2 = __toESM(require("react"));
272
+ var import_core = require("@dnd-kit/core");
273
+ var import_sortable = require("@dnd-kit/sortable");
274
+ var import_jsx_runtime2 = require("react/jsx-runtime");
275
+ var InnerFormWrapper = ({ children, className }) => {
276
+ const {
277
+ moveItemBetweenContainers,
278
+ reorderItemWithinContainer,
279
+ setDragOverState,
280
+ setDraggingActive,
281
+ clearItemSelection
282
+ } = useLayoutContext();
283
+ const lastEmit = import_react2.default.useRef({ id: null, side: null });
284
+ const pointerRef = import_react2.default.useRef({ x: 0, y: 0 });
285
+ import_react2.default.useEffect(() => {
286
+ const handlePointerMove = (e) => {
287
+ pointerRef.current = { x: e.clientX, y: e.clientY };
288
+ };
289
+ window.addEventListener("pointermove", handlePointerMove);
290
+ return () => window.removeEventListener("pointermove", handlePointerMove);
291
+ }, []);
292
+ const sensors = (0, import_core.useSensors)(
293
+ (0, import_core.useSensor)(import_core.PointerSensor, { activationConstraint: { distance: 5 } }),
294
+ (0, import_core.useSensor)(import_core.KeyboardSensor, { coordinateGetter: import_sortable.sortableKeyboardCoordinates })
295
+ );
296
+ const handleDragStart = (_event) => {
297
+ setDraggingActive(true);
298
+ };
299
+ const handleDragMove = (event) => {
300
+ const { active, over } = event;
301
+ if (over?.data?.current?.type === "GapDropZone") {
302
+ if (lastEmit.current.id !== null) {
303
+ lastEmit.current = { id: null, side: null };
304
+ setDragOverState(null, null);
305
+ }
306
+ return;
307
+ }
308
+ if (!over || active.id === over.id) {
309
+ if (lastEmit.current.id !== null) {
310
+ lastEmit.current = { id: null, side: null };
311
+ setDragOverState(null, null);
312
+ }
313
+ return;
314
+ }
315
+ const activeType = active.data.current?.type;
316
+ const overType = over.data.current?.type;
317
+ if (activeType === "Panel" && overType === "Item") {
318
+ return;
319
+ }
320
+ const overRect = over.rect;
321
+ const activeRect = active.rect.current.translated;
322
+ if (!overRect || !activeRect) return;
323
+ const overCenterX = overRect.left + overRect.width / 2;
324
+ const pointerX = pointerRef.current.x;
325
+ const dx = pointerX - overCenterX;
326
+ let side;
327
+ side = dx > 0 ? "right" : "left";
328
+ let targetId = over.id;
329
+ let finalSide = side;
330
+ if (over.data.current?.type === "Item" || over.data.current?.type === "Panel") {
331
+ const containerId = over.data.current.sortable.containerId;
332
+ const items = over.data.current.sortable.items;
333
+ const overIndex = items.indexOf(over.id);
334
+ if (side === "left") {
335
+ if (overIndex > 0) {
336
+ targetId = `gap-${containerId}-after-${items[overIndex - 1]}`;
337
+ } else {
338
+ targetId = `gap-${containerId}-start`;
339
+ }
340
+ } else {
341
+ targetId = `gap-${containerId}-after-${over.id}`;
342
+ }
343
+ finalSide = null;
344
+ }
345
+ if (lastEmit.current.id !== targetId || lastEmit.current.side !== side) {
346
+ lastEmit.current = { id: targetId, side };
347
+ setDragOverState(targetId, finalSide);
348
+ }
349
+ };
350
+ const handleDragEnd = (event) => {
351
+ const currentSide = lastEmit.current.side;
352
+ lastEmit.current = { id: null, side: null };
353
+ setDragOverState(null, null);
354
+ setDraggingActive(false);
355
+ const { active, over } = event;
356
+ if (!over) return;
357
+ const overData = over.data.current;
358
+ const activeContainer = active.data.current?.sortable?.containerId;
359
+ const overContainer = over.data.current?.sortable?.containerId || over.id;
360
+ const fallbackItems = active.data.current?.sortable?.items || [];
361
+ if (!activeContainer || !overContainer) return;
362
+ if (overData?.type === "Item" || overData?.type === "Panel") {
363
+ const items = overData.sortable.items;
364
+ const overIndex = items.indexOf(over.id);
365
+ let insertAfterItemId = void 0;
366
+ if (currentSide === "left") {
367
+ if (overIndex > 0) {
368
+ insertAfterItemId = items[overIndex - 1];
369
+ }
370
+ } else {
371
+ insertAfterItemId = over.id;
372
+ }
373
+ if (activeContainer === overContainer) {
374
+ if (active.id !== over.id) {
375
+ reorderItemWithinContainerByGap(activeContainer, active.id, insertAfterItemId, fallbackItems, reorderItemWithinContainer);
376
+ }
377
+ } else {
378
+ moveItemBetweenContainersByGap(activeContainer, overContainer, active.id, insertAfterItemId, fallbackItems, moveItemBetweenContainers);
379
+ }
380
+ return;
381
+ }
382
+ if (overData?.type === "GapDropZone") {
383
+ const destContainerId = overData.containerId;
384
+ const insertAfterItemId = overData.insertAfterItemId;
385
+ if (activeContainer === destContainerId) {
386
+ reorderItemWithinContainerByGap(
387
+ activeContainer,
388
+ active.id,
389
+ insertAfterItemId,
390
+ fallbackItems,
391
+ reorderItemWithinContainer
392
+ );
393
+ } else {
394
+ moveItemBetweenContainersByGap(
395
+ activeContainer,
396
+ destContainerId,
397
+ active.id,
398
+ insertAfterItemId,
399
+ fallbackItems,
400
+ moveItemBetweenContainers
401
+ );
402
+ }
403
+ return;
404
+ }
405
+ const insertAfter = currentSide === "right";
406
+ if (activeContainer === overContainer) {
407
+ if (active.id !== over.id) {
408
+ reorderItemWithinContainer(activeContainer, active.id, over.id, fallbackItems, insertAfter);
409
+ }
410
+ } else {
411
+ let actualDestContainer = overContainer;
412
+ if (event.over?.data?.current?.sortable?.containerId) {
413
+ actualDestContainer = event.over.data.current.sortable.containerId;
414
+ }
415
+ moveItemBetweenContainers(activeContainer, actualDestContainer, active.id, over.id, fallbackItems, insertAfter);
416
+ }
417
+ };
418
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core.DndContext, { sensors, collisionDetection: customCollisionDetection, onDragStart: handleDragStart, onDragMove: handleDragMove, onDragEnd: handleDragEnd, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: `bob-react-form ${className}`, children }) });
419
+ };
420
+ function reorderItemWithinContainerByGap(containerId, activeId, insertAfterItemId, fallbackItems, reorderFn) {
421
+ if (!insertAfterItemId) {
422
+ reorderFn(containerId, activeId, "__start__", fallbackItems, false);
423
+ return;
424
+ }
425
+ reorderFn(
426
+ containerId,
427
+ activeId,
428
+ insertAfterItemId,
429
+ fallbackItems,
430
+ true
431
+ /* insertAfter = true → after insertAfterItemId */
432
+ );
433
+ }
434
+ function moveItemBetweenContainersByGap(sourceContainerId, destContainerId, activeId, insertAfterItemId, fallbackItems, moveFn) {
435
+ if (!insertAfterItemId) {
436
+ moveFn(sourceContainerId, destContainerId, activeId, "__start__", fallbackItems, false);
437
+ return;
438
+ }
439
+ moveFn(sourceContainerId, destContainerId, activeId, insertAfterItemId, fallbackItems, true);
440
+ }
441
+ var customCollisionDetection = (args) => {
442
+ const pointerCollisions = (0, import_core.pointerWithin)(args);
443
+ if (pointerCollisions.length > 0) {
444
+ return pointerCollisions;
445
+ }
446
+ return (0, import_core.closestCenter)(args);
447
+ };
448
+ var BobForm = ({
449
+ children,
450
+ className = "",
451
+ designMode = false,
452
+ designTabs = false,
453
+ designGroups = false,
454
+ designFields = false,
455
+ layoutState,
456
+ pageCols,
457
+ groupCols,
458
+ initialResponsivenessConfig,
459
+ onLayoutChange,
460
+ onPageColsChange,
461
+ onGroupColsChange,
462
+ onResponsivenessConfigChange
463
+ }) => {
464
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
465
+ LayoutProvider,
466
+ {
467
+ initialDesignMode: designMode,
468
+ initialDesignTabs: designTabs,
469
+ initialDesignGroups: designGroups,
470
+ initialDesignFields: designFields,
471
+ initialLayoutState: layoutState,
472
+ initialPageCols: pageCols,
473
+ initialGroupCols: groupCols,
474
+ initialResponsivenessConfig,
475
+ onLayoutChange,
476
+ onPageColsChange,
477
+ onGroupColsChange,
478
+ onResponsivenessConfigChange,
479
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(InnerFormWrapper, { className, children })
480
+ }
481
+ );
482
+ };
483
+
484
+ // src/components/BobTabStrip.tsx
485
+ var import_react3 = __toESM(require("react"));
486
+ var import_sortable2 = require("@dnd-kit/sortable");
487
+ var import_jsx_runtime3 = require("react/jsx-runtime");
488
+ var BobTabStrip = ({ id, children, className = "" }) => {
489
+ const { layoutState } = useLayoutContext();
490
+ const childrenArray = import_react3.default.Children.toArray(children).filter(import_react3.default.isValidElement);
491
+ const getId = (c) => c.props.id || c.key;
492
+ const defaultIds = childrenArray.map(getId);
493
+ const itemIds = layoutState[id] || defaultIds;
494
+ const sortedChildren = itemIds.map((itemId) => childrenArray.find((c) => getId(c) === itemId)).filter(Boolean);
495
+ const newChildren = childrenArray.filter((c) => !itemIds.includes(getId(c)));
496
+ const finalChildren = [...sortedChildren, ...newChildren];
497
+ const finalIds = finalChildren.map(getId).filter(Boolean);
498
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_sortable2.SortableContext, { id, items: finalIds, strategy: import_sortable2.horizontalListSortingStrategy, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `bob-react-tab-strip ${className}`, children: finalChildren }) });
499
+ };
500
+
501
+ // src/components/BobTab.tsx
502
+ var import_sortable3 = require("@dnd-kit/sortable");
503
+ var import_utilities = require("@dnd-kit/utilities");
504
+ var import_jsx_runtime4 = require("react/jsx-runtime");
505
+ var BobTab = ({ id, label, isActive = false, onClick, className = "" }) => {
506
+ const { designTabs } = useLayoutContext();
507
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = (0, import_sortable3.useSortable)({
508
+ id,
509
+ data: { type: "Tab" }
510
+ });
511
+ const style = {
512
+ transform: import_utilities.CSS.Transform.toString(transform),
513
+ transition,
514
+ opacity: isDragging ? 0.5 : 1,
515
+ zIndex: isDragging ? 2 : 1
516
+ };
517
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
518
+ "button",
519
+ {
520
+ ref: setNodeRef,
521
+ type: "button",
522
+ className: `bob-react-tab ${isActive ? "bob-react-tab-active" : ""} ${className}`,
523
+ style,
524
+ onClick: () => onClick?.(id),
525
+ children: [
526
+ designTabs && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "bob-react-tab-drag-handle", ...attributes, ...listeners, style: { marginRight: "8px", cursor: "grab", color: "#94a3b8" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("i", { className: "fa-solid fa-grip-vertical" }) }),
527
+ label
528
+ ]
529
+ }
530
+ );
531
+ };
532
+
533
+ // src/components/BobPage.tsx
534
+ var import_react4 = __toESM(require("react"));
535
+ var import_sortable4 = require("@dnd-kit/sortable");
536
+ var import_core3 = require("@dnd-kit/core");
537
+
538
+ // src/components/GapDropZone.tsx
539
+ var import_core2 = require("@dnd-kit/core");
540
+ var import_jsx_runtime5 = require("react/jsx-runtime");
541
+ var GapDropZone = ({ id, containerId, insertAfterItemId, position, colSpan, newline, onResize, onToggleNewline }) => {
542
+ const { designFields, designGroups, dragOverId } = useLayoutContext();
543
+ const isActive = dragOverId === id;
544
+ const showControls = (designFields || designGroups || !!onResize) && position !== "end";
545
+ const { setNodeRef } = (0, import_core2.useDroppable)({
546
+ id,
547
+ data: { type: "GapDropZone", containerId, insertAfterItemId }
548
+ });
549
+ const className = `bob-react-gap-zone ${position} ${isActive ? "bob-react-gap-zone-active" : ""}`;
550
+ if (!showControls) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: setNodeRef, className });
551
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
552
+ "div",
553
+ {
554
+ ref: setNodeRef,
555
+ className: `bob-react-gap-zone ${position} ${isActive ? "bob-react-gap-zone-active" : ""}`,
556
+ "data-gap-id": id,
557
+ children: showControls && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "bob-react-gap-controls", onPointerDown: (e) => e.stopPropagation(), children: [
558
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
559
+ "button",
560
+ {
561
+ className: "bob-react-gap-btn",
562
+ title: "Increase column span",
563
+ onPointerDown: (e) => {
564
+ e.preventDefault();
565
+ e.stopPropagation();
566
+ onResize(1);
567
+ },
568
+ children: "+"
569
+ }
570
+ ),
571
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "bob-react-gap-span-label", children: colSpan }),
572
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
573
+ "button",
574
+ {
575
+ className: "bob-react-gap-btn",
576
+ title: "Decrease column span",
577
+ onPointerDown: (e) => {
578
+ e.preventDefault();
579
+ e.stopPropagation();
580
+ onResize(-1);
581
+ },
582
+ children: "-"
583
+ }
584
+ ),
585
+ onToggleNewline && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
586
+ "button",
587
+ {
588
+ className: "bob-react-gap-btn",
589
+ title: newline ? "Remove Newline" : "Set Newline",
590
+ style: { marginTop: "2px", color: newline ? "var(--q-primary, #2563eb)" : "var(--q-text-muted, #94a3b8)" },
591
+ onPointerDown: (e) => {
592
+ e.preventDefault();
593
+ e.stopPropagation();
594
+ onToggleNewline();
595
+ },
596
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("i", { className: newline ? "fa-solid fa-arrow-turn-down" : "fa-solid fa-arrow-right" })
597
+ }
598
+ )
599
+ ] })
600
+ }
601
+ );
602
+ };
603
+
604
+ // src/components/BobPage.tsx
605
+ var import_jsx_runtime6 = require("react/jsx-runtime");
606
+ var BobPage = ({ id, children, columns, className = "" }) => {
607
+ const { layoutState, designGroups, pageCols } = useLayoutContext();
608
+ const activeColumns = columns ?? pageCols;
609
+ const { setNodeRef } = (0, import_core3.useDroppable)({ id });
610
+ const [panelSpanOverrides, setPanelSpanOverrides] = (0, import_react4.useState)({});
611
+ const [panelRowSpanOverrides, setPanelRowSpanOverrides] = (0, import_react4.useState)({});
612
+ const [panelNewlineOverrides, setPanelNewlineOverrides] = (0, import_react4.useState)({});
613
+ const childrenArray = import_react4.default.Children.toArray(children).filter(import_react4.default.isValidElement);
614
+ const getId = (c) => c.props.id || c.key;
615
+ const defaultIds = childrenArray.map(getId);
616
+ const itemIds = layoutState[id] || defaultIds;
617
+ const sortedChildren = itemIds.map((itemId) => childrenArray.find((c) => getId(c) === itemId)).filter(Boolean);
618
+ const newChildren = childrenArray.filter((c) => !itemIds.includes(getId(c)));
619
+ const finalChildren = [...sortedChildren, ...newChildren];
620
+ const finalIds = finalChildren.map(getId).filter(Boolean);
621
+ const styledPanels = finalChildren.map((child) => {
622
+ const panelId = getId(child);
623
+ const requestedColSpan = panelSpanOverrides[panelId] ?? child.props.colSpan ?? activeColumns;
624
+ const panelColSpan = Math.min(requestedColSpan, activeColumns);
625
+ const panelRowSpan = panelRowSpanOverrides[panelId] ?? child.props.rowSpan ?? 1;
626
+ const panelNewline = panelNewlineOverrides[panelId] ?? child.props.newline ?? false;
627
+ return import_react4.default.cloneElement(child, {
628
+ key: panelId,
629
+ colSpan: panelColSpan,
630
+ rowSpan: panelRowSpan,
631
+ newline: panelNewline,
632
+ onRowResize: (delta) => {
633
+ setPanelRowSpanOverrides((prev) => ({
634
+ ...prev,
635
+ [panelId]: Math.max(1, (prev[panelId] ?? child.props.rowSpan ?? 1) + delta)
636
+ }));
637
+ }
638
+ });
639
+ });
640
+ const buildPanelsWithGaps = () => {
641
+ if (!designGroups) return styledPanels;
642
+ const result = [];
643
+ styledPanels.forEach((panel, idx) => {
644
+ const panelId = getId(panel);
645
+ const panelColSpan = panel.props.colSpan ?? activeColumns;
646
+ const panelRowSpan = panel.props.rowSpan ?? 1;
647
+ const panelNewline = panel.props.newline ?? false;
648
+ const isFirst = idx === 0;
649
+ const isLast = idx === styledPanels.length - 1;
650
+ const gapId = isFirst ? `gap-${id}-start` : `gap-${id}-after-${getId(styledPanels[idx - 1])}`;
651
+ const insertAfterItemId = isFirst ? void 0 : getId(styledPanels[idx - 1]);
652
+ result.push(
653
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
654
+ "div",
655
+ {
656
+ style: {
657
+ display: "flex",
658
+ flexDirection: "row",
659
+ alignItems: "stretch",
660
+ gridColumn: panelNewline ? `1 / span ${panelColSpan}` : `span ${panelColSpan}`,
661
+ gridRow: `span ${panelRowSpan}`,
662
+ minWidth: 0,
663
+ boxSizing: "border-box"
664
+ },
665
+ children: [
666
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
667
+ GapDropZone,
668
+ {
669
+ id: gapId,
670
+ containerId: id,
671
+ insertAfterItemId,
672
+ colSpan: panelColSpan,
673
+ newline: panelNewline,
674
+ onResize: (delta) => {
675
+ setPanelSpanOverrides((prev) => {
676
+ const current = prev[panelId] ?? panel.props.colSpan ?? activeColumns;
677
+ return { ...prev, [panelId]: Math.max(1, current + delta) };
678
+ });
679
+ const el = document.querySelector(`[data-bob-item-id="${panelId}"], [id="${panelId}"]`);
680
+ if (el) {
681
+ const event = new CustomEvent("bob-resize", { detail: { delta }, bubbles: false });
682
+ el.dispatchEvent(event);
683
+ }
684
+ },
685
+ onToggleNewline: () => {
686
+ const newVal = !panelNewline;
687
+ setPanelNewlineOverrides((prev) => ({ ...prev, [panelId]: newVal }));
688
+ const el = document.querySelector(`[data-bob-item-id="${panelId}"], [id="${panelId}"]`);
689
+ if (el) {
690
+ const event = new CustomEvent("bob-toggle-newline", { detail: { newline: newVal }, bubbles: false });
691
+ el.dispatchEvent(event);
692
+ }
693
+ },
694
+ position: isFirst ? "start" : "between"
695
+ }
696
+ ),
697
+ panel,
698
+ isLast && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
699
+ GapDropZone,
700
+ {
701
+ id: `gap-${id}-after-${panelId}`,
702
+ containerId: id,
703
+ insertAfterItemId: panelId,
704
+ position: "end"
705
+ },
706
+ `gap-${id}-end`
707
+ )
708
+ ]
709
+ },
710
+ `pair-${id}-${panelId}`
711
+ )
712
+ );
713
+ });
714
+ if (styledPanels.length === 0) {
715
+ result.push(
716
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
717
+ GapDropZone,
718
+ {
719
+ id: `gap-${id}-start`,
720
+ containerId: id,
721
+ insertAfterItemId: void 0,
722
+ position: "start"
723
+ },
724
+ `gap-${id}-empty`
725
+ )
726
+ );
727
+ }
728
+ return result;
729
+ };
730
+ const pageStyle = {
731
+ gridTemplateColumns: `repeat(${activeColumns}, 1fr)`,
732
+ display: "grid",
733
+ gap: 0
734
+ };
735
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_sortable4.SortableContext, { id, items: finalIds, strategy: import_sortable4.rectSortingStrategy, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
736
+ "div",
737
+ {
738
+ ref: setNodeRef,
739
+ className: `bob-react-page ${className}`,
740
+ style: pageStyle,
741
+ children: designGroups ? buildPanelsWithGaps() : styledPanels
742
+ }
743
+ ) });
744
+ };
745
+
746
+ // src/components/BobTabPage.tsx
747
+ var import_jsx_runtime7 = require("react/jsx-runtime");
748
+ var BobTabPage = ({
749
+ id,
750
+ children,
751
+ isActive = false,
752
+ columns = 12,
753
+ className = "",
754
+ pageClassName = ""
755
+ }) => {
756
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: `bob-react-tab-page-wrapper ${className}`, style: { display: isActive ? "block" : "none" }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BobPage, { id: id ?? "tab-page", columns, className: pageClassName, children }) });
757
+ };
758
+
759
+ // src/components/BobPanel.tsx
760
+ var import_react5 = __toESM(require("react"));
761
+ var import_sortable5 = require("@dnd-kit/sortable");
762
+ var import_jsx_runtime8 = require("react/jsx-runtime");
763
+ var BobPanel = ({
764
+ id,
765
+ children,
766
+ colSpan = 12,
767
+ rowSpan = 1,
768
+ columns,
769
+ className = "",
770
+ caption,
771
+ newline = false,
772
+ onRowResize,
773
+ style: customStyle
774
+ }) => {
775
+ const { designGroups, designFields, layoutState, dragOverId, selectedItemIds, toggleItemSelection, groupCols } = useLayoutContext();
776
+ const activeColumns = columns ?? groupCols;
777
+ const isDragOver = dragOverId === id;
778
+ const isSelected = selectedItemIds.includes(id);
779
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = (0, import_sortable5.useSortable)({
780
+ id,
781
+ data: { type: "Panel" }
782
+ });
783
+ const [overrideColSpan, setOverrideColSpan] = (0, import_react5.useState)(null);
784
+ const activeColSpan = overrideColSpan ?? colSpan;
785
+ const [overrideRowSpan, setOverrideRowSpan] = (0, import_react5.useState)(null);
786
+ const activeRowSpan = overrideRowSpan ?? rowSpan;
787
+ const [overrideNewline, setOverrideNewline] = (0, import_react5.useState)(null);
788
+ const activeNewline = overrideNewline ?? newline;
789
+ const [itemSpanOverrides, setItemSpanOverrides] = (0, import_react5.useState)({});
790
+ const [itemRowSpanOverrides, setItemRowSpanOverrides] = (0, import_react5.useState)({});
791
+ const [itemNewlineOverrides, setItemNewlineOverrides] = (0, import_react5.useState)({});
792
+ const childrenArray = import_react5.default.Children.toArray(children).filter(import_react5.default.isValidElement);
793
+ const getId = (c) => c.props.id || c.key;
794
+ const defaultIds = childrenArray.map(getId);
795
+ const itemIds = layoutState[id] || defaultIds;
796
+ const sortedChildren = itemIds.map((itemId) => childrenArray.find((c) => getId(c) === itemId)).filter(Boolean);
797
+ const newChildren = childrenArray.filter((c) => !itemIds.includes(getId(c)));
798
+ const finalChildren = [...sortedChildren, ...newChildren];
799
+ const finalIds = finalChildren.map(getId).filter(Boolean);
800
+ const styledChildren = finalChildren.map((child) => {
801
+ const childId = getId(child);
802
+ const requestedColSpan = itemSpanOverrides[childId] ?? child.props.colSpan ?? 1;
803
+ const childColSpan = Math.min(requestedColSpan, activeColumns);
804
+ const childRowSpan = itemRowSpanOverrides[childId] ?? child.props.rowSpan ?? 1;
805
+ const childNewline = itemNewlineOverrides[childId] ?? child.props.newline ?? false;
806
+ return import_react5.default.cloneElement(child, {
807
+ key: childId,
808
+ colSpan: childColSpan,
809
+ rowSpan: childRowSpan,
810
+ newline: childNewline,
811
+ onRowResize: (delta) => {
812
+ setItemRowSpanOverrides((prev) => ({
813
+ ...prev,
814
+ [childId]: Math.max(1, (prev[childId] ?? child.props.rowSpan ?? 1) + delta)
815
+ }));
816
+ }
817
+ });
818
+ });
819
+ const baseStyle = {
820
+ gridColumn: activeNewline ? `1 / span ${activeColSpan}` : `span ${activeColSpan}`,
821
+ gridRow: `span ${activeRowSpan}`,
822
+ gridTemplateColumns: `repeat(${activeColumns}, 1fr)`,
823
+ ["--panel-columns"]: activeColumns,
824
+ transition,
825
+ opacity: isDragging ? 0.5 : 1,
826
+ zIndex: isDragging ? 2 : 1,
827
+ flex: 1,
828
+ // Expand to fill pair div in flex mode
829
+ width: "100%",
830
+ boxSizing: "border-box"
831
+ };
832
+ const nodeRef = import_react5.default.useRef(null);
833
+ import_react5.default.useEffect(() => {
834
+ const el = nodeRef.current;
835
+ if (!el) return;
836
+ const resizeHandler = (e) => {
837
+ const delta = e.detail?.delta;
838
+ if (typeof delta === "number") {
839
+ setOverrideColSpan((prev) => Math.max(1, (prev ?? activeColSpan) + delta));
840
+ }
841
+ };
842
+ const newlineHandler = (e) => {
843
+ const val = e.detail?.newline;
844
+ if (typeof val === "boolean") {
845
+ setOverrideNewline(val);
846
+ }
847
+ };
848
+ el.addEventListener("bob-resize", resizeHandler);
849
+ el.addEventListener("bob-toggle-newline", newlineHandler);
850
+ return () => {
851
+ el.removeEventListener("bob-resize", resizeHandler);
852
+ el.removeEventListener("bob-toggle-newline", newlineHandler);
853
+ };
854
+ }, [activeColSpan, activeNewline]);
855
+ const setRefs = (el) => {
856
+ nodeRef.current = el;
857
+ setNodeRef(el);
858
+ };
859
+ const buildChildrenWithGaps = () => {
860
+ if (!designFields) return styledChildren;
861
+ const result = [];
862
+ styledChildren.forEach((child, idx) => {
863
+ const childId = getId(child);
864
+ const childColSpan = child.props.colSpan ?? 1;
865
+ const childRowSpan = child.props.rowSpan ?? 1;
866
+ const childNewline = child.props.newline ?? false;
867
+ const isFirst = idx === 0;
868
+ const isLast = idx === styledChildren.length - 1;
869
+ const gapId = isFirst ? `gap-${id}-start` : `gap-${id}-after-${getId(styledChildren[idx - 1])}`;
870
+ const insertAfterItemId = isFirst ? void 0 : getId(styledChildren[idx - 1]);
871
+ result.push(
872
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
873
+ "div",
874
+ {
875
+ style: {
876
+ display: "flex",
877
+ flexDirection: "row",
878
+ alignItems: "stretch",
879
+ gridColumn: childNewline ? `1 / span ${childColSpan}` : `span ${childColSpan}`,
880
+ gridRow: `span ${childRowSpan}`,
881
+ minWidth: 0,
882
+ boxSizing: "border-box"
883
+ },
884
+ children: [
885
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
886
+ GapDropZone,
887
+ {
888
+ id: gapId,
889
+ containerId: id,
890
+ insertAfterItemId,
891
+ colSpan: childColSpan,
892
+ newline: childNewline,
893
+ onResize: (delta) => {
894
+ setItemSpanOverrides((prev) => {
895
+ const current = prev[childId] ?? child.props.colSpan ?? 1;
896
+ return { ...prev, [childId]: Math.max(1, current + delta) };
897
+ });
898
+ const el = document.querySelector(`[data-bob-item-id="${childId}"]`);
899
+ if (el) {
900
+ const event = new CustomEvent("bob-resize", { detail: { delta }, bubbles: false });
901
+ el.dispatchEvent(event);
902
+ }
903
+ },
904
+ onToggleNewline: () => {
905
+ const newVal = !childNewline;
906
+ setItemNewlineOverrides((prev) => ({ ...prev, [childId]: newVal }));
907
+ const el = document.querySelector(`[data-bob-item-id="${childId}"]`);
908
+ if (el) {
909
+ const event = new CustomEvent("bob-toggle-newline", { detail: { newline: newVal }, bubbles: false });
910
+ el.dispatchEvent(event);
911
+ }
912
+ },
913
+ position: isFirst ? "start" : "between"
914
+ }
915
+ ),
916
+ child,
917
+ isLast && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
918
+ GapDropZone,
919
+ {
920
+ id: `gap-${id}-after-${childId}`,
921
+ containerId: id,
922
+ insertAfterItemId: childId,
923
+ position: "end"
924
+ },
925
+ `gap-${id}-end`
926
+ )
927
+ ]
928
+ },
929
+ `pair-${id}-${childId}`
930
+ )
931
+ );
932
+ });
933
+ if (styledChildren.length === 0) {
934
+ result.push(
935
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
936
+ GapDropZone,
937
+ {
938
+ id: `gap-${id}-start`,
939
+ containerId: id,
940
+ insertAfterItemId: void 0,
941
+ position: "start"
942
+ },
943
+ `gap-${id}-empty`
944
+ )
945
+ );
946
+ }
947
+ return result;
948
+ };
949
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_sortable5.SortableContext, { id, items: finalIds, strategy: import_sortable5.rectSortingStrategy, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
950
+ "div",
951
+ {
952
+ ref: setRefs,
953
+ className: `bob-react-panel ${designGroups ? "bob-react-panel-design-mode" : ""} ${isSelected ? "bob-react-item-selected" : ""} ${isDragOver && !designFields && !designGroups ? "bob-react-panel-dragover" : ""} ${className}`,
954
+ style: { ...baseStyle, ...customStyle },
955
+ children: [
956
+ designGroups && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "bob-react-panel-row-controls", onPointerDown: (e) => e.stopPropagation(), children: [
957
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
958
+ "button",
959
+ {
960
+ className: "bob-react-gap-btn",
961
+ title: "Increase row span",
962
+ onPointerDown: (e) => {
963
+ e.preventDefault();
964
+ e.stopPropagation();
965
+ onRowResize ? onRowResize(1) : setOverrideRowSpan((prev) => (prev ?? activeRowSpan) + 1);
966
+ },
967
+ children: "+"
968
+ }
969
+ ),
970
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "bob-react-gap-span-label", children: activeRowSpan }),
971
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
972
+ "button",
973
+ {
974
+ className: "bob-react-gap-btn",
975
+ title: "Decrease row span",
976
+ onPointerDown: (e) => {
977
+ e.preventDefault();
978
+ e.stopPropagation();
979
+ onRowResize ? onRowResize(-1) : setOverrideRowSpan((prev) => Math.max(1, (prev ?? activeRowSpan) - 1));
980
+ },
981
+ children: "-"
982
+ }
983
+ )
984
+ ] }),
985
+ caption && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "bob-react-panel-header", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { className: "bob-react-panel-caption", children: caption }) }),
986
+ designFields ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "bob-react-panel-fields-row", children: buildChildrenWithGaps() }) : styledChildren,
987
+ designGroups && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
988
+ "div",
989
+ {
990
+ className: "bob-react-overlay",
991
+ ...attributes,
992
+ ...listeners,
993
+ onClick: (e) => {
994
+ toggleItemSelection(id, e.ctrlKey || e.metaKey);
995
+ }
996
+ }
997
+ )
998
+ ]
999
+ }
1000
+ ) });
1001
+ };
1002
+
1003
+ // src/components/BobItem.tsx
1004
+ var import_react6 = __toESM(require("react"));
1005
+ var import_sortable6 = require("@dnd-kit/sortable");
1006
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1007
+ var BobItem = ({
1008
+ id,
1009
+ children,
1010
+ colSpan = 1,
1011
+ rowSpan = 1,
1012
+ className = "",
1013
+ expandVertical = false,
1014
+ caption,
1015
+ hideCaption = false,
1016
+ newline = false,
1017
+ onRowResize,
1018
+ style: customStyle,
1019
+ horizontal = false,
1020
+ labelWidth = "120px"
1021
+ }) => {
1022
+ const { designFields, designGroups, selectedItemIds, toggleItemSelection } = useLayoutContext();
1023
+ const isSelected = selectedItemIds.includes(id);
1024
+ const [overrideColSpan, setOverrideColSpan] = (0, import_react6.useState)(null);
1025
+ const [overrideNewline, setOverrideNewline] = (0, import_react6.useState)(null);
1026
+ const activeColSpan = overrideColSpan ?? colSpan;
1027
+ const activeNewline = overrideNewline ?? newline;
1028
+ const {
1029
+ attributes,
1030
+ listeners,
1031
+ setNodeRef,
1032
+ transform,
1033
+ transition,
1034
+ isDragging
1035
+ } = (0, import_sortable6.useSortable)({
1036
+ id,
1037
+ data: { type: "Item" },
1038
+ disabled: !designFields
1039
+ });
1040
+ const style = designFields ? {
1041
+ // In design mode the wrapper pair-div owns the column width;
1042
+ // this item just fills the remaining space after the 20px gap.
1043
+ gridColumn: `span ${activeColSpan}`,
1044
+ gridRow: `span ${rowSpan}`,
1045
+ flex: "1 1 0",
1046
+ minWidth: 0,
1047
+ transition,
1048
+ opacity: isDragging ? 0.4 : 1,
1049
+ zIndex: isDragging ? 2 : 1
1050
+ } : {
1051
+ // Normal CSS-grid mode
1052
+ gridColumn: activeNewline ? `1 / span ${activeColSpan}` : `span ${activeColSpan}`,
1053
+ gridRow: `span ${rowSpan}`,
1054
+ transition,
1055
+ opacity: isDragging ? 0.4 : 1,
1056
+ zIndex: isDragging ? 2 : 1,
1057
+ pointerEvents: designGroups ? "none" : void 0
1058
+ };
1059
+ const nodeRef = import_react6.default.useRef(null);
1060
+ (0, import_react6.useEffect)(() => {
1061
+ const el = nodeRef.current;
1062
+ if (!el) return;
1063
+ const resizeHandler = (e) => {
1064
+ const delta = e.detail?.delta;
1065
+ if (typeof delta === "number") {
1066
+ setOverrideColSpan((prev) => Math.max(1, (prev ?? activeColSpan) + delta));
1067
+ }
1068
+ };
1069
+ const newlineHandler = (e) => {
1070
+ const val = e.detail?.newline;
1071
+ if (typeof val === "boolean") {
1072
+ setOverrideNewline(val);
1073
+ }
1074
+ };
1075
+ el.addEventListener("bob-resize", resizeHandler);
1076
+ el.addEventListener("bob-toggle-newline", newlineHandler);
1077
+ return () => {
1078
+ el.removeEventListener("bob-resize", resizeHandler);
1079
+ el.removeEventListener("bob-toggle-newline", newlineHandler);
1080
+ };
1081
+ }, [activeColSpan, activeNewline]);
1082
+ const setRefs = (el) => {
1083
+ nodeRef.current = el;
1084
+ setNodeRef(el);
1085
+ };
1086
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1087
+ "div",
1088
+ {
1089
+ ref: setRefs,
1090
+ "data-bob-item-id": id,
1091
+ className: `bob-react-item ${expandVertical ? "bob-react-item-expand-vertical" : ""} ${isSelected ? "bob-react-item-selected" : ""} ${className}`,
1092
+ style: { ...style, ...customStyle },
1093
+ children: [
1094
+ caption && !hideCaption && !horizontal && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bob-react-item-caption", children: caption }),
1095
+ horizontal ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1096
+ "div",
1097
+ {
1098
+ className: "bob-react-item-horizontal",
1099
+ style: { gridTemplateColumns: `${labelWidth} 1fr` },
1100
+ children: [
1101
+ caption && !hideCaption && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bob-react-item-horizontal-caption", children: caption }),
1102
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "bob-react-item-horizontal-content", children })
1103
+ ]
1104
+ }
1105
+ ) : children,
1106
+ designFields && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1107
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "bob-react-item-row-controls", onPointerDown: (e) => e.stopPropagation(), children: [
1108
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1109
+ "button",
1110
+ {
1111
+ className: "bob-react-button bob-react-item-row-btn",
1112
+ title: "Increase row span",
1113
+ onPointerDown: (e) => {
1114
+ e.preventDefault();
1115
+ e.stopPropagation();
1116
+ onRowResize?.(1);
1117
+ },
1118
+ children: "+"
1119
+ }
1120
+ ),
1121
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "bob-react-gap-span-label", children: rowSpan }),
1122
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1123
+ "button",
1124
+ {
1125
+ className: "bob-react-button bob-react-item-row-btn",
1126
+ title: "Decrease row span",
1127
+ onPointerDown: (e) => {
1128
+ e.preventDefault();
1129
+ e.stopPropagation();
1130
+ onRowResize?.(-1);
1131
+ },
1132
+ children: "-"
1133
+ }
1134
+ )
1135
+ ] }),
1136
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1137
+ "div",
1138
+ {
1139
+ className: "bob-react-overlay",
1140
+ ...attributes,
1141
+ ...listeners,
1142
+ onClick: (e) => {
1143
+ toggleItemSelection(id, e.ctrlKey || e.metaKey);
1144
+ }
1145
+ }
1146
+ )
1147
+ ] })
1148
+ ]
1149
+ }
1150
+ );
1151
+ };
1152
+
1153
+ // src/components/BobResponsiveness.tsx
1154
+ var import_react7 = __toESM(require("react"));
1155
+ var import_bob_ui_react_components = require("@qiiqa/bob-ui-react-components");
1156
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1157
+ var BobResponsiveness = ({
1158
+ className = ""
1159
+ }) => {
1160
+ const {
1161
+ responsivenessConfig: config,
1162
+ setResponsivenessConfig,
1163
+ currentBreakpoint
1164
+ } = useLayoutContext();
1165
+ const handleConfigChange = (tier, type, value) => {
1166
+ const newConfig = {
1167
+ ...config,
1168
+ [tier]: {
1169
+ ...config[tier],
1170
+ [type]: value ?? 0
1171
+ }
1172
+ };
1173
+ setResponsivenessConfig(newConfig);
1174
+ };
1175
+ const currentTier = config[currentBreakpoint];
1176
+ const displayValue = currentTier ? `P:${currentTier.page}, G:${currentTier.group}` : "...";
1177
+ const tiers = [
1178
+ { key: "small", label: "Small" },
1179
+ { key: "medium", label: "Medium" },
1180
+ { key: "large", label: "Large" },
1181
+ { key: "extraLarge", label: "Extra Large" }
1182
+ ];
1183
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: `bob-react-responsiveness ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1184
+ import_bob_ui_react_components.BobBoxTextPopupGrid,
1185
+ {
1186
+ id: "bob-responsiveness-popup",
1187
+ className: "bob-react-responsiveness-display",
1188
+ value: displayValue,
1189
+ icon: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: currentBreakpoint.toUpperCase().charAt(0) }),
1190
+ isReadOnly: true,
1191
+ rowCaptions: ["Small", "Medium", "Large", "Extra Large"],
1192
+ colCaptions: ["Page", "Group"],
1193
+ colWidths: ["max-content", "50px", "50px"],
1194
+ popupWidth: "auto",
1195
+ children: tiers.map((tier, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react7.default.Fragment, { children: [
1196
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1197
+ import_bob_ui_react_components.BobBoxInteger,
1198
+ {
1199
+ id: `resp-${tier.key}-page`,
1200
+ row: rowIndex,
1201
+ col: 0,
1202
+ value: config[tier.key].page,
1203
+ onChange: (v) => handleConfigChange(tier.key, "page", v),
1204
+ minValue: 1,
1205
+ maxValue: 24
1206
+ }
1207
+ ),
1208
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1209
+ import_bob_ui_react_components.BobBoxInteger,
1210
+ {
1211
+ id: `resp-${tier.key}-group`,
1212
+ row: rowIndex,
1213
+ col: 1,
1214
+ value: config[tier.key].group,
1215
+ onChange: (v) => handleConfigChange(tier.key, "group", v),
1216
+ minValue: 1,
1217
+ maxValue: 24
1218
+ }
1219
+ )
1220
+ ] }, tier.key))
1221
+ }
1222
+ ) });
1223
+ };
1224
+ // Annotate the CommonJS export names for ESM import in node:
1225
+ 0 && (module.exports = {
1226
+ BobForm,
1227
+ BobItem,
1228
+ BobPage,
1229
+ BobPanel,
1230
+ BobResponsiveness,
1231
+ BobTab,
1232
+ BobTabPage,
1233
+ BobTabStrip,
1234
+ DEFAULT_RESPONSIVENESS_CONFIG,
1235
+ GapDropZone,
1236
+ LayoutProvider,
1237
+ useLayoutContext
1238
+ });