@mieweb/ui 0.7.3-dev.15 → 0.7.3-dev.17

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.
Files changed (40) hide show
  1. package/dist/brands/index.cjs +12 -12
  2. package/dist/brands/index.js +3 -3
  3. package/dist/{chunk-33TQ25IK.js → chunk-2WLP2ERQ.js} +4 -4
  4. package/dist/{chunk-33TQ25IK.js.map → chunk-2WLP2ERQ.js.map} +1 -1
  5. package/dist/{chunk-I2AHT7JM.js → chunk-CKZX6IQN.js} +27 -2
  6. package/dist/chunk-CKZX6IQN.js.map +1 -0
  7. package/dist/{chunk-MO65V64I.js → chunk-DSSMQH74.js} +2 -2
  8. package/dist/{chunk-MO65V64I.js.map → chunk-DSSMQH74.js.map} +1 -1
  9. package/dist/chunk-F57VRLKP.js +708 -0
  10. package/dist/chunk-F57VRLKP.js.map +1 -0
  11. package/dist/{chunk-JU62BLDY.cjs → chunk-GJD4OW7P.cjs} +27 -2
  12. package/dist/chunk-GJD4OW7P.cjs.map +1 -0
  13. package/dist/{chunk-JDHIBJ5K.cjs → chunk-RBEQQ4GG.cjs} +2 -2
  14. package/dist/{chunk-JDHIBJ5K.cjs.map → chunk-RBEQQ4GG.cjs.map} +1 -1
  15. package/dist/{chunk-35HHCXWC.cjs → chunk-SETNPK4P.cjs} +4 -4
  16. package/dist/{chunk-35HHCXWC.cjs.map → chunk-SETNPK4P.cjs.map} +1 -1
  17. package/dist/chunk-YVIH26FG.cjs +736 -0
  18. package/dist/chunk-YVIH26FG.cjs.map +1 -0
  19. package/dist/components/CustomizableDashboard/index.cjs +43 -0
  20. package/dist/components/CustomizableDashboard/index.cjs.map +1 -0
  21. package/dist/components/CustomizableDashboard/index.d.cts +195 -0
  22. package/dist/components/CustomizableDashboard/index.d.ts +195 -0
  23. package/dist/components/CustomizableDashboard/index.js +10 -0
  24. package/dist/components/CustomizableDashboard/index.js.map +1 -0
  25. package/dist/esheet.cjs +6 -6
  26. package/dist/esheet.js +3 -3
  27. package/dist/index.cjs +196 -167
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +1 -0
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.js +20 -19
  32. package/dist/index.js.map +1 -1
  33. package/dist/styles.css +1 -1
  34. package/dist/tailwind-preset.cjs +4 -4
  35. package/dist/tailwind-preset.js +1 -1
  36. package/dist/utils/index.cjs +9 -9
  37. package/dist/utils/index.js +1 -1
  38. package/package.json +4 -1
  39. package/dist/chunk-I2AHT7JM.js.map +0 -1
  40. package/dist/chunk-JU62BLDY.cjs.map +0 -1
