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