@dimaan/ui 0.0.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,1263 @@
1
+ import { createContext, forwardRef, Children, isValidElement, cloneElement, useRef, useImperativeHandle, useLayoutEffect, useContext, useState, useCallback, useMemo, useEffect, useId } from 'react';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
5
+ import { Loader2, Check, Minus, ChevronLeft, Menu, ChevronDown, ChevronsUpDown, ChevronUp, ChevronRight } from 'lucide-react';
6
+
7
+ // src/components/dashboard-layout/context.ts
8
+ var DashboardLayoutContext = createContext(null);
9
+ function useDashboardLayout() {
10
+ const ctx = useContext(DashboardLayoutContext);
11
+ if (!ctx) {
12
+ throw new Error("useDashboardLayout must be used within a <DashboardLayout> component.");
13
+ }
14
+ return ctx;
15
+ }
16
+ function cn(...inputs) {
17
+ return twMerge(clsx(inputs));
18
+ }
19
+ function DashboardContent({ className, children, ...props }) {
20
+ return /* @__PURE__ */ jsx(
21
+ "main",
22
+ {
23
+ className: cn(
24
+ "flex min-h-[calc(100vh-var(--header-height))] flex-1 flex-col gap-6 p-4 sm:p-6 lg:p-8",
25
+ className
26
+ ),
27
+ ...props,
28
+ children
29
+ }
30
+ );
31
+ }
32
+ function DashboardLayout({
33
+ defaultCollapsed = false,
34
+ collapsed: collapsedProp,
35
+ onCollapsedChange,
36
+ className,
37
+ children,
38
+ ...props
39
+ }) {
40
+ const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
41
+ const [mobileOpen, setMobileOpenState] = useState(false);
42
+ const isControlled = collapsedProp !== void 0;
43
+ const collapsed = isControlled ? collapsedProp : internalCollapsed;
44
+ const setCollapsed = useCallback(
45
+ (next) => {
46
+ if (!isControlled) setInternalCollapsed(next);
47
+ onCollapsedChange?.(next);
48
+ },
49
+ [isControlled, onCollapsedChange]
50
+ );
51
+ const toggleCollapsed = useCallback(() => {
52
+ setCollapsed(!collapsed);
53
+ }, [collapsed, setCollapsed]);
54
+ const setMobileOpen = useCallback((next) => {
55
+ setMobileOpenState(next);
56
+ }, []);
57
+ const toggleMobileOpen = useCallback(() => {
58
+ setMobileOpenState((prev) => !prev);
59
+ }, []);
60
+ const value = useMemo(
61
+ () => ({
62
+ collapsed,
63
+ setCollapsed,
64
+ toggleCollapsed,
65
+ mobileOpen,
66
+ setMobileOpen,
67
+ toggleMobileOpen
68
+ }),
69
+ [collapsed, setCollapsed, toggleCollapsed, mobileOpen, setMobileOpen, toggleMobileOpen]
70
+ );
71
+ return /* @__PURE__ */ jsx(DashboardLayoutContext.Provider, { value, children: /* @__PURE__ */ jsx(
72
+ "div",
73
+ {
74
+ "data-collapsed": collapsed ? "true" : "false",
75
+ className: cn(
76
+ "relative flex min-h-screen w-full bg-background text-foreground font-sans antialiased",
77
+ className
78
+ ),
79
+ ...props,
80
+ children
81
+ }
82
+ ) });
83
+ }
84
+ function DashboardMain({ className, children, ...props }) {
85
+ const { collapsed } = useDashboardLayout();
86
+ return /* @__PURE__ */ jsx(
87
+ "div",
88
+ {
89
+ className: cn(
90
+ "flex min-h-screen flex-1 flex-col transition-[margin] duration-200 ease-out",
91
+ // On desktop, push the main column past the fixed sidebar using logical margin.
92
+ collapsed ? "lg:ms-[var(--sidebar-width-collapsed)]" : "lg:ms-[var(--sidebar-width)]",
93
+ className
94
+ ),
95
+ ...props,
96
+ children
97
+ }
98
+ );
99
+ }
100
+ function DashboardHeader({ className, children, ...props }) {
101
+ return /* @__PURE__ */ jsx(
102
+ "header",
103
+ {
104
+ className: cn(
105
+ "sticky top-0 z-20 flex h-[var(--header-height)] shrink-0 items-center gap-3 border-b border-header-border bg-header/80 px-4 text-header-foreground backdrop-blur-md sm:px-6",
106
+ className
107
+ ),
108
+ ...props,
109
+ children
110
+ }
111
+ );
112
+ }
113
+ function HeaderActions({ className, children, ...props }) {
114
+ return /* @__PURE__ */ jsx("div", { className: cn("ms-auto flex items-center gap-1", className), ...props, children });
115
+ }
116
+
117
+ // src/components/button/buttonVariants.ts
118
+ var buttonVariantClass = {
119
+ primary: "bg-primary text-primary-foreground shadow-sm hover:bg-primary/90 focus-visible:ring-primary/40",
120
+ secondary: "bg-muted text-foreground hover:bg-muted/80 focus-visible:ring-muted-foreground/30",
121
+ outline: "border border-input bg-background text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring/40",
122
+ ghost: "bg-transparent text-foreground hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring/40",
123
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90 focus-visible:ring-destructive/40",
124
+ success: "bg-success text-success-foreground shadow-sm hover:bg-success/90 focus-visible:ring-success/40",
125
+ warning: "bg-warning text-warning-foreground shadow-sm hover:bg-warning/90 focus-visible:ring-warning/40",
126
+ link: "text-primary underline-offset-4 hover:underline focus-visible:ring-primary/40 px-0 shadow-none"
127
+ };
128
+ var buttonSizeClass = {
129
+ sm: "h-8 gap-1.5 rounded-md px-3 text-sm",
130
+ md: "h-9 gap-2 rounded-md px-4 text-sm",
131
+ lg: "h-11 gap-2.5 rounded-md px-6 text-base",
132
+ icon: "h-9 w-9 shrink-0 rounded-md p-0",
133
+ "icon-sm": "h-8 w-8 shrink-0 rounded-md p-0"
134
+ };
135
+ var buttonBaseClass = "group/button relative inline-flex items-center justify-center font-medium select-none whitespace-nowrap outline-none transition-[background-color,color,box-shadow,opacity] focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0";
136
+ var Button = forwardRef(function Button2({
137
+ variant = "primary",
138
+ size = "md",
139
+ loading = false,
140
+ loadingText,
141
+ leadingIcon,
142
+ trailingIcon,
143
+ fullWidth = false,
144
+ asChild = false,
145
+ type,
146
+ disabled,
147
+ className,
148
+ children,
149
+ ...props
150
+ }, ref) {
151
+ const isDisabled = disabled || loading;
152
+ const composedClass = cn(
153
+ buttonBaseClass,
154
+ buttonVariantClass[variant],
155
+ buttonSizeClass[size],
156
+ fullWidth && "w-full",
157
+ className
158
+ );
159
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
160
+ loading ? /* @__PURE__ */ jsx(Spinner, {}) : leadingIcon ? /* @__PURE__ */ jsx(Slot, { children: leadingIcon }) : null,
161
+ loading && loadingText !== void 0 ? loadingText : children,
162
+ !loading && trailingIcon ? /* @__PURE__ */ jsx(Slot, { children: trailingIcon }) : null
163
+ ] });
164
+ if (asChild) {
165
+ const child = Children.only(children);
166
+ if (!isValidElement(child)) {
167
+ throw new Error("Button: `asChild` requires a single valid React element as a child.");
168
+ }
169
+ const mergedClassName = cn(composedClass, child.props.className);
170
+ return cloneElement(child, {
171
+ ...child.props,
172
+ className: mergedClassName,
173
+ "aria-disabled": isDisabled ? true : void 0,
174
+ "data-loading": loading ? "true" : void 0,
175
+ children: /* @__PURE__ */ jsxs(Fragment, { children: [
176
+ loading ? /* @__PURE__ */ jsx(Spinner, {}) : leadingIcon ? /* @__PURE__ */ jsx(Slot, { children: leadingIcon }) : null,
177
+ loading && loadingText !== void 0 ? loadingText : child.props.children,
178
+ !loading && trailingIcon ? /* @__PURE__ */ jsx(Slot, { children: trailingIcon }) : null
179
+ ] })
180
+ });
181
+ }
182
+ return /* @__PURE__ */ jsx(
183
+ "button",
184
+ {
185
+ ref,
186
+ type: type ?? "button",
187
+ disabled: isDisabled,
188
+ "data-loading": loading ? "true" : void 0,
189
+ className: composedClass,
190
+ ...props,
191
+ children: content
192
+ }
193
+ );
194
+ });
195
+ function Slot({ children }) {
196
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "inline-flex h-4 w-4 items-center justify-center", children });
197
+ }
198
+ function Spinner() {
199
+ return /* @__PURE__ */ jsx(Loader2, { "aria-hidden": "true", className: "h-4 w-4 animate-spin", "data-testid": "button-spinner" });
200
+ }
201
+ function HeaderCollapseTrigger({
202
+ icon,
203
+ className,
204
+ onClick,
205
+ variant = "ghost",
206
+ size = "icon",
207
+ "aria-label": ariaLabel = "Toggle sidebar",
208
+ ...props
209
+ }) {
210
+ const { toggleCollapsed, collapsed } = useDashboardLayout();
211
+ return /* @__PURE__ */ jsx(
212
+ Button,
213
+ {
214
+ "aria-label": ariaLabel,
215
+ "aria-pressed": collapsed,
216
+ variant,
217
+ size,
218
+ onClick: (e) => {
219
+ toggleCollapsed();
220
+ onClick?.(e);
221
+ },
222
+ className: cn("hidden lg:inline-flex", className),
223
+ ...props,
224
+ children: icon ?? /* @__PURE__ */ jsx(ChevronIcon, { collapsed })
225
+ }
226
+ );
227
+ }
228
+ function ChevronIcon({ collapsed }) {
229
+ return /* @__PURE__ */ jsx(
230
+ ChevronLeft,
231
+ {
232
+ "aria-hidden": "true",
233
+ className: cn(
234
+ "h-[18px] w-[18px] transition-transform duration-200 rtl:-scale-x-100",
235
+ collapsed && "rotate-180"
236
+ )
237
+ }
238
+ );
239
+ }
240
+ function HeaderMobileTrigger({
241
+ icon,
242
+ className,
243
+ onClick,
244
+ variant = "ghost",
245
+ size = "icon",
246
+ "aria-label": ariaLabel = "Toggle navigation",
247
+ ...props
248
+ }) {
249
+ const { toggleMobileOpen, mobileOpen } = useDashboardLayout();
250
+ return /* @__PURE__ */ jsx(
251
+ Button,
252
+ {
253
+ "aria-label": ariaLabel,
254
+ "aria-expanded": mobileOpen,
255
+ variant,
256
+ size,
257
+ onClick: (e) => {
258
+ toggleMobileOpen();
259
+ onClick?.(e);
260
+ },
261
+ className: className ? `lg:hidden ${className}` : "lg:hidden",
262
+ ...props,
263
+ children: icon ?? /* @__PURE__ */ jsx(DefaultMenuIcon, {})
264
+ }
265
+ );
266
+ }
267
+ function DefaultMenuIcon() {
268
+ return /* @__PURE__ */ jsx(Menu, { "aria-hidden": "true", className: "h-[18px] w-[18px]" });
269
+ }
270
+ var HeaderSearch = forwardRef(
271
+ ({ icon, containerClassName, className, type = "search", ...props }, ref) => /* @__PURE__ */ jsxs(
272
+ "div",
273
+ {
274
+ className: cn("relative hidden h-9 w-full max-w-sm items-center md:flex", containerClassName),
275
+ children: [
276
+ icon ? /* @__PURE__ */ jsx(
277
+ "span",
278
+ {
279
+ "aria-hidden": "true",
280
+ className: "pointer-events-none absolute start-3 flex h-4 w-4 items-center justify-center text-muted-foreground",
281
+ children: icon
282
+ }
283
+ ) : null,
284
+ /* @__PURE__ */ jsx(
285
+ "input",
286
+ {
287
+ ref,
288
+ type,
289
+ className: cn(
290
+ "h-9 w-full rounded-md border border-input bg-background text-sm transition-colors",
291
+ "placeholder:text-muted-foreground",
292
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
293
+ icon ? "ps-9 pe-3" : "px-3",
294
+ className
295
+ ),
296
+ ...props
297
+ }
298
+ )
299
+ ]
300
+ }
301
+ )
302
+ );
303
+ HeaderSearch.displayName = "HeaderSearch";
304
+ function HeaderTitle({ className, children, ...props }) {
305
+ return /* @__PURE__ */ jsx(
306
+ "div",
307
+ {
308
+ className: cn("flex min-w-0 flex-1 items-center gap-2 text-base font-semibold", className),
309
+ ...props,
310
+ children
311
+ }
312
+ );
313
+ }
314
+ function Sidebar({ className, children, ...props }) {
315
+ const { collapsed, mobileOpen, setMobileOpen } = useDashboardLayout();
316
+ useEffect(() => {
317
+ if (!mobileOpen) return;
318
+ const onKey = (e) => {
319
+ if (e.key === "Escape") setMobileOpen(false);
320
+ };
321
+ document.addEventListener("keydown", onKey);
322
+ return () => document.removeEventListener("keydown", onKey);
323
+ }, [mobileOpen, setMobileOpen]);
324
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
325
+ /* @__PURE__ */ jsx(
326
+ "div",
327
+ {
328
+ "aria-hidden": "true",
329
+ onClick: () => setMobileOpen(false),
330
+ className: cn(
331
+ "fixed inset-0 z-30 bg-foreground/40 backdrop-blur-sm transition-opacity duration-200 lg:hidden",
332
+ mobileOpen ? "opacity-100" : "pointer-events-none opacity-0"
333
+ )
334
+ }
335
+ ),
336
+ /* @__PURE__ */ jsx(
337
+ "aside",
338
+ {
339
+ "data-collapsed": collapsed ? "true" : "false",
340
+ "data-mobile-open": mobileOpen ? "true" : "false",
341
+ className: cn(
342
+ // Positioning
343
+ "fixed inset-y-0 start-0 z-40 flex flex-col",
344
+ // Surface
345
+ "bg-sidebar text-sidebar-foreground border-e border-sidebar-border",
346
+ // Sizing — width animates between full and collapsed
347
+ collapsed ? "w-[var(--sidebar-width-collapsed)]" : "w-[var(--sidebar-width)]",
348
+ // Motion
349
+ "transition-[transform,width] duration-200 ease-out",
350
+ // Mobile slide: hidden by default, visible when mobileOpen.
351
+ // Logical translate via rtl variant so it slides off the inline-start edge
352
+ // in both LTR and RTL.
353
+ mobileOpen ? "translate-x-0" : "-translate-x-full rtl:translate-x-full lg:translate-x-0 lg:rtl:translate-x-0",
354
+ className
355
+ ),
356
+ ...props,
357
+ children
358
+ }
359
+ )
360
+ ] });
361
+ }
362
+ function SidebarFooter({ className, children, ...props }) {
363
+ return /* @__PURE__ */ jsx(
364
+ "div",
365
+ {
366
+ className: cn(
367
+ "mt-auto flex shrink-0 items-center gap-2 border-t border-sidebar-border p-3",
368
+ className
369
+ ),
370
+ ...props,
371
+ children
372
+ }
373
+ );
374
+ }
375
+ function SidebarGroup({ label, className, children, ...props }) {
376
+ const { collapsed } = useDashboardLayout();
377
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-1 py-2", className), ...props, children: [
378
+ label ? /* @__PURE__ */ jsx(
379
+ "div",
380
+ {
381
+ className: cn(
382
+ "px-3 pb-1 text-xs font-medium uppercase tracking-wider text-muted-foreground transition-opacity",
383
+ collapsed && "pointer-events-none h-0 overflow-hidden opacity-0"
384
+ ),
385
+ children: label
386
+ }
387
+ ) : null,
388
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5", children })
389
+ ] });
390
+ }
391
+ function SidebarHeader({ className, children, ...props }) {
392
+ return /* @__PURE__ */ jsx(
393
+ "div",
394
+ {
395
+ className: cn(
396
+ "flex h-[var(--header-height)] shrink-0 items-center gap-2 border-b border-sidebar-border px-3",
397
+ className
398
+ ),
399
+ ...props,
400
+ children
401
+ }
402
+ );
403
+ }
404
+ function SidebarNav({ className, children, ...props }) {
405
+ return /* @__PURE__ */ jsx("nav", { className: cn("flex flex-1 flex-col gap-1 overflow-y-auto p-2", className), ...props, children });
406
+ }
407
+ function SidebarNavGroup({
408
+ icon,
409
+ label,
410
+ endSlot,
411
+ active = false,
412
+ defaultOpen = false,
413
+ open: openProp,
414
+ onOpenChange,
415
+ className,
416
+ children,
417
+ onClick,
418
+ ...props
419
+ }) {
420
+ const { collapsed } = useDashboardLayout();
421
+ const submenuId = useId();
422
+ const [internalOpen, setInternalOpen] = useState(defaultOpen);
423
+ const isControlled = openProp !== void 0;
424
+ const open = isControlled ? openProp : internalOpen;
425
+ const setOpen = useCallback(
426
+ (next) => {
427
+ if (!isControlled) setInternalOpen(next);
428
+ onOpenChange?.(next);
429
+ },
430
+ [isControlled, onOpenChange]
431
+ );
432
+ useEffect(() => {
433
+ if (collapsed && open) setOpen(false);
434
+ }, [collapsed, open, setOpen]);
435
+ const titleAttr = collapsed && typeof label === "string" ? label : props.title ?? void 0;
436
+ const showChildren = !collapsed;
437
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
438
+ /* @__PURE__ */ jsxs(
439
+ "button",
440
+ {
441
+ type: "button",
442
+ "aria-expanded": showChildren ? open : void 0,
443
+ "aria-controls": showChildren ? submenuId : void 0,
444
+ "data-active": active ? "true" : void 0,
445
+ title: titleAttr,
446
+ onClick: (e) => {
447
+ if (showChildren) setOpen(!open);
448
+ onClick?.(e);
449
+ },
450
+ className: cn(
451
+ "group relative flex h-9 w-full items-center gap-3 rounded-md px-3 text-sm font-medium outline-none transition-colors",
452
+ "text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
453
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-sidebar",
454
+ active && "bg-sidebar-accent text-sidebar-accent-foreground",
455
+ collapsed && "justify-center px-0",
456
+ className
457
+ ),
458
+ ...props,
459
+ children: [
460
+ icon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex h-5 w-5 shrink-0 items-center justify-center", children: icon }) : null,
461
+ /* @__PURE__ */ jsx(
462
+ "span",
463
+ {
464
+ className: cn(
465
+ "flex-1 truncate text-start transition-[opacity,width]",
466
+ collapsed && "pointer-events-none w-0 opacity-0"
467
+ ),
468
+ children: label
469
+ }
470
+ ),
471
+ endSlot && !collapsed ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", children: endSlot }) : null,
472
+ showChildren ? /* @__PURE__ */ jsx(ChevronCaret, { open }) : null
473
+ ]
474
+ }
475
+ ),
476
+ /* @__PURE__ */ jsx(
477
+ "div",
478
+ {
479
+ id: submenuId,
480
+ hidden: !showChildren || !open,
481
+ className: cn(
482
+ "grid transition-[grid-template-rows] duration-200 ease-out",
483
+ showChildren && open ? "grid-rows-[1fr]" : "grid-rows-[0fr]"
484
+ ),
485
+ children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-0.5 ps-7 pt-1", children }) })
486
+ }
487
+ )
488
+ ] });
489
+ }
490
+ function ChevronCaret({ open }) {
491
+ return /* @__PURE__ */ jsx(
492
+ ChevronDown,
493
+ {
494
+ "aria-hidden": "true",
495
+ className: cn(
496
+ "h-3.5 w-3.5 shrink-0 text-muted-foreground transition-transform duration-200",
497
+ open && "rotate-180"
498
+ )
499
+ }
500
+ );
501
+ }
502
+ function SidebarNavItem({
503
+ icon,
504
+ active = false,
505
+ label,
506
+ endSlot,
507
+ className,
508
+ children,
509
+ render,
510
+ ...props
511
+ }) {
512
+ const { collapsed } = useDashboardLayout();
513
+ const labelContent = label ?? children;
514
+ const titleAttr = collapsed && typeof labelContent === "string" ? labelContent : props.title;
515
+ const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
516
+ icon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "flex h-5 w-5 shrink-0 items-center justify-center", children: icon }) : null,
517
+ /* @__PURE__ */ jsx(
518
+ "span",
519
+ {
520
+ className: cn(
521
+ "flex-1 truncate text-start transition-[opacity,width]",
522
+ collapsed && "pointer-events-none w-0 opacity-0"
523
+ ),
524
+ children: labelContent
525
+ }
526
+ ),
527
+ endSlot && !collapsed ? /* @__PURE__ */ jsx("span", { className: "ms-auto flex shrink-0 items-center", children: endSlot }) : null
528
+ ] });
529
+ const computedClass = cn(
530
+ "group relative flex h-9 items-center gap-3 rounded-md px-3 text-sm font-medium outline-none transition-colors",
531
+ "text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
532
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-sidebar",
533
+ active && "bg-sidebar-accent text-sidebar-accent-foreground",
534
+ collapsed && "justify-center px-0",
535
+ className
536
+ );
537
+ if (render) {
538
+ return render({
539
+ className: computedClass,
540
+ children: inner,
541
+ title: titleAttr,
542
+ "aria-current": active ? "page" : void 0,
543
+ "data-active": active ? "true" : void 0
544
+ });
545
+ }
546
+ return /* @__PURE__ */ jsx(
547
+ "a",
548
+ {
549
+ "aria-current": active ? "page" : void 0,
550
+ "data-active": active ? "true" : void 0,
551
+ title: titleAttr,
552
+ className: computedClass,
553
+ ...props,
554
+ children: inner
555
+ }
556
+ );
557
+ }
558
+ function isSection(entry) {
559
+ return "items" in entry && !("label" in entry && "href" in entry) && Array.isArray(entry.items);
560
+ }
561
+ function isGroup(entry) {
562
+ return "items" in entry;
563
+ }
564
+ function normalize(nav) {
565
+ if (nav.length === 0) return [];
566
+ if (isSection(nav[0])) return nav;
567
+ return [{ key: "__root", items: nav }];
568
+ }
569
+ function renderItem(item) {
570
+ if (item.render) {
571
+ return /* @__PURE__ */ jsx(
572
+ SidebarNavItem,
573
+ {
574
+ icon: item.icon,
575
+ active: item.active,
576
+ endSlot: item.endSlot,
577
+ render: item.render,
578
+ children: item.label
579
+ },
580
+ item.key
581
+ );
582
+ }
583
+ return /* @__PURE__ */ jsx(
584
+ SidebarNavItem,
585
+ {
586
+ href: item.href,
587
+ icon: item.icon,
588
+ active: item.active,
589
+ endSlot: item.endSlot,
590
+ children: item.label
591
+ },
592
+ item.key
593
+ );
594
+ }
595
+ function AppShell({
596
+ brand,
597
+ nav,
598
+ title,
599
+ searchPlaceholder,
600
+ onSearch,
601
+ headerActions,
602
+ sidebarFooter,
603
+ defaultCollapsed,
604
+ collapsed,
605
+ onCollapsedChange,
606
+ children
607
+ }) {
608
+ const sections = normalize(nav);
609
+ return /* @__PURE__ */ jsxs(
610
+ DashboardLayout,
611
+ {
612
+ defaultCollapsed,
613
+ collapsed,
614
+ onCollapsedChange,
615
+ children: [
616
+ /* @__PURE__ */ jsxs(Sidebar, { children: [
617
+ (brand?.logo || brand?.name) && /* @__PURE__ */ jsxs(SidebarHeader, { children: [
618
+ brand.logo,
619
+ brand.name ? /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold", children: brand.name }) : null
620
+ ] }),
621
+ /* @__PURE__ */ jsx(SidebarNav, { children: sections.map((section) => /* @__PURE__ */ jsx(SidebarGroup, { label: section.label, children: section.items.map(
622
+ (entry) => isGroup(entry) ? /* @__PURE__ */ jsx(
623
+ SidebarNavGroup,
624
+ {
625
+ label: entry.label,
626
+ icon: entry.icon,
627
+ active: entry.active,
628
+ defaultOpen: entry.defaultOpen ?? entry.items.some((i) => i.active),
629
+ children: entry.items.map(renderItem)
630
+ },
631
+ entry.key
632
+ ) : renderItem(entry)
633
+ ) }, section.key)) }),
634
+ sidebarFooter ? /* @__PURE__ */ jsx(SidebarFooter, { children: sidebarFooter }) : null
635
+ ] }),
636
+ /* @__PURE__ */ jsxs(DashboardMain, { children: [
637
+ /* @__PURE__ */ jsxs(DashboardHeader, { children: [
638
+ /* @__PURE__ */ jsx(HeaderMobileTrigger, {}),
639
+ /* @__PURE__ */ jsx(HeaderCollapseTrigger, {}),
640
+ title ? /* @__PURE__ */ jsx(HeaderTitle, { children: /* @__PURE__ */ jsx("span", { className: "truncate", children: title }) }) : null,
641
+ searchPlaceholder ? /* @__PURE__ */ jsx(
642
+ HeaderSearch,
643
+ {
644
+ placeholder: searchPlaceholder,
645
+ onChange: onSearch ? (e) => onSearch(e.target.value) : void 0
646
+ }
647
+ ) : null,
648
+ headerActions ? /* @__PURE__ */ jsx(HeaderActions, { children: headerActions }) : null
649
+ ] }),
650
+ /* @__PURE__ */ jsx(DashboardContent, { children })
651
+ ] })
652
+ ]
653
+ }
654
+ );
655
+ }
656
+ var sizeClass = {
657
+ sm: "h-7 w-7 text-xs",
658
+ md: "h-9 w-9 text-sm",
659
+ lg: "h-11 w-11 text-base"
660
+ };
661
+ function Avatar({ src, alt = "", fallback, size = "md", className, ...props }) {
662
+ const [errored, setErrored] = useState(false);
663
+ const showImage = Boolean(src) && !errored;
664
+ return /* @__PURE__ */ jsx(
665
+ "span",
666
+ {
667
+ className: cn(
668
+ "inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted text-muted-foreground font-medium select-none",
669
+ sizeClass[size],
670
+ className
671
+ ),
672
+ ...props,
673
+ children: showImage ? /* @__PURE__ */ jsx(
674
+ "img",
675
+ {
676
+ src,
677
+ alt,
678
+ onError: () => setErrored(true),
679
+ className: "h-full w-full object-cover"
680
+ }
681
+ ) : /* @__PURE__ */ jsx("span", { "aria-hidden": !fallback, children: fallback ?? "?" })
682
+ }
683
+ );
684
+ }
685
+ var sizeClass2 = {
686
+ sm: "h-3.5 w-3.5",
687
+ md: "h-4 w-4"
688
+ };
689
+ var Checkbox = forwardRef(function Checkbox2({
690
+ checked,
691
+ defaultChecked,
692
+ indeterminate = false,
693
+ onCheckedChange,
694
+ onChange,
695
+ disabled,
696
+ size = "md",
697
+ className,
698
+ "aria-checked": ariaCheckedProp,
699
+ ...rest
700
+ }, forwardedRef) {
701
+ const inputRef = useRef(null);
702
+ useImperativeHandle(forwardedRef, () => inputRef.current, []);
703
+ useLayoutEffect(() => {
704
+ if (inputRef.current) {
705
+ inputRef.current.indeterminate = indeterminate;
706
+ }
707
+ }, [indeterminate]);
708
+ const ariaChecked = ariaCheckedProp ?? (indeterminate ? "mixed" : void 0);
709
+ return /* @__PURE__ */ jsxs("span", { className: cn("relative inline-flex shrink-0", sizeClass2[size], className), children: [
710
+ /* @__PURE__ */ jsx(
711
+ "input",
712
+ {
713
+ ref: inputRef,
714
+ type: "checkbox",
715
+ checked,
716
+ defaultChecked,
717
+ disabled,
718
+ "aria-checked": ariaChecked,
719
+ onChange: (event) => {
720
+ onChange?.(event);
721
+ onCheckedChange?.(event.currentTarget.checked);
722
+ },
723
+ className: cn(
724
+ "peer absolute inset-0 m-0 cursor-pointer appearance-none rounded-sm border border-input bg-background",
725
+ "transition-colors",
726
+ "checked:border-primary checked:bg-primary",
727
+ "indeterminate:border-primary indeterminate:bg-primary",
728
+ "hover:border-ring",
729
+ "disabled:cursor-not-allowed disabled:opacity-50",
730
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background"
731
+ ),
732
+ ...rest
733
+ }
734
+ ),
735
+ /* @__PURE__ */ jsx(
736
+ Check,
737
+ {
738
+ "aria-hidden": "true",
739
+ strokeWidth: 3,
740
+ className: "pointer-events-none absolute inset-0 m-auto h-3 w-3 text-primary-foreground opacity-0 peer-checked:opacity-100 peer-indeterminate:opacity-0"
741
+ }
742
+ ),
743
+ /* @__PURE__ */ jsx(
744
+ Minus,
745
+ {
746
+ "aria-hidden": "true",
747
+ strokeWidth: 3,
748
+ className: "pointer-events-none absolute inset-0 m-auto h-3 w-3 text-primary-foreground opacity-0 peer-indeterminate:opacity-100"
749
+ }
750
+ )
751
+ ] });
752
+ });
753
+ function readDocumentDirection() {
754
+ if (typeof document === "undefined") return "ltr";
755
+ const dir = document.documentElement.getAttribute("dir");
756
+ return dir === "rtl" ? "rtl" : "ltr";
757
+ }
758
+ function useDirection() {
759
+ const [dir, setDir] = useState(() => readDocumentDirection());
760
+ useEffect(() => {
761
+ setDir(readDocumentDirection());
762
+ const observer = new MutationObserver(() => {
763
+ setDir(readDocumentDirection());
764
+ });
765
+ observer.observe(document.documentElement, {
766
+ attributes: true,
767
+ attributeFilter: ["dir"]
768
+ });
769
+ return () => observer.disconnect();
770
+ }, []);
771
+ return dir;
772
+ }
773
+ function Pagination({
774
+ pageIndex,
775
+ pageSize,
776
+ pageCount,
777
+ totalRowCount,
778
+ pageSizeOptions,
779
+ onChange
780
+ }) {
781
+ const dir = useDirection();
782
+ const isRtl = dir === "rtl";
783
+ const isFirst = pageIndex <= 0;
784
+ const isLast = pageIndex >= pageCount - 1;
785
+ const goPrev = () => {
786
+ if (!isFirst) onChange({ pageIndex: pageIndex - 1, pageSize });
787
+ };
788
+ const goNext = () => {
789
+ if (!isLast) onChange({ pageIndex: pageIndex + 1, pageSize });
790
+ };
791
+ const start = totalRowCount === 0 ? 0 : pageIndex * pageSize + 1;
792
+ const end = Math.min(totalRowCount, (pageIndex + 1) * pageSize);
793
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 text-sm text-muted-foreground", children: [
794
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2", children: [
795
+ /* @__PURE__ */ jsx("span", { children: "Rows per page" }),
796
+ /* @__PURE__ */ jsx(
797
+ "select",
798
+ {
799
+ className: "h-8 rounded-md border border-input bg-background px-2 text-sm text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
800
+ value: pageSize,
801
+ onChange: (event) => {
802
+ const nextSize = Number(event.target.value);
803
+ onChange({ pageIndex: 0, pageSize: nextSize });
804
+ },
805
+ children: pageSizeOptions.map((option) => /* @__PURE__ */ jsx("option", { value: option, children: option }, option))
806
+ }
807
+ )
808
+ ] }) }),
809
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
810
+ /* @__PURE__ */ jsxs("span", { "aria-live": "polite", children: [
811
+ start,
812
+ "\u2013",
813
+ end,
814
+ " of ",
815
+ totalRowCount
816
+ ] }),
817
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
818
+ /* @__PURE__ */ jsx(
819
+ Button,
820
+ {
821
+ type: "button",
822
+ variant: "outline",
823
+ size: "sm",
824
+ disabled: isFirst,
825
+ onClick: goPrev,
826
+ "aria-label": "Previous page",
827
+ children: isRtl ? /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": "true", className: "h-3.5 w-3.5" })
828
+ }
829
+ ),
830
+ /* @__PURE__ */ jsxs("span", { className: "px-1 text-foreground", children: [
831
+ pageIndex + 1,
832
+ " / ",
833
+ pageCount
834
+ ] }),
835
+ /* @__PURE__ */ jsx(
836
+ Button,
837
+ {
838
+ type: "button",
839
+ variant: "outline",
840
+ size: "sm",
841
+ disabled: isLast,
842
+ onClick: goNext,
843
+ "aria-label": "Next page",
844
+ children: isRtl ? /* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": "true", className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true", className: "h-3.5 w-3.5" })
845
+ }
846
+ )
847
+ ] })
848
+ ] })
849
+ ] });
850
+ }
851
+ function Toolbar({ count, onClear, renderLabel, clearLabel, children }) {
852
+ return /* @__PURE__ */ jsxs(
853
+ "div",
854
+ {
855
+ role: "toolbar",
856
+ "aria-label": "Bulk actions",
857
+ className: "flex flex-wrap items-center gap-3 rounded-md border border-border bg-muted/40 px-3 py-2 text-sm",
858
+ children: [
859
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: renderLabel ? renderLabel(count) : `${count} selected` }),
860
+ /* @__PURE__ */ jsxs("div", { className: "ms-auto flex flex-wrap items-center gap-2", children: [
861
+ children,
862
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: onClear, children: clearLabel ?? "Clear" })
863
+ ] })
864
+ ]
865
+ }
866
+ );
867
+ }
868
+
869
+ // src/components/table/tableVariants.ts
870
+ var tableSizeClass = {
871
+ sm: {
872
+ row: "",
873
+ cell: "px-3 py-1.5 text-xs",
874
+ head: "px-3 py-2 text-xs font-medium"
875
+ },
876
+ md: {
877
+ row: "",
878
+ cell: "px-4 py-2.5 text-sm",
879
+ head: "px-4 py-2.5 text-xs font-medium uppercase tracking-wide"
880
+ },
881
+ lg: {
882
+ row: "",
883
+ cell: "px-5 py-3.5 text-sm",
884
+ head: "px-5 py-3 text-sm font-medium"
885
+ }
886
+ };
887
+ var tableBaseClass = "w-full caption-bottom border-collapse";
888
+ var selectedRowClass = "bg-muted/40";
889
+ var sortIconClass = "inline-flex h-3 w-3 shrink-0 items-center justify-center";
890
+ var alignClass = {
891
+ start: "text-start",
892
+ center: "text-center",
893
+ end: "text-end"
894
+ };
895
+ var EMPTY_SELECTION = /* @__PURE__ */ new Set();
896
+ var NO_SORT = { columnId: null, direction: "asc" };
897
+ function useTableState(props) {
898
+ const {
899
+ defaultSort,
900
+ sort: sortProp,
901
+ onSortChange,
902
+ defaultPagination,
903
+ pagination: paginationProp,
904
+ onPaginationChange,
905
+ pageSizeOptions,
906
+ defaultSelectedRowIds,
907
+ selectedRowIds: selectedRowIdsProp,
908
+ onSelectedRowIdsChange,
909
+ totalCount
910
+ } = props;
911
+ const [internalSort, setInternalSort] = useState(defaultSort ?? NO_SORT);
912
+ const isSortControlled = sortProp !== void 0;
913
+ const sort = isSortControlled ? sortProp : internalSort;
914
+ const setSort = useCallback(
915
+ (next) => {
916
+ if (!isSortControlled) setInternalSort(next);
917
+ onSortChange?.(next);
918
+ },
919
+ [isSortControlled, onSortChange]
920
+ );
921
+ const [internalPagination, setInternalPagination] = useState(
922
+ defaultPagination ?? { pageIndex: 0, pageSize: pageSizeOptions?.[0] ?? 10 }
923
+ );
924
+ const isPaginationControlled = paginationProp !== void 0;
925
+ const pagination = isPaginationControlled ? paginationProp : internalPagination;
926
+ const setPagination = useCallback(
927
+ (next) => {
928
+ if (!isPaginationControlled) setInternalPagination(next);
929
+ onPaginationChange?.(next);
930
+ },
931
+ [isPaginationControlled, onPaginationChange]
932
+ );
933
+ const [internalSelected, setInternalSelected] = useState(
934
+ defaultSelectedRowIds ?? EMPTY_SELECTION
935
+ );
936
+ const isSelectionControlled = selectedRowIdsProp !== void 0;
937
+ const selected = isSelectionControlled ? selectedRowIdsProp : internalSelected;
938
+ const setSelected = useCallback(
939
+ (next) => {
940
+ if (!isSelectionControlled) setInternalSelected(next);
941
+ onSelectedRowIdsChange?.(next);
942
+ },
943
+ [isSelectionControlled, onSelectedRowIdsChange]
944
+ );
945
+ return {
946
+ sort,
947
+ setSort,
948
+ pagination,
949
+ setPagination,
950
+ selected,
951
+ setSelected,
952
+ isServerSide: totalCount !== void 0
953
+ };
954
+ }
955
+ var DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50];
956
+ function Table(props) {
957
+ const {
958
+ data,
959
+ columns,
960
+ getRowId,
961
+ enableRowSelection = false,
962
+ isRowSelectable,
963
+ bulkActions,
964
+ renderSelectionLabel,
965
+ clearSelectionLabel,
966
+ loading = false,
967
+ loadingRowCount,
968
+ emptyState,
969
+ size = "md",
970
+ className,
971
+ tableClassName,
972
+ maxHeight,
973
+ striped = false,
974
+ onRowClick,
975
+ tableRef,
976
+ pageSizeOptions = DEFAULT_PAGE_SIZE_OPTIONS,
977
+ showPagination,
978
+ caption
979
+ } = props;
980
+ const ariaLabel = props["aria-label"];
981
+ const ariaLabelledBy = props["aria-labelledby"];
982
+ const { sort, setSort, pagination, setPagination, selected, setSelected, isServerSide } = useTableState(props);
983
+ const sortedRows = useMemo(() => {
984
+ if (isServerSide || sort.columnId === null) return data;
985
+ const col = columns.find((c) => c.id === sort.columnId);
986
+ if (!col) return data;
987
+ const get = resolveSortGetter(col);
988
+ if (!get) return data;
989
+ const tagged = data.map((row, index) => ({ row, index }));
990
+ tagged.sort((a, b) => {
991
+ const cmp = compareValues(get(a.row), get(b.row));
992
+ if (cmp !== 0) return sort.direction === "asc" ? cmp : -cmp;
993
+ return a.index - b.index;
994
+ });
995
+ return tagged.map((entry) => entry.row);
996
+ }, [data, columns, sort, isServerSide]);
997
+ const pagedRows = useMemo(() => {
998
+ if (isServerSide) return sortedRows;
999
+ const start = pagination.pageIndex * pagination.pageSize;
1000
+ return sortedRows.slice(start, start + pagination.pageSize);
1001
+ }, [sortedRows, pagination, isServerSide]);
1002
+ const totalRowCount = isServerSide ? props.totalCount ?? 0 : data.length;
1003
+ const pageCount = Math.max(1, Math.ceil(totalRowCount / pagination.pageSize));
1004
+ const selectableRowIds = useMemo(() => {
1005
+ if (!enableRowSelection) return [];
1006
+ return pagedRows.map((row, index) => ({ row, index })).filter(({ row }) => isRowSelectable ? isRowSelectable(row) : true).map(({ row, index }) => getRowId(row, index));
1007
+ }, [pagedRows, enableRowSelection, isRowSelectable, getRowId]);
1008
+ const selectedOnPageCount = selectableRowIds.reduce(
1009
+ (acc, id) => selected.has(id) ? acc + 1 : acc,
1010
+ 0
1011
+ );
1012
+ const allOnPageSelected = selectableRowIds.length > 0 && selectedOnPageCount === selectableRowIds.length;
1013
+ const someOnPageSelected = selectedOnPageCount > 0 && !allOnPageSelected;
1014
+ const selectedRowsInData = useMemo(() => {
1015
+ if (selected.size === 0) return [];
1016
+ return data.filter((row, index) => selected.has(getRowId(row, index)));
1017
+ }, [data, selected, getRowId]);
1018
+ const toggleHeader = (next) => {
1019
+ const updated = new Set(selected);
1020
+ if (next) {
1021
+ for (const id of selectableRowIds) updated.add(id);
1022
+ } else {
1023
+ for (const id of selectableRowIds) updated.delete(id);
1024
+ }
1025
+ setSelected(updated);
1026
+ };
1027
+ const toggleRow = (id, next) => {
1028
+ const updated = new Set(selected);
1029
+ if (next) updated.add(id);
1030
+ else updated.delete(id);
1031
+ setSelected(updated);
1032
+ };
1033
+ const clearSelection = () => setSelected(/* @__PURE__ */ new Set());
1034
+ const handleSortClick = (columnId) => {
1035
+ setSort(nextSort(sort, columnId));
1036
+ };
1037
+ const totalColumnCount = columns.length + (enableRowSelection ? 1 : 0);
1038
+ const paginationVisible = showPagination ?? totalRowCount > pagination.pageSize;
1039
+ const sizeClasses = tableSizeClass[size];
1040
+ const showToolbar = enableRowSelection && bulkActions !== void 0 && selected.size > 0;
1041
+ const skeletonCount = loadingRowCount ?? pagination.pageSize;
1042
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex w-full flex-col gap-3", className), children: [
1043
+ showToolbar && /* @__PURE__ */ jsx(
1044
+ Toolbar,
1045
+ {
1046
+ count: selected.size,
1047
+ onClear: clearSelection,
1048
+ renderLabel: renderSelectionLabel,
1049
+ clearLabel: clearSelectionLabel,
1050
+ children: bulkActions(selectedRowsInData)
1051
+ }
1052
+ ),
1053
+ /* @__PURE__ */ jsx(
1054
+ "div",
1055
+ {
1056
+ className: cn(
1057
+ "overflow-x-auto rounded-md border border-border bg-background",
1058
+ maxHeight !== void 0 && "overflow-y-auto"
1059
+ ),
1060
+ style: maxHeight !== void 0 ? { maxHeight } : void 0,
1061
+ children: /* @__PURE__ */ jsxs(
1062
+ "table",
1063
+ {
1064
+ ref: tableRef,
1065
+ "aria-label": ariaLabel,
1066
+ "aria-labelledby": ariaLabelledBy,
1067
+ "aria-rowcount": totalRowCount,
1068
+ className: cn(tableBaseClass, "text-sm text-foreground", tableClassName),
1069
+ children: [
1070
+ caption ? /* @__PURE__ */ jsx("caption", { className: "sr-only", children: caption }) : null,
1071
+ /* @__PURE__ */ jsx(
1072
+ "thead",
1073
+ {
1074
+ className: cn(
1075
+ "bg-muted/40 text-muted-foreground",
1076
+ maxHeight !== void 0 && "sticky top-0 z-10"
1077
+ ),
1078
+ children: /* @__PURE__ */ jsxs("tr", { children: [
1079
+ enableRowSelection ? /* @__PURE__ */ jsx("th", { scope: "col", className: cn("w-10", sizeClasses.head), children: /* @__PURE__ */ jsx(
1080
+ Checkbox,
1081
+ {
1082
+ "aria-label": "Select all rows on this page",
1083
+ checked: allOnPageSelected,
1084
+ indeterminate: someOnPageSelected,
1085
+ disabled: selectableRowIds.length === 0,
1086
+ onCheckedChange: toggleHeader
1087
+ }
1088
+ ) }) : null,
1089
+ columns.map((column) => {
1090
+ const isSorted = sort.columnId === column.id;
1091
+ const ariaSort = isSorted ? sort.direction === "asc" ? "ascending" : "descending" : "none";
1092
+ return /* @__PURE__ */ jsx(
1093
+ "th",
1094
+ {
1095
+ scope: "col",
1096
+ "aria-sort": column.sortable ? ariaSort : void 0,
1097
+ className: cn(
1098
+ sizeClasses.head,
1099
+ alignClass[column.align ?? "start"],
1100
+ column.className
1101
+ ),
1102
+ children: column.sortable ? /* @__PURE__ */ jsxs(
1103
+ "button",
1104
+ {
1105
+ type: "button",
1106
+ onClick: () => handleSortClick(column.id),
1107
+ className: "inline-flex items-center gap-1.5 font-inherit uppercase tracking-inherit text-inherit hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
1108
+ "aria-label": sortAriaLabel(column, sort),
1109
+ children: [
1110
+ /* @__PURE__ */ jsx("span", { children: renderHeader(column.header) }),
1111
+ /* @__PURE__ */ jsx(
1112
+ SortIndicator,
1113
+ {
1114
+ active: isSorted,
1115
+ direction: isSorted ? sort.direction : null
1116
+ }
1117
+ )
1118
+ ]
1119
+ }
1120
+ ) : renderHeader(column.header)
1121
+ },
1122
+ column.id
1123
+ );
1124
+ })
1125
+ ] })
1126
+ }
1127
+ ),
1128
+ /* @__PURE__ */ jsx("tbody", { children: loading ? /* @__PURE__ */ jsx(
1129
+ SkeletonRows,
1130
+ {
1131
+ rowCount: skeletonCount,
1132
+ columnCount: totalColumnCount,
1133
+ cellClassName: sizeClasses.cell
1134
+ }
1135
+ ) : pagedRows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1136
+ "td",
1137
+ {
1138
+ colSpan: totalColumnCount,
1139
+ className: cn(sizeClasses.cell, "py-10 text-center text-muted-foreground"),
1140
+ children: emptyState ?? "No data"
1141
+ }
1142
+ ) }) : pagedRows.map((row, rowIndex) => {
1143
+ const id = getRowId(row, rowIndex);
1144
+ const isSelected = selected.has(id);
1145
+ const rowSelectable = isRowSelectable ? isRowSelectable(row) : true;
1146
+ return /* @__PURE__ */ jsxs(
1147
+ "tr",
1148
+ {
1149
+ "data-selected": isSelected ? "true" : void 0,
1150
+ "aria-selected": enableRowSelection ? isSelected : void 0,
1151
+ className: cn(
1152
+ "border-t border-border transition-colors",
1153
+ "hover:bg-accent",
1154
+ striped && rowIndex % 2 === 1 && "bg-muted/20",
1155
+ isSelected && selectedRowClass,
1156
+ onRowClick && "cursor-pointer"
1157
+ ),
1158
+ onClick: onRowClick ? () => onRowClick(row, rowIndex) : void 0,
1159
+ children: [
1160
+ enableRowSelection ? /* @__PURE__ */ jsx("td", { className: cn(sizeClasses.cell, "w-10"), children: /* @__PURE__ */ jsx(
1161
+ Checkbox,
1162
+ {
1163
+ "aria-label": `Select row ${rowIndex + 1}`,
1164
+ checked: isSelected,
1165
+ disabled: !rowSelectable,
1166
+ onCheckedChange: (next) => toggleRow(id, next),
1167
+ onClick: stopRowClickPropagation
1168
+ }
1169
+ ) }) : null,
1170
+ columns.map((column) => /* @__PURE__ */ jsx(
1171
+ "td",
1172
+ {
1173
+ className: cn(
1174
+ sizeClasses.cell,
1175
+ alignClass[column.align ?? "start"],
1176
+ column.className
1177
+ ),
1178
+ children: renderCell(column, row, rowIndex)
1179
+ },
1180
+ column.id
1181
+ ))
1182
+ ]
1183
+ },
1184
+ id
1185
+ );
1186
+ }) })
1187
+ ]
1188
+ }
1189
+ )
1190
+ }
1191
+ ),
1192
+ paginationVisible ? /* @__PURE__ */ jsx(
1193
+ Pagination,
1194
+ {
1195
+ pageIndex: pagination.pageIndex,
1196
+ pageSize: pagination.pageSize,
1197
+ pageCount,
1198
+ totalRowCount,
1199
+ pageSizeOptions,
1200
+ onChange: setPagination
1201
+ }
1202
+ ) : null
1203
+ ] });
1204
+ }
1205
+ function renderHeader(header) {
1206
+ return typeof header === "function" ? header() : header;
1207
+ }
1208
+ function renderCell(column, row, rowIndex) {
1209
+ if (column.render) return column.render(row, rowIndex);
1210
+ if (column.accessor !== void 0) {
1211
+ const value = row[column.accessor];
1212
+ return value === null || value === void 0 ? "" : String(value);
1213
+ }
1214
+ return null;
1215
+ }
1216
+ function resolveSortGetter(column) {
1217
+ if (column.sortAccessor) return column.sortAccessor;
1218
+ if (column.accessor !== void 0) {
1219
+ const key = column.accessor;
1220
+ return (row) => row[key];
1221
+ }
1222
+ return null;
1223
+ }
1224
+ function compareValues(a, b) {
1225
+ if (a === b) return 0;
1226
+ if (a === null || a === void 0) return 1;
1227
+ if (b === null || b === void 0) return -1;
1228
+ if (a instanceof Date && b instanceof Date) return a.getTime() - b.getTime();
1229
+ if (typeof a === "number" && typeof b === "number") return a - b;
1230
+ if (typeof a === "bigint" && typeof b === "bigint") return a < b ? -1 : 1;
1231
+ if (typeof a === "boolean" && typeof b === "boolean") return a === b ? 0 : a ? 1 : -1;
1232
+ return String(a).localeCompare(String(b), void 0, { numeric: true, sensitivity: "base" });
1233
+ }
1234
+ function nextSort(current, columnId) {
1235
+ if (current.columnId !== columnId) return { columnId, direction: "asc" };
1236
+ if (current.direction === "asc") return { columnId, direction: "desc" };
1237
+ return { columnId: null, direction: "asc" };
1238
+ }
1239
+ function sortAriaLabel(column, sort) {
1240
+ const headerText = typeof column.header === "string" ? column.header : column.id;
1241
+ if (sort.columnId !== column.id) return `Sort by ${headerText}`;
1242
+ return sort.direction === "asc" ? `Sort by ${headerText}, currently ascending` : `Sort by ${headerText}, currently descending`;
1243
+ }
1244
+ function stopRowClickPropagation(event) {
1245
+ event.stopPropagation();
1246
+ }
1247
+ function SkeletonRows({ rowCount, columnCount, cellClassName }) {
1248
+ const rowKeys = Array.from({ length: Math.max(0, rowCount) }, (_, i) => `skeleton-row-${i}`);
1249
+ const colKeys = Array.from({ length: Math.max(1, columnCount) }, (_, i) => `skeleton-col-${i}`);
1250
+ return /* @__PURE__ */ jsx(Fragment, { children: rowKeys.map((rowKey) => /* @__PURE__ */ jsx("tr", { className: "border-t border-border", "data-testid": "table-skeleton-row", children: colKeys.map((colKey) => /* @__PURE__ */ jsx("td", { className: cellClassName, children: /* @__PURE__ */ jsx("span", { className: "block h-3 w-full animate-pulse rounded bg-muted" }) }, `${rowKey}-${colKey}`)) }, rowKey)) });
1251
+ }
1252
+ function SortIndicator({ active, direction }) {
1253
+ const className = cn(
1254
+ "h-3.5 w-3.5 shrink-0",
1255
+ active ? "text-foreground" : "text-muted-foreground"
1256
+ );
1257
+ if (!active) return /* @__PURE__ */ jsx(ChevronsUpDown, { "aria-hidden": "true", className });
1258
+ return direction === "asc" ? /* @__PURE__ */ jsx(ChevronUp, { "aria-hidden": "true", className }) : /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": "true", className });
1259
+ }
1260
+
1261
+ export { AppShell, Avatar, Button, Checkbox, DashboardContent, DashboardHeader, DashboardLayout, DashboardMain, HeaderActions, HeaderCollapseTrigger, HeaderMobileTrigger, HeaderSearch, HeaderTitle, Sidebar, SidebarFooter, SidebarGroup, SidebarHeader, SidebarNav, SidebarNavGroup, SidebarNavItem, Table, buttonBaseClass, buttonSizeClass, buttonVariantClass, cn, alignClass as tableAlignClass, tableBaseClass, selectedRowClass as tableSelectedRowClass, tableSizeClass, sortIconClass as tableSortIconClass, useDashboardLayout, useDirection };
1262
+ //# sourceMappingURL=index.js.map
1263
+ //# sourceMappingURL=index.js.map