@@ -0,0 +1,708 @@
1
+ import { Switch } from './chunk-AQ564XO7.js';
2
+ import { Sheet, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter } from './chunk-LMBXJZLG.js';
3
+ import { Button } from './chunk-D2GID3YK.js';
4
+ import { cn } from './chunk-TQONQSJO.js';
5
+ import * as React2 from 'react';
6
+ import { createPortal } from 'react-dom';
7
+ import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, pointerWithin, closestCenter, rectIntersection, useDroppable } from '@dnd-kit/core';
8
+ import { sortableKeyboardCoordinates, arrayMove, SortableContext, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
9
+ import { CSS } from '@dnd-kit/utilities';
10
+ import { SlidersHorizontal, Columns3, Columns2, Square, RotateCcw, GripVertical } from 'lucide-react';
11
+ import { jsxs, jsx } from 'react/jsx-runtime';
12
+
13
+ function DashboardCustomizePanel({
14
+ open,
15
+ onOpenChange,
16
+ widgets,
17
+ hiddenIds,
18
+ onToggleWidget,
19
+ onReset,
20
+ title = "Customize dashboard",
21
+ description = "Choose which widgets appear on this dashboard. Drag a widget by its handle to rearrange it."
22
+ }) {
23
+ const hidden = React2.useMemo(() => new Set(hiddenIds), [hiddenIds]);
24
+ const groups = React2.useMemo(() => {
25
+ const map = /* @__PURE__ */ new Map();
26
+ for (const w of widgets) {
27
+ const key = w.category ?? "";
28
+ const list = map.get(key);
29
+ if (list) list.push(w);
30
+ else map.set(key, [w]);
31
+ }
32
+ return Array.from(map.entries());
33
+ }, [widgets]);
34
+ const visibleCount = widgets.length - hidden.size;
35
+ return /* @__PURE__ */ jsxs(
36
+ Sheet,
37
+ {
38
+ open,
39
+ onOpenChange,
40
+ side: "end",
41
+ "aria-label": title,
42
+ children: [
43
+ /* @__PURE__ */ jsx(SheetHeader, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
44
+ /* @__PURE__ */ jsx(SheetTitle, { children: title }),
45
+ /* @__PURE__ */ jsx(SheetDescription, { children: description })
46
+ ] }) }),
47
+ /* @__PURE__ */ jsxs(SheetBody, { className: "flex flex-col gap-5", children: [
48
+ /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground text-xs font-medium", children: [
49
+ visibleCount,
50
+ " of ",
51
+ widgets.length,
52
+ " widgets shown"
53
+ ] }),
54
+ groups.map(([category, items]) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
55
+ category && /* @__PURE__ */ jsx("h3", { className: "text-muted-foreground pb-1 text-xs font-semibold tracking-wide uppercase", children: category }),
56
+ /* @__PURE__ */ jsx("ul", { className: "flex list-none flex-col gap-1 p-0", children: items.map((widget) => {
57
+ const isVisible = !hidden.has(widget.id);
58
+ return /* @__PURE__ */ jsxs(
59
+ "li",
60
+ {
61
+ className: cn(
62
+ "border-border flex items-center gap-3 rounded-lg border p-3 transition-colors",
63
+ isVisible ? "bg-card" : "bg-muted/50"
64
+ ),
65
+ children: [
66
+ widget.icon && /* @__PURE__ */ jsx(
67
+ "span",
68
+ {
69
+ className: cn(
70
+ "shrink-0",
71
+ isVisible ? "text-primary-600 dark:text-primary-400" : "text-muted-foreground"
72
+ ),
73
+ children: widget.icon
74
+ }
75
+ ),
76
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
77
+ /* @__PURE__ */ jsx(
78
+ "span",
79
+ {
80
+ className: cn(
81
+ "truncate text-sm font-medium",
82
+ isVisible ? "text-foreground" : "text-muted-foreground"
83
+ ),
84
+ children: widget.title
85
+ }
86
+ ),
87
+ widget.description && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate text-xs", children: widget.description })
88
+ ] }),
89
+ /* @__PURE__ */ jsx(
90
+ Switch,
91
+ {
92
+ checked: isVisible,
93
+ onCheckedChange: (checked) => onToggleWidget(widget.id, checked),
94
+ "aria-label": `Show ${widget.title}`
95
+ }
96
+ )
97
+ ]
98
+ },
99
+ widget.id
100
+ );
101
+ }) })
102
+ ] }, category || "general"))
103
+ ] }),
104
+ onReset && /* @__PURE__ */ jsx(SheetFooter, { children: /* @__PURE__ */ jsx(
105
+ Button,
106
+ {
107
+ variant: "outline",
108
+ size: "sm",
109
+ onClick: onReset,
110
+ leftIcon: /* @__PURE__ */ jsx(RotateCcw, { "aria-hidden": "true", className: "h-4 w-4" }),
111
+ children: "Reset layout"
112
+ }
113
+ ) })
114
+ ]
115
+ }
116
+ );
117
+ }
118
+ var COL_IDS = ["column-0", "column-1", "column-2"];
119
+ var DEFAULT_HANDLE_SELECTOR = '[data-slot="dashboard-widget-header"]';
120
+ var HEADER_LAYOUT_CLASSES = [
121
+ "flex",
122
+ "flex-row",
123
+ "flex-nowrap",
124
+ "items-center",
125
+ "[&>:nth-child(2)]:ms-auto"
126
+ ];
127
+ function orderStorageKey(base) {
128
+ return `${base}-portlet-order`;
129
+ }
130
+ function layoutStorageKey(base) {
131
+ return `${base}-dashboard-layout`;
132
+ }
133
+ function hiddenStorageKey(base) {
134
+ return `${base}-dashboard-hidden`;
135
+ }
136
+ function loadHidden(base) {
137
+ if (!base) return null;
138
+ try {
139
+ const raw = localStorage.getItem(hiddenStorageKey(base));
140
+ const parsed = raw ? JSON.parse(raw) : null;
141
+ return Array.isArray(parsed) ? parsed.filter((v) => typeof v === "string") : null;
142
+ } catch {
143
+ return null;
144
+ }
145
+ }
146
+ function saveHidden(base, ids) {
147
+ if (!base) return;
148
+ try {
149
+ localStorage.setItem(hiddenStorageKey(base), JSON.stringify(ids));
150
+ } catch {
151
+ }
152
+ }
153
+ function loadLayout(base) {
154
+ if (!base) return null;
155
+ try {
156
+ const raw = localStorage.getItem(layoutStorageKey(base));
157
+ const val = raw ? Number(raw) : null;
158
+ return val === 1 || val === 2 || val === 3 ? val : null;
159
+ } catch {
160
+ return null;
161
+ }
162
+ }
163
+ function saveLayout(base, mode) {
164
+ if (!base) return;
165
+ try {
166
+ localStorage.setItem(layoutStorageKey(base), String(mode));
167
+ } catch {
168
+ }
169
+ }
170
+ function sanitizeSlot(value) {
171
+ return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
172
+ }
173
+ function loadOrder(base) {
174
+ if (!base) return null;
175
+ try {
176
+ const raw = localStorage.getItem(orderStorageKey(base));
177
+ if (!raw) return null;
178
+ const parsed = JSON.parse(raw);
179
+ return [
180
+ sanitizeSlot(parsed[0]),
181
+ sanitizeSlot(parsed[1]),
182
+ sanitizeSlot(parsed[2])
183
+ ];
184
+ } catch {
185
+ return null;
186
+ }
187
+ }
188
+ function saveOrder(base, order) {
189
+ if (!base) return;
190
+ try {
191
+ const record = {};
192
+ order.forEach((ids, i) => {
193
+ record[i] = ids;
194
+ });
195
+ localStorage.setItem(orderStorageKey(base), JSON.stringify(record));
196
+ } catch {
197
+ }
198
+ }
199
+ function mergeColumnOrder(columns, saved) {
200
+ const allIds = new Set(
201
+ [...columns[0], ...columns[1], ...columns[2]].map((p) => p.id)
202
+ );
203
+ const result = [[], [], []];
204
+ const placed = /* @__PURE__ */ new Set();
205
+ for (let i = 0; i < 3; i++) {
206
+ for (const id of saved?.[i] ?? []) {
207
+ if (allIds.has(id) && !placed.has(id)) {
208
+ result[i].push(id);
209
+ placed.add(id);
210
+ }
211
+ }
212
+ }
213
+ for (let i = 0; i < 3; i++) {
214
+ for (const item of columns[i]) {
215
+ if (!placed.has(item.id)) {
216
+ result[i].push(item.id);
217
+ placed.add(item.id);
218
+ }
219
+ }
220
+ }
221
+ return result;
222
+ }
223
+ function buildItemMap(columns) {
224
+ const map = /* @__PURE__ */ new Map();
225
+ for (const col of columns) {
226
+ for (const item of col) map.set(item.id, item);
227
+ }
228
+ return map;
229
+ }
230
+ function findColumn(colOrder, itemId) {
231
+ return colOrder.findIndex((ids) => ids.includes(itemId));
232
+ }
233
+ function moveAcrossColumns(order, activeId, overId) {
234
+ const colIdx = COL_IDS.indexOf(overId);
235
+ const sourceCol = findColumn(order, activeId);
236
+ if (sourceCol === -1) return order;
237
+ const targetCol = colIdx !== -1 ? colIdx : findColumn(order, overId);
238
+ if (targetCol === -1 || sourceCol === targetCol) return order;
239
+ const next = order.map((col) => [...col]);
240
+ next[sourceCol] = next[sourceCol].filter((id) => id !== activeId);
241
+ const overIdx = colIdx !== -1 ? -1 : next[targetCol].indexOf(overId);
242
+ if (overIdx === -1) {
243
+ next[targetCol].push(activeId);
244
+ } else {
245
+ next[targetCol].splice(overIdx, 0, activeId);
246
+ }
247
+ return next;
248
+ }
249
+ function reorderOnDrop(order, activeId, overId, crossedColumns) {
250
+ const isColumnTarget = COL_IDS.indexOf(overId) !== -1;
251
+ if (isColumnTarget || overId === activeId) return order;
252
+ const col = findColumn(order, activeId);
253
+ if (col === -1 || findColumn(order, overId) !== col) return order;
254
+ const oldIdx = order[col].indexOf(activeId);
255
+ const newIdx = order[col].indexOf(overId);
256
+ if (oldIdx === -1 || newIdx === -1) return order;
257
+ if (crossedColumns && newIdx === oldIdx + 1) return order;
258
+ const next = order.map((c) => [...c]);
259
+ next[col] = arrayMove(next[col], oldIdx, newIdx);
260
+ return next;
261
+ }
262
+ function consolidateColumns(order, mode) {
263
+ if (mode >= 3 || requiredColumns(order) <= mode) return order;
264
+ if (mode === 1) return [[...order[0], ...order[1], ...order[2]], [], []];
265
+ return [[...order[0]], [...order[1], ...order[2]], []];
266
+ }
267
+ function requiredColumns(order) {
268
+ return Math.max(
269
+ 1,
270
+ ...order.map((ids, i) => ids.length > 0 ? i + 1 : 0)
271
+ );
272
+ }
273
+ var collisionDetection = (args) => {
274
+ const pw = pointerWithin(args);
275
+ if (pw.length > 0) return pw;
276
+ const cc = closestCenter(args);
277
+ if (cc.length > 0) return cc;
278
+ return rectIntersection(args);
279
+ };
280
+ function SortablePortlet({
281
+ id,
282
+ handleSelector,
283
+ children
284
+ }) {
285
+ const {
286
+ attributes,
287
+ listeners,
288
+ setNodeRef,
289
+ transform,
290
+ transition,
291
+ isDragging
292
+ } = useSortable({ id });
293
+ const wrapperRef = React2.useRef(null);
294
+ const [headerEl, setHeaderEl] = React2.useState(null);
295
+ const [searched, setSearched] = React2.useState(false);
296
+ const style = {
297
+ transform: CSS.Transform.toString(transform),
298
+ transition,
299
+ opacity: isDragging ? 0.4 : 1
300
+ };
301
+ React2.useEffect(() => {
302
+ const container = wrapperRef.current;
303
+ if (!container) return;
304
+ let rafId = 0;
305
+ const findHeader = () => {
306
+ const header = container.querySelector(handleSelector);
307
+ if (header instanceof HTMLElement) {
308
+ header.classList.add(...HEADER_LAYOUT_CLASSES);
309
+ setHeaderEl(header);
310
+ observer.disconnect();
311
+ } else {
312
+ setHeaderEl(null);
313
+ }
314
+ setSearched(true);
315
+ };
316
+ const observer = new MutationObserver(() => {
317
+ cancelAnimationFrame(rafId);
318
+ rafId = requestAnimationFrame(findHeader);
319
+ });
320
+ findHeader();
321
+ observer.observe(container, { childList: true, subtree: true });
322
+ return () => {
323
+ cancelAnimationFrame(rafId);
324
+ observer.disconnect();
325
+ };
326
+ }, [handleSelector]);
327
+ const handle = /* @__PURE__ */ jsx(
328
+ "button",
329
+ {
330
+ type: "button",
331
+ className: cn(
332
+ "inline-flex h-7 w-6 shrink-0 cursor-grab touch-none items-center justify-center rounded border-none bg-transparent",
333
+ "text-muted-foreground hover:bg-muted hover:text-foreground active:cursor-grabbing",
334
+ "opacity-40 transition-opacity group-hover/portlet:opacity-100 focus-visible:opacity-100",
335
+ "[@media(hover:none)]:opacity-100",
336
+ !headerEl && "bg-card/80 absolute end-2 top-2 z-10 shadow-sm"
337
+ ),
338
+ "aria-label": "Drag to reorder",
339
+ ...attributes,
340
+ ...listeners,
341
+ children: /* @__PURE__ */ jsx(GripVertical, { "aria-hidden": "true", className: "h-4 w-4" })
342
+ }
343
+ );
344
+ return /* @__PURE__ */ jsxs(
345
+ "div",
346
+ {
347
+ ref: (node) => {
348
+ setNodeRef(node);
349
+ wrapperRef.current = node;
350
+ },
351
+ style,
352
+ className: "group/portlet relative min-w-0",
353
+ children: [
354
+ /* @__PURE__ */ jsx("div", { className: "w-full min-w-0", children }),
355
+ headerEl ? createPortal(handle, headerEl) : searched ? handle : null
356
+ ]
357
+ }
358
+ );
359
+ }
360
+ function DroppableColumn({
361
+ id,
362
+ items,
363
+ columnIndex,
364
+ handleSelector,
365
+ flatten,
366
+ dragActive
367
+ }) {
368
+ const { setNodeRef, isOver } = useDroppable({ id });
369
+ const ids = items.map((p) => p.id);
370
+ return /* @__PURE__ */ jsx(SortableContext, { items: ids, strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsxs(
371
+ "div",
372
+ {
373
+ ref: setNodeRef,
374
+ className: cn(
375
+ "flex min-h-16 min-w-0 flex-col gap-3",
376
+ flatten && "max-lg:contents"
377
+ ),
378
+ role: "group",
379
+ "aria-label": `Dashboard column ${columnIndex + 1}`,
380
+ children: [
381
+ items.map((item) => /* @__PURE__ */ jsx(
382
+ SortablePortlet,
383
+ {
384
+ id: item.id,
385
+ handleSelector,
386
+ children: item.node
387
+ },
388
+ item.id
389
+ )),
390
+ items.length === 0 && dragActive && /* @__PURE__ */ jsx(
391
+ "div",
392
+ {
393
+ "aria-hidden": "true",
394
+ className: cn(
395
+ "border-border text-muted-foreground flex min-h-32 items-center justify-center rounded-lg border-2 border-dashed text-sm font-medium transition-colors",
396
+ isOver && "border-primary-500 bg-primary-500/10 text-primary-600"
397
+ ),
398
+ children: "Drop here"
399
+ }
400
+ )
401
+ ]
402
+ }
403
+ ) });
404
+ }
405
+ var CustomizableDashboard = React2.forwardRef(function CustomizableDashboard2({
406
+ columns,
407
+ title,
408
+ storageKey,
409
+ ariaLabel,
410
+ toolbarSlot,
411
+ defaultOrder,
412
+ order: controlledOrder,
413
+ onOrderChange,
414
+ defaultLayout = 3,
415
+ layout: controlledLayout,
416
+ onLayoutChange,
417
+ dragHandleSelector = DEFAULT_HANDLE_SELECTOR,
418
+ widgets,
419
+ defaultHiddenIds,
420
+ hiddenIds: controlledHidden,
421
+ onHiddenChange,
422
+ customizeLabel = "Customize",
423
+ className
424
+ }, ref) {
425
+ const itemMap = React2.useMemo(() => buildItemMap(columns), [columns]);
426
+ const [internalLayout, setInternalLayout] = React2.useState(defaultLayout);
427
+ const layoutMode = controlledLayout ?? internalLayout;
428
+ const [colOrder, setColOrder] = React2.useState(
429
+ () => consolidateColumns(
430
+ mergeColumnOrder(columns, controlledOrder ?? defaultOrder ?? null),
431
+ controlledLayout ?? defaultLayout
432
+ )
433
+ );
434
+ const [activeId, setActiveId] = React2.useState(null);
435
+ const [internalHidden, setInternalHidden] = React2.useState(
436
+ () => defaultHiddenIds ?? []
437
+ );
438
+ const hiddenList = controlledHidden ?? internalHidden;
439
+ const hiddenSet = React2.useMemo(() => new Set(hiddenList), [hiddenList]);
440
+ const [panelOpen, setPanelOpen] = React2.useState(false);
441
+ const prevColOrder = React2.useRef(colOrder);
442
+ const layoutModeRef = React2.useRef(layoutMode);
443
+ layoutModeRef.current = layoutMode;
444
+ const colOrderRef = React2.useRef(colOrder);
445
+ colOrderRef.current = colOrder;
446
+ const columnsRef = React2.useRef(columns);
447
+ columnsRef.current = columns;
448
+ const draggingRef = React2.useRef(false);
449
+ React2.useEffect(() => {
450
+ if (!storageKey) return;
451
+ if (controlledLayout === void 0) {
452
+ const savedLayout = loadLayout(storageKey);
453
+ if (savedLayout !== null) setInternalLayout(savedLayout);
454
+ }
455
+ if (controlledOrder === void 0) {
456
+ const savedOrder = loadOrder(storageKey);
457
+ if (savedOrder) {
458
+ setColOrder(mergeColumnOrder(columnsRef.current, savedOrder));
459
+ }
460
+ }
461
+ if (controlledHidden === void 0) {
462
+ const savedHidden = loadHidden(storageKey);
463
+ if (savedHidden) setInternalHidden(savedHidden);
464
+ }
465
+ }, [storageKey]);
466
+ React2.useEffect(() => {
467
+ if (draggingRef.current) return;
468
+ setColOrder((prev) => {
469
+ const next = consolidateColumns(
470
+ mergeColumnOrder(columns, controlledOrder ?? prev),
471
+ layoutMode
472
+ );
473
+ return JSON.stringify(next) === JSON.stringify(prev) ? prev : next;
474
+ });
475
+ }, [columns, controlledOrder, layoutMode]);
476
+ const setLayout = React2.useCallback(
477
+ (mode) => {
478
+ if (controlledLayout === void 0) setInternalLayout(mode);
479
+ saveLayout(storageKey, mode);
480
+ onLayoutChange?.(mode);
481
+ },
482
+ [controlledLayout, storageKey, onLayoutChange]
483
+ );
484
+ const commitOrder = React2.useCallback(
485
+ (next) => {
486
+ saveOrder(storageKey, next);
487
+ onOrderChange?.(next);
488
+ const occupied = requiredColumns(next);
489
+ if (occupied < layoutModeRef.current) {
490
+ setLayout(occupied);
491
+ }
492
+ },
493
+ [storageKey, onOrderChange, setLayout]
494
+ );
495
+ const handleLayoutChange = React2.useCallback(
496
+ (mode) => {
497
+ setLayout(mode);
498
+ const next = consolidateColumns(colOrderRef.current, mode);
499
+ if (next !== colOrderRef.current) {
500
+ setColOrder(next);
501
+ saveOrder(storageKey, next);
502
+ onOrderChange?.(next);
503
+ }
504
+ },
505
+ [setLayout, storageKey, onOrderChange]
506
+ );
507
+ const sensors = useSensors(
508
+ useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
509
+ useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
510
+ );
511
+ const resolve = (ids) => ids.filter((id) => !hiddenSet.has(id)).map((id) => itemMap.get(id)).filter(Boolean);
512
+ const handleDragStart = React2.useCallback(
513
+ (event) => {
514
+ prevColOrder.current = colOrder;
515
+ draggingRef.current = true;
516
+ setActiveId(String(event.active.id));
517
+ },
518
+ [colOrder]
519
+ );
520
+ const handleDragOver = React2.useCallback((event) => {
521
+ const { active, over } = event;
522
+ if (!over) return;
523
+ const next = moveAcrossColumns(
524
+ colOrderRef.current,
525
+ String(active.id),
526
+ String(over.id)
527
+ );
528
+ if (next !== colOrderRef.current) setColOrder(next);
529
+ }, []);
530
+ const handleDragEnd = React2.useCallback(
531
+ (event) => {
532
+ const { active, over } = event;
533
+ draggingRef.current = false;
534
+ setActiveId(null);
535
+ if (!over) {
536
+ setColOrder(prevColOrder.current);
537
+ return;
538
+ }
539
+ const activeItemId = String(active.id);
540
+ const crossedColumns = findColumn(prevColOrder.current, activeItemId) !== findColumn(colOrderRef.current, activeItemId);
541
+ const next = reorderOnDrop(
542
+ colOrderRef.current,
543
+ activeItemId,
544
+ String(over.id),
545
+ crossedColumns
546
+ );
547
+ if (next !== colOrderRef.current) setColOrder(next);
548
+ if (JSON.stringify(next) !== JSON.stringify(prevColOrder.current)) {
549
+ commitOrder(next);
550
+ }
551
+ },
552
+ [commitOrder]
553
+ );
554
+ const handleDragCancel = React2.useCallback(() => {
555
+ draggingRef.current = false;
556
+ setActiveId(null);
557
+ setColOrder(prevColOrder.current);
558
+ }, []);
559
+ const setHidden = React2.useCallback(
560
+ (ids) => {
561
+ if (controlledHidden === void 0) setInternalHidden(ids);
562
+ saveHidden(storageKey, ids);
563
+ onHiddenChange?.(ids);
564
+ },
565
+ [controlledHidden, storageKey, onHiddenChange]
566
+ );
567
+ const handleToggleWidget = React2.useCallback(
568
+ (id, visible) => {
569
+ setHidden(
570
+ visible ? hiddenList.filter((h) => h !== id) : [...hiddenList, id]
571
+ );
572
+ },
573
+ [hiddenList, setHidden]
574
+ );
575
+ const handleReset = React2.useCallback(() => {
576
+ if (storageKey) {
577
+ try {
578
+ localStorage.removeItem(orderStorageKey(storageKey));
579
+ localStorage.removeItem(layoutStorageKey(storageKey));
580
+ localStorage.removeItem(hiddenStorageKey(storageKey));
581
+ } catch {
582
+ }
583
+ }
584
+ const next = mergeColumnOrder(columns, defaultOrder ?? null);
585
+ setColOrder(next);
586
+ onOrderChange?.(next);
587
+ if (controlledLayout === void 0) setInternalLayout(defaultLayout);
588
+ onLayoutChange?.(defaultLayout);
589
+ const hidden = defaultHiddenIds ?? [];
590
+ if (controlledHidden === void 0) setInternalHidden(hidden);
591
+ onHiddenChange?.(hidden);
592
+ }, [
593
+ storageKey,
594
+ columns,
595
+ defaultOrder,
596
+ onOrderChange,
597
+ controlledLayout,
598
+ defaultLayout,
599
+ onLayoutChange,
600
+ defaultHiddenIds,
601
+ controlledHidden,
602
+ onHiddenChange
603
+ ]);
604
+ const cols = [
605
+ resolve(colOrder[0]),
606
+ resolve(colOrder[1]),
607
+ resolve(colOrder[2])
608
+ ];
609
+ const visibleCols = layoutMode === 1 ? [cols[0]] : layoutMode === 2 ? [cols[0], cols[1]] : cols;
610
+ const toolbar = /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3 pb-2", children: [
611
+ title && /* @__PURE__ */ jsx("h2", { className: "text-foreground me-auto text-lg font-semibold", children: title }),
612
+ widgets && widgets.length > 0 && /* @__PURE__ */ jsx(
613
+ Button,
614
+ {
615
+ variant: "outline",
616
+ size: "sm",
617
+ onClick: () => setPanelOpen(true),
618
+ "aria-haspopup": "dialog",
619
+ leftIcon: /* @__PURE__ */ jsx(SlidersHorizontal, { "aria-hidden": "true", className: "h-4 w-4" }),
620
+ children: customizeLabel
621
+ }
622
+ ),
623
+ /* @__PURE__ */ jsx(
624
+ "div",
625
+ {
626
+ className: "border-border bg-muted flex gap-0.5 rounded-md border p-0.5 max-md:hidden",
627
+ role: "radiogroup",
628
+ "aria-label": "Column layout",
629
+ children: [3, 2, 1].map((mode) => {
630
+ const Icon = mode === 3 ? Columns3 : mode === 2 ? Columns2 : Square;
631
+ return /* @__PURE__ */ jsx(
632
+ "button",
633
+ {
634
+ type: "button",
635
+ className: cn(
636
+ "flex h-7 w-7 items-center justify-center rounded transition-colors",
637
+ layoutMode === mode ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:bg-background hover:text-foreground"
638
+ ),
639
+ onClick: () => handleLayoutChange(mode),
640
+ role: "radio",
641
+ "aria-checked": layoutMode === mode,
642
+ "aria-label": `${mode} column layout`,
643
+ title: `${mode} column${mode > 1 ? "s" : ""}`,
644
+ children: /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true", className: "h-4 w-4" })
645
+ },
646
+ mode
647
+ );
648
+ })
649
+ }
650
+ )
651
+ ] });
652
+ return /* @__PURE__ */ jsxs(
653
+ DndContext,
654
+ {
655
+ sensors,
656
+ collisionDetection,
657
+ onDragStart: handleDragStart,
658
+ onDragOver: handleDragOver,
659
+ onDragEnd: handleDragEnd,
660
+ onDragCancel: handleDragCancel,
661
+ children: [
662
+ toolbarSlot ? createPortal(toolbar, toolbarSlot) : toolbar,
663
+ /* @__PURE__ */ jsx(
664
+ "div",
665
+ {
666
+ ref,
667
+ className: cn(
668
+ "grid items-start gap-3",
669
+ layoutMode === 3 && "grid-cols-1 md:grid-cols-2 lg:grid-cols-3",
670
+ layoutMode === 2 && "grid-cols-1 md:grid-cols-2",
671
+ layoutMode === 1 && "grid-cols-1",
672
+ className
673
+ ),
674
+ role: "region",
675
+ "aria-label": ariaLabel ?? "Dashboard",
676
+ children: visibleCols.map((col, i) => /* @__PURE__ */ jsx(
677
+ DroppableColumn,
678
+ {
679
+ id: COL_IDS[i],
680
+ items: col,
681
+ columnIndex: i,
682
+ handleSelector: dragHandleSelector,
683
+ flatten: layoutMode === 3,
684
+ dragActive: activeId !== null
685
+ },
686
+ COL_IDS[i]
687
+ ))
688
+ }
689
+ ),
690
+ widgets && widgets.length > 0 && /* @__PURE__ */ jsx(
691
+ DashboardCustomizePanel,
692
+ {
693
+ open: panelOpen,
694
+ onOpenChange: setPanelOpen,
695
+ widgets,
696
+ hiddenIds: hiddenList,
697
+ onToggleWidget: handleToggleWidget,
698
+ onReset: handleReset
699
+ }
700
+ )
701
+ ]
702
+ }
703
+ );
704
+ });
705
+
706
+ export { CustomizableDashboard, DashboardCustomizePanel, consolidateColumns, mergeColumnOrder, moveAcrossColumns, reorderOnDrop, requiredColumns };
707
+ //# sourceMappingURL=chunk-F57VRLKP.js.map
708
+ //# sourceMappingURL=chunk-F57VRLKP.js.map