@classytic/fluid 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -1
- package/dist/client/calendar.d.mts +1 -2
- package/dist/client/calendar.mjs +4 -4
- package/dist/client/color-picker.d.mts +94 -0
- package/dist/client/color-picker.mjs +392 -0
- package/dist/client/core.d.mts +243 -557
- package/dist/client/core.mjs +351 -1462
- package/dist/client/error.d.mts +41 -41
- package/dist/client/error.mjs +35 -35
- package/dist/client/gallery.d.mts +175 -0
- package/dist/client/gallery.mjs +546 -0
- package/dist/client/hooks.d.mts +57 -39
- package/dist/client/hooks.mjs +29 -7
- package/dist/client/spreadsheet.d.mts +30 -27
- package/dist/client/spreadsheet.mjs +80 -80
- package/dist/client/table.d.mts +66 -33
- package/dist/client/table.mjs +87 -54
- package/dist/client/theme.mjs +1 -1
- package/dist/command.d.mts +6 -4
- package/dist/command.mjs +3 -3
- package/dist/compact.d.mts +97 -95
- package/dist/compact.mjs +336 -322
- package/dist/dashboard.d.mts +614 -422
- package/dist/dashboard.mjs +1051 -762
- package/dist/{dropdown-wrapper-B86u9Fri.mjs → dropdown-wrapper-B9nRDUlz.mjs} +25 -35
- package/dist/forms.d.mts +1037 -972
- package/dist/forms.mjs +2849 -2721
- package/dist/index.d.mts +218 -152
- package/dist/index.mjs +357 -264
- package/dist/layouts.d.mts +94 -94
- package/dist/layouts.mjs +115 -110
- package/dist/phone-input-B9_XPNvv.mjs +429 -0
- package/dist/phone-input-CLH_UjQZ.d.mts +31 -0
- package/dist/{search-context-DR7DBs7S.mjs → search-context-1g3ZmOvx.mjs} +1 -1
- package/dist/search.d.mts +168 -164
- package/dist/search.mjs +305 -301
- package/dist/{sheet-wrapper-C13Y-Q6w.mjs → sheet-wrapper-B2uxookb.mjs} +1 -1
- package/dist/timeline-Bgu1mIe9.d.mts +373 -0
- package/dist/timeline-HJtWf4Op.mjs +804 -0
- package/dist/{use-base-search-BGgWnWaF.d.mts → use-base-search-DFC4QKYU.d.mts} +1 -1
- package/dist/{use-media-query-BnVNIKT4.mjs → use-media-query-ChLfFChU.mjs} +6 -7
- package/package.json +10 -2
- /package/dist/{api-pagination-CJ0vR_w6.d.mts → api-pagination-C30ser2L.d.mts} +0 -0
- /package/dist/{filter-utils-DqMmy_v-.mjs → filter-utils-BGIvtq1R.mjs} +0 -0
- /package/dist/{filter-utils-IZ0GtuPo.d.mts → filter-utils-DOFTBWm1.d.mts} +0 -0
- /package/dist/{use-debounce-xmZucz5e.mjs → use-debounce-BNoNiEon.mjs} +0 -0
- /package/dist/{use-keyboard-shortcut-Bl6YM5Q7.mjs → use-keyboard-shortcut-C_Vk-36P.mjs} +0 -0
- /package/dist/{use-keyboard-shortcut-_mRCh3QO.d.mts → use-keyboard-shortcut-Q4CSPzSI.d.mts} +0 -0
- /package/dist/{use-mobile-BX3SQVo2.mjs → use-mobile-CnEmFiQx.mjs} +0 -0
- /package/dist/{use-scroll-detection-CsgsQYvy.mjs → use-scroll-detection-BKfqkmEC.mjs} +0 -0
- /package/dist/{utils-CDue7cEt.d.mts → utils-rqvYP1by.d.mts} +0 -0
package/dist/dashboard.mjs
CHANGED
|
@@ -1,25 +1,186 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { t as cn } from "./utils-DQ5SCVoW.mjs";
|
|
4
|
-
import { t as useIsMobile } from "./use-mobile-
|
|
5
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
-
import { Skeleton } from "@/components/ui/skeleton";
|
|
4
|
+
import { t as useIsMobile } from "./use-mobile-CnEmFiQx.mjs";
|
|
7
5
|
import * as React$1 from "react";
|
|
8
|
-
import React, { Suspense, useState } from "react";
|
|
6
|
+
import React, { Suspense, useEffect, useMemo, useState } from "react";
|
|
7
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
9
8
|
import { Building2, Check, ChevronLeft, ChevronRight, ChevronsUpDown, CircleUserRound, LogOut, Menu } from "lucide-react";
|
|
10
9
|
import { Button } from "@/components/ui/button";
|
|
10
|
+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
11
|
+
import { Badge } from "@/components/ui/badge";
|
|
12
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
13
|
+
import { usePathname } from "next/navigation";
|
|
11
14
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
|
12
15
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
|
|
13
|
-
import { Badge } from "@/components/ui/badge";
|
|
14
|
-
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
15
16
|
import { Sheet, SheetContent, SheetDescription, SheetTitle, SheetTrigger } from "@/components/ui/sheet";
|
|
16
17
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
17
18
|
import { Separator } from "@/components/ui/separator";
|
|
19
|
+
import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarTrigger, useSidebar } from "@/components/ui/sidebar";
|
|
18
20
|
import Link from "next/link";
|
|
19
21
|
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb";
|
|
20
|
-
import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarRail, SidebarTrigger, useSidebar } from "@/components/ui/sidebar";
|
|
21
22
|
|
|
23
|
+
//#region src/components/client-only.tsx
|
|
24
|
+
/**
|
|
25
|
+
* Renders children only on the client after hydration.
|
|
26
|
+
* Use for components with Base UI auto-generated IDs that cause hydration mismatches.
|
|
27
|
+
*/
|
|
28
|
+
function ClientOnly({ children, fallback = null }) {
|
|
29
|
+
const [mounted, setMounted] = useState(false);
|
|
30
|
+
useEffect(() => setMounted(true), []);
|
|
31
|
+
if (!mounted) return fallback;
|
|
32
|
+
return children;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/hooks/use-dashboard-breadcrumbs.ts
|
|
37
|
+
function humanize(slug) {
|
|
38
|
+
return slug.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
39
|
+
}
|
|
40
|
+
function useDashboardBreadcrumbs(basePath, options = {}) {
|
|
41
|
+
const pathname = usePathname();
|
|
42
|
+
const { labelOverrides = {}, hideSegments = [], baseLabel = "Home" } = options;
|
|
43
|
+
return useMemo(() => {
|
|
44
|
+
const normalizedBase = basePath.replace(/\/+$/, "");
|
|
45
|
+
const visibleSegments = (pathname.startsWith(normalizedBase) ? pathname.slice(normalizedBase.length) : pathname).split("/").filter(Boolean).filter((s) => !hideSegments.includes(s));
|
|
46
|
+
const crumbs = [{
|
|
47
|
+
label: baseLabel,
|
|
48
|
+
href: normalizedBase || "/"
|
|
49
|
+
}];
|
|
50
|
+
let cumulativePath = normalizedBase;
|
|
51
|
+
for (let i = 0; i < visibleSegments.length; i++) {
|
|
52
|
+
const segment = visibleSegments[i];
|
|
53
|
+
cumulativePath += `/${segment}`;
|
|
54
|
+
const isLast = i === visibleSegments.length - 1;
|
|
55
|
+
crumbs.push({
|
|
56
|
+
label: labelOverrides[segment] ?? humanize(segment),
|
|
57
|
+
href: isLast ? void 0 : cumulativePath,
|
|
58
|
+
current: isLast
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return crumbs;
|
|
62
|
+
}, [
|
|
63
|
+
pathname,
|
|
64
|
+
basePath,
|
|
65
|
+
labelOverrides,
|
|
66
|
+
hideSegments,
|
|
67
|
+
baseLabel
|
|
68
|
+
]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/dashboard/dashboard-content.tsx
|
|
73
|
+
const PADDING = {
|
|
74
|
+
none: "",
|
|
75
|
+
sm: "px-2",
|
|
76
|
+
md: "px-4",
|
|
77
|
+
lg: "px-6 lg:px-8"
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* DashboardContent — Safe content wrapper for sidebar layouts.
|
|
81
|
+
*
|
|
82
|
+
* Prevents the classic flexbox overflow bug where children with
|
|
83
|
+
* `max-w-*` or grid layouts extend beyond the available space
|
|
84
|
+
* (behind the sidebar) by propagating `min-w-0` through the
|
|
85
|
+
* flex chain.
|
|
86
|
+
*
|
|
87
|
+
* **Important**: `SidebarInset` also needs `min-w-0` for the
|
|
88
|
+
* width constraint to propagate correctly from the flex parent.
|
|
89
|
+
* Pass `className="min-w-0"` to `SidebarInset`.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```tsx
|
|
93
|
+
* <SidebarProvider>
|
|
94
|
+
* <AppSidebar />
|
|
95
|
+
* <SidebarInset className="min-w-0">
|
|
96
|
+
* <DashboardContent>
|
|
97
|
+
* <PageHeader items={breadcrumbs} />
|
|
98
|
+
* <MyPageContent />
|
|
99
|
+
* </DashboardContent>
|
|
100
|
+
* </SidebarInset>
|
|
101
|
+
* </SidebarProvider>
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
function DashboardContent({ children, className, container = true, padding = "md" }) {
|
|
105
|
+
const containerClass = container === true ? "@container/main" : typeof container === "string" ? `@container/${container}` : "";
|
|
106
|
+
return /* @__PURE__ */ jsx("div", {
|
|
107
|
+
className: cn("flex flex-1 flex-col min-w-0", className),
|
|
108
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
109
|
+
className: cn("flex flex-1 flex-col gap-2 min-w-0", containerClass, PADDING[padding]),
|
|
110
|
+
children
|
|
111
|
+
})
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/dashboard/dashboard-header.tsx
|
|
117
|
+
function BreadcrumbNav({ items }) {
|
|
118
|
+
return /* @__PURE__ */ jsx(Breadcrumb, { children: /* @__PURE__ */ jsx(BreadcrumbList, { children: items.map((item, index) => /* @__PURE__ */ jsxs(React$1.Fragment, { children: [/* @__PURE__ */ jsx(BreadcrumbItem, {
|
|
119
|
+
className: item.current ? "" : "hidden md:block",
|
|
120
|
+
children: item.current ? /* @__PURE__ */ jsx(BreadcrumbPage, { children: item.label }) : /* @__PURE__ */ jsx(BreadcrumbLink, {
|
|
121
|
+
render: /* @__PURE__ */ jsx(Link, { href: item.href || "#" }),
|
|
122
|
+
children: item.label
|
|
123
|
+
})
|
|
124
|
+
}), index < items.length - 1 && /* @__PURE__ */ jsx(BreadcrumbSeparator, { className: "hidden md:block" })] }, `breadcrumb-${index}`)) }) });
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* DashboardHeader - Top header bar for dashboard pages.
|
|
128
|
+
* Includes sidebar trigger, breadcrumbs, and action slots.
|
|
129
|
+
*/
|
|
130
|
+
function DashboardHeader({ breadcrumbs, leftContent, rightContent, showSidebarTrigger = true, customTrigger, className, children }) {
|
|
131
|
+
return /* @__PURE__ */ jsxs("header", {
|
|
132
|
+
className: cn("flex h-12 shrink-0 items-center justify-between gap-2 px-4", className),
|
|
133
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
134
|
+
className: "flex items-center gap-2",
|
|
135
|
+
children: [
|
|
136
|
+
showSidebarTrigger && !customTrigger && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(SidebarTrigger, { className: "-ml-1" }), /* @__PURE__ */ jsx(Separator, {
|
|
137
|
+
orientation: "vertical",
|
|
138
|
+
className: "mx-2 !h-4 !self-auto"
|
|
139
|
+
})] }),
|
|
140
|
+
customTrigger,
|
|
141
|
+
children ? children : /* @__PURE__ */ jsxs(Fragment$1, { children: [breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx(BreadcrumbNav, { items: breadcrumbs }), leftContent] })
|
|
142
|
+
]
|
|
143
|
+
}), rightContent && /* @__PURE__ */ jsx("div", {
|
|
144
|
+
className: "flex items-center gap-2",
|
|
145
|
+
children: rightContent
|
|
146
|
+
})]
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
//#endregion
|
|
22
151
|
//#region src/dashboard/nav-utils.ts
|
|
152
|
+
/**
|
|
153
|
+
* matchRoute — Check if a pathname matches a URL.
|
|
154
|
+
* Exact match or prefix match with trailing slash boundary.
|
|
155
|
+
*/
|
|
156
|
+
function matchRoute(pathname, url) {
|
|
157
|
+
return pathname === url || url !== "/" && pathname.startsWith(url + "/");
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* findActiveItem — Find the most specific matching item from a flat list.
|
|
161
|
+
* Returns the item with the longest matching URL (avoids `/settings` matching `/settings/users`).
|
|
162
|
+
*/
|
|
163
|
+
function findActiveItem(items, pathname) {
|
|
164
|
+
let best = null;
|
|
165
|
+
for (const item of items) if (matchRoute(pathname, item.url) && (!best || item.url.length > best.url.length)) best = item;
|
|
166
|
+
return best;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* findActiveCategory — Find the category containing the best-matching item.
|
|
170
|
+
* Returns { category, item } or null.
|
|
171
|
+
*/
|
|
172
|
+
function findActiveCategory(categories, pathname) {
|
|
173
|
+
let best = null;
|
|
174
|
+
for (const category of categories) for (const item of category.items) if (matchRoute(pathname, item.url) && (!best || item.url.length > best.length)) best = {
|
|
175
|
+
category,
|
|
176
|
+
item,
|
|
177
|
+
length: item.url.length
|
|
178
|
+
};
|
|
179
|
+
return best ? {
|
|
180
|
+
category: best.category,
|
|
181
|
+
item: best.item
|
|
182
|
+
} : null;
|
|
183
|
+
}
|
|
23
184
|
/** Check hidden flag, then canAccess(ctx) */
|
|
24
185
|
function isItemVisible(item, ctx) {
|
|
25
186
|
if (item.hidden) return false;
|
|
@@ -109,429 +270,459 @@ function resolveCategories(categories, ctx) {
|
|
|
109
270
|
}
|
|
110
271
|
|
|
111
272
|
//#endregion
|
|
112
|
-
//#region src/dashboard/
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
},
|
|
122
|
-
compact: {
|
|
123
|
-
wrapper: "px-4 py-3 rounded-lg border bg-background/50",
|
|
124
|
-
title: "text-lg font-semibold",
|
|
125
|
-
description: "text-xs text-muted-foreground",
|
|
126
|
-
iconSize: "size-8",
|
|
127
|
-
iconInner: "size-4",
|
|
128
|
-
spacing: "space-y-0.5"
|
|
129
|
-
},
|
|
130
|
-
hero: {
|
|
131
|
-
wrapper: "px-8 py-8 rounded-2xl shadow-lg border bg-gradient-to-br from-background to-muted/20",
|
|
132
|
-
title: "text-3xl font-bold tracking-tight bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text text-transparent",
|
|
133
|
-
description: "text-base text-muted-foreground mt-2",
|
|
134
|
-
iconSize: "size-14",
|
|
135
|
-
iconInner: "size-7",
|
|
136
|
-
spacing: "space-y-2"
|
|
137
|
-
},
|
|
138
|
-
minimal: {
|
|
139
|
-
wrapper: "px-0 py-2",
|
|
140
|
-
title: "text-xl font-medium",
|
|
141
|
-
description: "text-sm text-muted-foreground",
|
|
142
|
-
iconSize: "size-8",
|
|
143
|
-
iconInner: "size-4",
|
|
144
|
-
spacing: "space-y-0.5"
|
|
145
|
-
}
|
|
273
|
+
//#region src/dashboard/presets/dual-sidebar/constants.ts
|
|
274
|
+
const SIDEBAR_RAIL_WIDTH = "3.5rem";
|
|
275
|
+
const SIDEBAR_PANEL_WIDTH = "13rem";
|
|
276
|
+
const SIDEBAR_WIDTH_EXPANDED = "16.5rem";
|
|
277
|
+
const SIDEBAR_WIDTH_COLLAPSED = "3.5rem";
|
|
278
|
+
const SIDEBAR_PADDING = "0.5rem";
|
|
279
|
+
const RAIL_STYLE = {
|
|
280
|
+
width: SIDEBAR_RAIL_WIDTH,
|
|
281
|
+
minWidth: SIDEBAR_RAIL_WIDTH
|
|
146
282
|
};
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
283
|
+
const railVariants$1 = {
|
|
284
|
+
default: "bg-sidebar",
|
|
285
|
+
dark: "bg-zinc-900 dark:bg-zinc-950",
|
|
286
|
+
primary: "bg-primary",
|
|
287
|
+
muted: "bg-muted"
|
|
288
|
+
};
|
|
289
|
+
const railTextVariants$1 = {
|
|
290
|
+
default: "text-sidebar-foreground",
|
|
291
|
+
dark: "text-zinc-100",
|
|
292
|
+
primary: "text-primary-foreground",
|
|
293
|
+
muted: "text-muted-foreground"
|
|
294
|
+
};
|
|
295
|
+
const railBorderVariants$1 = {
|
|
296
|
+
default: "border-sidebar-border",
|
|
297
|
+
dark: "border-zinc-700 dark:border-zinc-800",
|
|
298
|
+
primary: "border-primary/30",
|
|
299
|
+
muted: "border-border"
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region src/dashboard/sidebar-user-menu.tsx
|
|
304
|
+
function getUserInitials$1(name) {
|
|
305
|
+
if (!name) return "?";
|
|
306
|
+
const names = name.trim().split(" ");
|
|
307
|
+
if (names.length === 1) return names[0].slice(0, 2).toUpperCase();
|
|
308
|
+
return (names[0][0] + names[names.length - 1][0]).toUpperCase();
|
|
309
|
+
}
|
|
310
|
+
function UserAvatar({ user, className }) {
|
|
311
|
+
return /* @__PURE__ */ jsxs(Avatar, {
|
|
312
|
+
className: cn("h-8 w-8 rounded-lg", className),
|
|
313
|
+
children: [/* @__PURE__ */ jsx(AvatarImage, {
|
|
314
|
+
src: user.avatar,
|
|
315
|
+
alt: user.name
|
|
316
|
+
}), /* @__PURE__ */ jsx(AvatarFallback, {
|
|
317
|
+
className: "rounded-lg bg-sidebar-primary text-sidebar-primary-foreground",
|
|
318
|
+
children: getUserInitials$1(user.name)
|
|
319
|
+
})]
|
|
167
320
|
});
|
|
321
|
+
}
|
|
322
|
+
function UserInfo({ user }) {
|
|
168
323
|
return /* @__PURE__ */ jsxs("div", {
|
|
169
|
-
className:
|
|
170
|
-
children: [
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
324
|
+
className: "grid flex-1 text-left text-sm leading-tight",
|
|
325
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
326
|
+
className: "truncate font-semibold",
|
|
327
|
+
children: user.name
|
|
328
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
329
|
+
className: "truncate text-xs text-muted-foreground",
|
|
330
|
+
children: user.email
|
|
331
|
+
})]
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* SidebarUserMenu - User avatar and dropdown menu for sidebar footer.
|
|
336
|
+
* Accepts handlers for logout and custom menu items.
|
|
337
|
+
*/
|
|
338
|
+
function SidebarUserMenu({ user, menuItems = [], onLogout, className }) {
|
|
339
|
+
const { isMobile, state } = useSidebar();
|
|
340
|
+
const isCollapsed = state === "collapsed";
|
|
341
|
+
const defaultItems = [{
|
|
342
|
+
label: "Account",
|
|
343
|
+
icon: CircleUserRound,
|
|
344
|
+
href: "/dashboard/profile"
|
|
345
|
+
}];
|
|
346
|
+
const items = menuItems.length > 0 ? menuItems : defaultItems;
|
|
347
|
+
return /* @__PURE__ */ jsx(SidebarMenu, {
|
|
348
|
+
className,
|
|
349
|
+
children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
|
|
350
|
+
render: /* @__PURE__ */ jsx(SidebarMenuButton, {
|
|
351
|
+
size: "lg",
|
|
352
|
+
tooltip: isCollapsed ? user.name : void 0,
|
|
353
|
+
className: cn("data-open:bg-sidebar-accent data-open:text-sidebar-accent-foreground", isCollapsed && !isMobile && "justify-center")
|
|
174
354
|
}),
|
|
175
|
-
/* @__PURE__ */ jsxs("
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
355
|
+
children: [/* @__PURE__ */ jsx(UserAvatar, { user }), (!isCollapsed || isMobile) && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(UserInfo, { user }), /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ml-auto size-4" })] })]
|
|
356
|
+
}), /* @__PURE__ */ jsxs(DropdownMenuContent, {
|
|
357
|
+
className: "w-(--anchor-width) min-w-56 rounded-lg",
|
|
358
|
+
side: isMobile ? "bottom" : "right",
|
|
359
|
+
align: "end",
|
|
360
|
+
sideOffset: 4,
|
|
361
|
+
children: [
|
|
362
|
+
/* @__PURE__ */ jsx(DropdownMenuGroup, { children: /* @__PURE__ */ jsx(DropdownMenuLabel, {
|
|
363
|
+
className: "p-0 font-normal",
|
|
364
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
365
|
+
className: "flex items-center gap-2 px-1 py-1.5 text-left text-sm",
|
|
366
|
+
children: [/* @__PURE__ */ jsx(UserAvatar, { user }), /* @__PURE__ */ jsx(UserInfo, { user })]
|
|
367
|
+
})
|
|
368
|
+
}) }),
|
|
369
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
370
|
+
/* @__PURE__ */ jsx(DropdownMenuGroup, { children: items.map((item, index) => /* @__PURE__ */ jsxs(React$1.Fragment, { children: [item.separator && index > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}), item.href ? /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
371
|
+
render: /* @__PURE__ */ jsx(Link, { href: item.href }),
|
|
372
|
+
className: "w-full flex items-center",
|
|
373
|
+
children: [item.icon && /* @__PURE__ */ jsx(item.icon, { className: "mr-2 h-4 w-4" }), item.label]
|
|
374
|
+
}) : /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
375
|
+
onClick: item.onClick,
|
|
376
|
+
className: "w-full flex items-center",
|
|
377
|
+
children: [item.icon && /* @__PURE__ */ jsx(item.icon, { className: "mr-2 h-4 w-4" }), item.label]
|
|
378
|
+
})] }, item.label)) }),
|
|
379
|
+
onLogout && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
380
|
+
onClick: onLogout,
|
|
381
|
+
className: "w-full flex items-center",
|
|
382
|
+
children: [/* @__PURE__ */ jsx(LogOut, { className: "mr-2 h-4 w-4" }), "Log out"]
|
|
383
|
+
})] })
|
|
384
|
+
]
|
|
385
|
+
})] }) })
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region src/dashboard/presets/dual-sidebar/dual-sidebar-expanded-panel.tsx
|
|
391
|
+
function DualSidebarExpandedPanel({ activeCategoryData, user, isItemActive, variant, isExpanded, onItemClick, headerContent, footerContent }) {
|
|
392
|
+
const isDesktop = variant === "desktop";
|
|
393
|
+
const panelStyles = isDesktop ? {
|
|
394
|
+
className: cn("flex flex-col bg-sidebar overflow-hidden transition-[width,opacity] duration-200 ease-linear", isExpanded ? "opacity-100" : "opacity-0 w-0"),
|
|
395
|
+
style: { width: isExpanded ? SIDEBAR_PANEL_WIDTH : 0 }
|
|
396
|
+
} : {
|
|
397
|
+
className: "flex-1 flex flex-col bg-sidebar overflow-hidden",
|
|
398
|
+
style: void 0
|
|
399
|
+
};
|
|
400
|
+
if (!activeCategoryData) return /* @__PURE__ */ jsx("div", {
|
|
401
|
+
...panelStyles,
|
|
402
|
+
style: panelStyles.style,
|
|
403
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
404
|
+
className: "flex-1 flex items-center justify-center text-sidebar-foreground/60 text-sm",
|
|
405
|
+
children: "Select a category"
|
|
406
|
+
})
|
|
407
|
+
});
|
|
408
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
409
|
+
className: panelStyles.className,
|
|
410
|
+
style: panelStyles.style,
|
|
411
|
+
children: [
|
|
412
|
+
/* @__PURE__ */ jsx(SidebarHeader, {
|
|
413
|
+
className: "h-12 border-b border-sidebar-border px-4 flex items-center shrink-0",
|
|
414
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
415
|
+
className: "flex items-center gap-2",
|
|
416
|
+
children: [/* @__PURE__ */ jsx(activeCategoryData.icon, { className: "h-5 w-5 text-sidebar-foreground" }), /* @__PURE__ */ jsx("span", {
|
|
417
|
+
className: "font-semibold text-sidebar-foreground truncate",
|
|
418
|
+
children: activeCategoryData.name
|
|
208
419
|
})]
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
420
|
+
})
|
|
421
|
+
}),
|
|
422
|
+
headerContent && /* @__PURE__ */ jsx("div", {
|
|
423
|
+
className: "px-3 py-2 border-b border-sidebar-border shrink-0",
|
|
424
|
+
children: headerContent
|
|
425
|
+
}),
|
|
426
|
+
/* @__PURE__ */ jsx(SidebarContent, {
|
|
427
|
+
className: "flex-1 overflow-y-auto",
|
|
428
|
+
children: /* @__PURE__ */ jsxs(SidebarGroup, {
|
|
429
|
+
className: "p-2",
|
|
430
|
+
children: [isDesktop && /* @__PURE__ */ jsxs(SidebarGroupLabel, {
|
|
431
|
+
className: "px-2 text-xs text-sidebar-foreground/60",
|
|
432
|
+
children: [activeCategoryData.name, " Menu"]
|
|
433
|
+
}), /* @__PURE__ */ jsx(SidebarMenu, { children: activeCategoryData.items.map((item, idx) => {
|
|
434
|
+
const Icon = item.icon;
|
|
435
|
+
const keyPrefix = isDesktop ? "" : "mobile-";
|
|
436
|
+
return /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(SidebarMenuButton, {
|
|
437
|
+
isActive: isItemActive(item),
|
|
438
|
+
render: /* @__PURE__ */ jsx(Link, {
|
|
439
|
+
href: item.url,
|
|
440
|
+
onClick: onItemClick
|
|
441
|
+
}),
|
|
221
442
|
children: [
|
|
222
|
-
|
|
223
|
-
/* @__PURE__ */ jsx("span", {
|
|
224
|
-
|
|
443
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
444
|
+
/* @__PURE__ */ jsx("span", {
|
|
445
|
+
className: "truncate",
|
|
446
|
+
children: item.title
|
|
447
|
+
}),
|
|
448
|
+
item.badge && /* @__PURE__ */ jsx("span", {
|
|
449
|
+
className: "ml-auto text-xs bg-sidebar-accent text-sidebar-accent-foreground px-1.5 py-0.5 rounded shrink-0",
|
|
450
|
+
children: item.badge
|
|
451
|
+
})
|
|
225
452
|
]
|
|
226
|
-
},
|
|
227
|
-
})
|
|
228
|
-
})
|
|
453
|
+
}) }, `${keyPrefix}${activeCategoryData.id}-${item.title}-${idx}`);
|
|
454
|
+
}) })]
|
|
455
|
+
})
|
|
229
456
|
}),
|
|
230
|
-
|
|
231
|
-
className:
|
|
232
|
-
children
|
|
457
|
+
footerContent && /* @__PURE__ */ jsx("div", {
|
|
458
|
+
className: "px-3 py-2 border-t border-sidebar-border shrink-0",
|
|
459
|
+
children: footerContent
|
|
460
|
+
}),
|
|
461
|
+
user && /* @__PURE__ */ jsx(SidebarFooter, {
|
|
462
|
+
className: "border-t border-sidebar-border shrink-0",
|
|
463
|
+
children: /* @__PURE__ */ jsx(SidebarUserMenu, {
|
|
464
|
+
user: user.data,
|
|
465
|
+
menuItems: user.menuItems,
|
|
466
|
+
onLogout: user.onLogout
|
|
467
|
+
})
|
|
233
468
|
})
|
|
234
469
|
]
|
|
235
470
|
});
|
|
236
471
|
}
|
|
237
472
|
|
|
238
473
|
//#endregion
|
|
239
|
-
//#region src/dashboard/
|
|
240
|
-
function PageHeader({ items, className, actions }) {
|
|
241
|
-
return /* @__PURE__ */ jsxs("header", {
|
|
242
|
-
className: cn("flex h-12 shrink-0 items-center justify-between gap-2", className),
|
|
243
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
244
|
-
className: "flex items-center gap-2",
|
|
245
|
-
children: [
|
|
246
|
-
/* @__PURE__ */ jsx(SidebarTrigger, { className: "-ml-1" }),
|
|
247
|
-
/* @__PURE__ */ jsx(Separator, {
|
|
248
|
-
orientation: "vertical",
|
|
249
|
-
className: "!self-center h-4"
|
|
250
|
-
}),
|
|
251
|
-
/* @__PURE__ */ jsx(Breadcrumb, { children: /* @__PURE__ */ jsx(BreadcrumbList, { children: items.map((item, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [/* @__PURE__ */ jsx(BreadcrumbItem, {
|
|
252
|
-
className: item.current ? "" : "hidden md:block",
|
|
253
|
-
children: item.current ? /* @__PURE__ */ jsx(BreadcrumbPage, { children: item.label }) : /* @__PURE__ */ jsx(BreadcrumbLink, {
|
|
254
|
-
render: /* @__PURE__ */ jsx(Link, { href: item.href || "#" }),
|
|
255
|
-
children: item.label
|
|
256
|
-
})
|
|
257
|
-
}), index < items.length - 1 && /* @__PURE__ */ jsx(BreadcrumbSeparator, { className: "hidden md:block" })] }, `breadcrumb-${index}`)) }) })
|
|
258
|
-
]
|
|
259
|
-
}), actions]
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
//#endregion
|
|
264
|
-
//#region src/dashboard/dashboard-content.tsx
|
|
265
|
-
const PADDING = {
|
|
266
|
-
none: "",
|
|
267
|
-
sm: "px-2",
|
|
268
|
-
md: "px-4",
|
|
269
|
-
lg: "px-6 lg:px-8"
|
|
270
|
-
};
|
|
474
|
+
//#region src/dashboard/presets/dual-sidebar/rail-tooltip.tsx
|
|
271
475
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
* Prevents the classic flexbox overflow bug where children with
|
|
275
|
-
* `max-w-*` or grid layouts extend beyond the available space
|
|
276
|
-
* (behind the sidebar) by propagating `min-w-0` through the
|
|
277
|
-
* flex chain.
|
|
278
|
-
*
|
|
279
|
-
* **Important**: `SidebarInset` also needs `min-w-0` for the
|
|
280
|
-
* width constraint to propagate correctly from the flex parent.
|
|
281
|
-
* Pass `className="min-w-0"` to `SidebarInset`.
|
|
476
|
+
* RailTooltip — Pure CSS/Tailwind tooltip for sidebar icon rails.
|
|
282
477
|
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* <SidebarInset className="min-w-0">
|
|
288
|
-
* <DashboardContent>
|
|
289
|
-
* <PageHeader items={breadcrumbs} />
|
|
290
|
-
* <MyPageContent />
|
|
291
|
-
* </DashboardContent>
|
|
292
|
-
* </SidebarInset>
|
|
293
|
-
* </SidebarProvider>
|
|
294
|
-
* ```
|
|
478
|
+
* - SSR-safe: no Base UI, no auto-generated IDs, no hydration mismatch
|
|
479
|
+
* - Accessible: aria-label on wrapper for screen readers
|
|
480
|
+
* - Shows on hover with delay, hides on click/active
|
|
481
|
+
* - Styled to match shadcn's tooltip (bg-foreground, text-background, arrow)
|
|
295
482
|
*/
|
|
296
|
-
function
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
children: /* @__PURE__ */
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
483
|
+
function RailTooltip({ label, children, className }) {
|
|
484
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
485
|
+
className: cn("group/tip relative", className),
|
|
486
|
+
"aria-label": label,
|
|
487
|
+
children: [children, /* @__PURE__ */ jsxs("div", {
|
|
488
|
+
role: "tooltip",
|
|
489
|
+
"aria-hidden": true,
|
|
490
|
+
className: cn("pointer-events-none absolute left-full top-1/2 -translate-y-1/2 ml-3 z-50", "rounded-md bg-foreground px-3 py-1.5 text-xs font-medium text-background", "shadow-md whitespace-nowrap", "opacity-0 scale-95 origin-left", "transition-[opacity,transform] duration-100", "group-hover/tip:opacity-100 group-hover/tip:scale-100 group-hover/tip:delay-300", "group-active/tip:opacity-0 group-active/tip:scale-95 group-active/tip:delay-0"),
|
|
491
|
+
children: [label, /* @__PURE__ */ jsx("div", {
|
|
492
|
+
className: "absolute right-full top-1/2 -translate-y-1/2 -mr-[3px]",
|
|
493
|
+
children: /* @__PURE__ */ jsx("div", { className: "size-2 rotate-45 bg-foreground rounded-[1px]" })
|
|
494
|
+
})]
|
|
495
|
+
})]
|
|
304
496
|
});
|
|
305
497
|
}
|
|
306
498
|
|
|
307
499
|
//#endregion
|
|
308
|
-
//#region src/dashboard/
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
500
|
+
//#region src/dashboard/presets/dual-sidebar/dual-sidebar-icon-rail.tsx
|
|
501
|
+
function DualSidebarIconRail({ brand, categories, activeCategory, railVariant, isExpanded, variant, onCategorySelect, onToggleExpanded, onBrandClick }) {
|
|
502
|
+
const isDarkOrPrimary = railVariant === "dark" || railVariant === "primary";
|
|
503
|
+
const brandButtonClasses = cn("flex items-center justify-center h-10 w-10 rounded-lg shadow-sm transition-colors", isDarkOrPrimary ? "bg-white/10 hover:bg-white/20 text-inherit" : "bg-sidebar-primary text-sidebar-primary-foreground");
|
|
504
|
+
const brandElement = /* @__PURE__ */ jsx(Link, {
|
|
505
|
+
href: brand.href || "/",
|
|
506
|
+
className: brandButtonClasses,
|
|
507
|
+
onClick: onBrandClick,
|
|
508
|
+
children: brand.icon
|
|
509
|
+
});
|
|
510
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
511
|
+
className: cn("flex flex-col shrink-0", variant === "desktop" && "transition-colors", railVariants$1[railVariant], railTextVariants$1[railVariant], variant === "mobile" ? cn("border-r", railBorderVariants$1[railVariant]) : isExpanded && cn("border-r", railBorderVariants$1[railVariant])),
|
|
512
|
+
style: RAIL_STYLE,
|
|
513
|
+
children: [
|
|
514
|
+
/* @__PURE__ */ jsx("div", {
|
|
515
|
+
className: "flex items-center justify-center p-2 h-12",
|
|
516
|
+
children: variant === "desktop" ? /* @__PURE__ */ jsx(RailTooltip, {
|
|
517
|
+
label: brand.title,
|
|
518
|
+
children: brandElement
|
|
519
|
+
}) : brandElement
|
|
520
|
+
}),
|
|
521
|
+
/* @__PURE__ */ jsx("div", {
|
|
522
|
+
className: "flex-1 flex flex-col gap-1 p-2",
|
|
523
|
+
children: categories.map((category) => {
|
|
524
|
+
const Icon = category.icon;
|
|
525
|
+
const button = /* @__PURE__ */ jsx("button", {
|
|
526
|
+
type: "button",
|
|
527
|
+
onClick: () => onCategorySelect(category.id),
|
|
528
|
+
className: cn("flex items-center justify-center h-10 w-10 rounded-lg transition-colors", activeCategory === category.id ? isDarkOrPrimary ? "bg-white/20" : "bg-sidebar-accent text-sidebar-accent-foreground" : isDarkOrPrimary ? "hover:bg-white/10" : "hover:bg-sidebar-accent/50"),
|
|
529
|
+
children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5" })
|
|
530
|
+
});
|
|
531
|
+
if (variant === "desktop") return /* @__PURE__ */ jsx(RailTooltip, {
|
|
532
|
+
label: category.name,
|
|
533
|
+
children: button
|
|
534
|
+
}, category.id);
|
|
535
|
+
return /* @__PURE__ */ jsx("div", { children: button }, category.id);
|
|
536
|
+
})
|
|
537
|
+
}),
|
|
538
|
+
variant === "desktop" && onToggleExpanded && /* @__PURE__ */ jsx("div", {
|
|
539
|
+
className: "p-2",
|
|
540
|
+
children: /* @__PURE__ */ jsx(RailTooltip, {
|
|
541
|
+
label: isExpanded ? "Collapse sidebar" : "Expand sidebar",
|
|
542
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
543
|
+
type: "button",
|
|
544
|
+
onClick: onToggleExpanded,
|
|
545
|
+
className: cn("flex items-center justify-center h-10 w-10 rounded-lg transition-colors", isDarkOrPrimary ? "hover:bg-white/10" : "hover:bg-sidebar-accent/50"),
|
|
546
|
+
children: isExpanded ? /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" })
|
|
547
|
+
})
|
|
548
|
+
})
|
|
549
|
+
})
|
|
550
|
+
]
|
|
551
|
+
});
|
|
342
552
|
}
|
|
343
553
|
|
|
344
554
|
//#endregion
|
|
345
|
-
//#region src/dashboard/sidebar-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
size: "lg",
|
|
357
|
-
className: "group-data-[collapsible=icon]:p-0!",
|
|
358
|
-
tooltip: isCollapsed ? tooltip || title : void 0,
|
|
359
|
-
render: /* @__PURE__ */ jsx(Link, {
|
|
360
|
-
href,
|
|
361
|
-
className: cn("flex items-center text-sidebar-foreground", isCollapsed && !isMobile && "justify-center")
|
|
555
|
+
//#region src/dashboard/presets/dual-sidebar/dual-sidebar-mobile.tsx
|
|
556
|
+
function DualSidebarMobile({ brand, categories, user, activeCategory, activeCategoryData, railVariant, ariaLabel, mobileOpen, isControlledMobile, isItemActive, onMobileOpenChange, onCategorySelect, headerContent, footerContent }) {
|
|
557
|
+
return /* @__PURE__ */ jsxs(Sheet, {
|
|
558
|
+
open: mobileOpen,
|
|
559
|
+
onOpenChange: onMobileOpenChange,
|
|
560
|
+
children: [!isControlledMobile && /* @__PURE__ */ jsx(SheetTrigger, {
|
|
561
|
+
render: /* @__PURE__ */ jsx(Button, {
|
|
562
|
+
variant: "outline",
|
|
563
|
+
size: "icon",
|
|
564
|
+
className: "md:hidden fixed top-2 left-2 z-40 h-9 w-9 bg-background/80 backdrop-blur-sm shadow-sm",
|
|
565
|
+
"aria-label": "Open navigation"
|
|
362
566
|
}),
|
|
363
|
-
children:
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
567
|
+
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
568
|
+
}), /* @__PURE__ */ jsxs(SheetContent, {
|
|
569
|
+
side: "left",
|
|
570
|
+
className: "w-72 p-0",
|
|
571
|
+
children: [
|
|
572
|
+
/* @__PURE__ */ jsx(SheetTitle, {
|
|
573
|
+
className: "sr-only",
|
|
574
|
+
children: ariaLabel
|
|
575
|
+
}),
|
|
576
|
+
/* @__PURE__ */ jsx(SheetDescription, {
|
|
577
|
+
className: "sr-only",
|
|
578
|
+
children: "Navigation menu with category icons and expanded content"
|
|
579
|
+
}),
|
|
580
|
+
/* @__PURE__ */ jsxs("div", {
|
|
581
|
+
className: "flex h-full flex-row",
|
|
582
|
+
children: [/* @__PURE__ */ jsx(DualSidebarIconRail, {
|
|
583
|
+
brand,
|
|
584
|
+
categories,
|
|
585
|
+
activeCategory,
|
|
586
|
+
railVariant,
|
|
587
|
+
variant: "mobile",
|
|
588
|
+
onCategorySelect,
|
|
589
|
+
onBrandClick: () => onMobileOpenChange(false)
|
|
590
|
+
}), /* @__PURE__ */ jsx(DualSidebarExpandedPanel, {
|
|
591
|
+
activeCategoryData,
|
|
592
|
+
user,
|
|
593
|
+
isItemActive,
|
|
594
|
+
variant: "mobile",
|
|
595
|
+
onItemClick: () => onMobileOpenChange(false),
|
|
596
|
+
headerContent,
|
|
597
|
+
footerContent
|
|
598
|
+
})]
|
|
599
|
+
})
|
|
600
|
+
]
|
|
601
|
+
})]
|
|
371
602
|
});
|
|
372
603
|
}
|
|
373
604
|
|
|
374
605
|
//#endregion
|
|
375
|
-
//#region src/dashboard/sidebar-
|
|
606
|
+
//#region src/dashboard/presets/dual-sidebar/dual-sidebar.tsx
|
|
376
607
|
/**
|
|
377
|
-
*
|
|
608
|
+
* DualSidebar - A sidebar with icon rail and collapsible expanded panel.
|
|
609
|
+
* Mimics the inset sidebar variant structure for consistent styling.
|
|
378
610
|
*/
|
|
379
|
-
function
|
|
380
|
-
const
|
|
381
|
-
const
|
|
382
|
-
const [
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
611
|
+
function DualSidebar({ brand, categories, user, defaultCategoryId, activeCategoryId: controlledCategoryId, onCategoryChange, defaultCollapsed = false, collapsed: controlledCollapsed, onCollapsedChange, mobileOpen: controlledMobileOpen, onMobileOpenChange, headerContent, footerContent, railVariant = "default", sidebarStyle = "inset", ariaLabel = "Navigation Menu", className }) {
|
|
612
|
+
const pathname = usePathname();
|
|
613
|
+
const [manualOverride, setManualOverride] = React$1.useState(null);
|
|
614
|
+
const [internalExpanded, setInternalExpanded] = React$1.useState(!defaultCollapsed);
|
|
615
|
+
const [internalMobileOpen, setInternalMobileOpen] = React$1.useState(false);
|
|
616
|
+
const { isMobile } = useSidebar();
|
|
617
|
+
const pathnameMatch = React$1.useMemo(() => findActiveCategory(categories, pathname), [pathname, categories]);
|
|
618
|
+
const manualCategory = manualOverride && manualOverride.forPathname === pathname ? manualOverride.id : null;
|
|
619
|
+
const isControlledCategory = controlledCategoryId !== void 0;
|
|
620
|
+
const activeCategory = isControlledCategory ? controlledCategoryId : manualCategory ?? pathnameMatch?.category.id ?? defaultCategoryId ?? (categories.length > 0 ? categories[0].id : null);
|
|
621
|
+
const setActiveCategory = React$1.useCallback((id) => {
|
|
622
|
+
if (isControlledCategory) onCategoryChange?.(id);
|
|
623
|
+
else {
|
|
624
|
+
setManualOverride({
|
|
625
|
+
id,
|
|
626
|
+
forPathname: pathname
|
|
627
|
+
});
|
|
628
|
+
onCategoryChange?.(id);
|
|
629
|
+
}
|
|
630
|
+
}, [
|
|
631
|
+
isControlledCategory,
|
|
632
|
+
onCategoryChange,
|
|
633
|
+
pathname
|
|
634
|
+
]);
|
|
635
|
+
const isItemActive = React$1.useCallback((item) => {
|
|
636
|
+
if (item.isActive !== void 0) return item.isActive;
|
|
637
|
+
return item.url === pathnameMatch?.item.url;
|
|
638
|
+
}, [pathnameMatch]);
|
|
639
|
+
const isControlledCollapsed = controlledCollapsed !== void 0;
|
|
640
|
+
const isExpanded = isControlledCollapsed ? !controlledCollapsed : internalExpanded;
|
|
641
|
+
const setExpanded = React$1.useCallback((expanded) => {
|
|
642
|
+
if (isControlledCollapsed) onCollapsedChange?.(!expanded);
|
|
643
|
+
else {
|
|
644
|
+
setInternalExpanded(expanded);
|
|
645
|
+
onCollapsedChange?.(!expanded);
|
|
646
|
+
}
|
|
647
|
+
}, [isControlledCollapsed, onCollapsedChange]);
|
|
648
|
+
const isControlledMobile = controlledMobileOpen !== void 0;
|
|
649
|
+
const mobileOpen = isControlledMobile ? controlledMobileOpen : internalMobileOpen;
|
|
650
|
+
const setMobileOpen = React$1.useCallback((open) => {
|
|
651
|
+
if (isControlledMobile) onMobileOpenChange?.(open);
|
|
652
|
+
else setInternalMobileOpen(open);
|
|
653
|
+
}, [isControlledMobile, onMobileOpenChange]);
|
|
654
|
+
const handleCategorySelect = (id) => {
|
|
655
|
+
setActiveCategory(id);
|
|
656
|
+
if (!isExpanded) setExpanded(true);
|
|
657
|
+
};
|
|
658
|
+
const handleMobileCategorySelect = (id) => {
|
|
659
|
+
setActiveCategory(id);
|
|
660
|
+
};
|
|
661
|
+
const toggleExpanded = () => {
|
|
662
|
+
setExpanded(!isExpanded);
|
|
663
|
+
};
|
|
664
|
+
const activeCategoryData = categories.find((c) => c.id === activeCategory) || null;
|
|
665
|
+
const isFlush = sidebarStyle === "flush";
|
|
666
|
+
const currentWidth = isExpanded ? SIDEBAR_WIDTH_EXPANDED : SIDEBAR_WIDTH_COLLAPSED;
|
|
667
|
+
const totalWidth = isFlush ? currentWidth : `calc(${currentWidth} + ${SIDEBAR_PADDING} * 2)`;
|
|
668
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DualSidebarMobile, {
|
|
669
|
+
brand,
|
|
670
|
+
categories,
|
|
671
|
+
user,
|
|
672
|
+
activeCategory,
|
|
673
|
+
activeCategoryData,
|
|
674
|
+
railVariant,
|
|
675
|
+
ariaLabel,
|
|
676
|
+
mobileOpen,
|
|
677
|
+
isControlledMobile,
|
|
678
|
+
isItemActive,
|
|
679
|
+
onMobileOpenChange: setMobileOpen,
|
|
680
|
+
onCategorySelect: handleMobileCategorySelect,
|
|
681
|
+
headerContent,
|
|
682
|
+
footerContent
|
|
683
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
684
|
+
className: "group peer text-sidebar-foreground hidden md:block",
|
|
685
|
+
"data-state": "expanded",
|
|
686
|
+
"data-collapsible": "",
|
|
687
|
+
"data-variant": isFlush ? "sidebar" : "inset",
|
|
688
|
+
"data-side": "left",
|
|
689
|
+
"data-slot": "sidebar",
|
|
690
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
691
|
+
"data-slot": "sidebar-gap",
|
|
692
|
+
className: "relative bg-transparent transition-[width] duration-200 ease-linear",
|
|
693
|
+
style: { width: totalWidth }
|
|
694
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
695
|
+
"data-slot": "sidebar-container",
|
|
696
|
+
className: cn("fixed inset-y-0 left-0 z-10 hidden h-svh transition-[width] duration-200 ease-linear md:flex", className),
|
|
697
|
+
style: {
|
|
698
|
+
width: totalWidth,
|
|
699
|
+
...isFlush ? {} : { padding: SIDEBAR_PADDING }
|
|
700
|
+
},
|
|
701
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
702
|
+
"data-sidebar": "sidebar",
|
|
703
|
+
"data-slot": "sidebar-inner",
|
|
704
|
+
className: cn("bg-sidebar flex h-full w-full flex-row overflow-hidden", isFlush ? "border-r border-sidebar-border" : "rounded-lg border border-sidebar-border shadow-sm"),
|
|
705
|
+
children: [/* @__PURE__ */ jsx(DualSidebarIconRail, {
|
|
706
|
+
brand,
|
|
707
|
+
categories,
|
|
708
|
+
activeCategory,
|
|
709
|
+
railVariant,
|
|
710
|
+
isExpanded,
|
|
711
|
+
variant: "desktop",
|
|
712
|
+
onCategorySelect: handleCategorySelect,
|
|
713
|
+
onToggleExpanded: toggleExpanded
|
|
714
|
+
}), /* @__PURE__ */ jsx(DualSidebarExpandedPanel, {
|
|
715
|
+
activeCategoryData,
|
|
716
|
+
user,
|
|
717
|
+
isItemActive,
|
|
718
|
+
variant: "desktop",
|
|
719
|
+
isExpanded,
|
|
720
|
+
headerContent,
|
|
721
|
+
footerContent
|
|
722
|
+
})]
|
|
723
|
+
})
|
|
479
724
|
})]
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* SidebarUserMenu - User avatar and dropdown menu for sidebar footer.
|
|
484
|
-
* Accepts handlers for logout and custom menu items.
|
|
485
|
-
*/
|
|
486
|
-
function SidebarUserMenu({ user, menuItems = [], onLogout, className }) {
|
|
487
|
-
const { isMobile, state } = useSidebar();
|
|
488
|
-
const isCollapsed = state === "collapsed";
|
|
489
|
-
const defaultItems = [{
|
|
490
|
-
label: "Account",
|
|
491
|
-
icon: CircleUserRound,
|
|
492
|
-
href: "/dashboard/profile"
|
|
493
|
-
}];
|
|
494
|
-
const items = menuItems.length > 0 ? menuItems : defaultItems;
|
|
495
|
-
return /* @__PURE__ */ jsx(SidebarMenu, {
|
|
496
|
-
className,
|
|
497
|
-
children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
|
|
498
|
-
render: /* @__PURE__ */ jsx(SidebarMenuButton, {
|
|
499
|
-
size: "lg",
|
|
500
|
-
tooltip: isCollapsed ? user.name : void 0,
|
|
501
|
-
className: cn("data-open:bg-sidebar-accent data-open:text-sidebar-accent-foreground", isCollapsed && !isMobile && "justify-center")
|
|
502
|
-
}),
|
|
503
|
-
children: [/* @__PURE__ */ jsx(UserAvatar, { user }), (!isCollapsed || isMobile) && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(UserInfo, { user }), /* @__PURE__ */ jsx(ChevronsUpDown, { className: "ml-auto size-4" })] })]
|
|
504
|
-
}), /* @__PURE__ */ jsxs(DropdownMenuContent, {
|
|
505
|
-
className: "w-(--anchor-width) min-w-56 rounded-lg",
|
|
506
|
-
side: isMobile ? "bottom" : "right",
|
|
507
|
-
align: "end",
|
|
508
|
-
sideOffset: 4,
|
|
509
|
-
children: [
|
|
510
|
-
/* @__PURE__ */ jsx(DropdownMenuGroup, { children: /* @__PURE__ */ jsx(DropdownMenuLabel, {
|
|
511
|
-
className: "p-0 font-normal",
|
|
512
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
513
|
-
className: "flex items-center gap-2 px-1 py-1.5 text-left text-sm",
|
|
514
|
-
children: [/* @__PURE__ */ jsx(UserAvatar, { user }), /* @__PURE__ */ jsx(UserInfo, { user })]
|
|
515
|
-
})
|
|
516
|
-
}) }),
|
|
517
|
-
/* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
|
|
518
|
-
/* @__PURE__ */ jsx(DropdownMenuGroup, { children: items.map((item, index) => /* @__PURE__ */ jsxs(React$1.Fragment, { children: [item.separator && index > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}), item.href ? /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
519
|
-
render: /* @__PURE__ */ jsx(Link, { href: item.href }),
|
|
520
|
-
className: "w-full flex items-center",
|
|
521
|
-
children: [item.icon && /* @__PURE__ */ jsx(item.icon, { className: "mr-2 h-4 w-4" }), item.label]
|
|
522
|
-
}) : /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
523
|
-
onClick: item.onClick,
|
|
524
|
-
className: "w-full flex items-center",
|
|
525
|
-
children: [item.icon && /* @__PURE__ */ jsx(item.icon, { className: "mr-2 h-4 w-4" }), item.label]
|
|
526
|
-
})] }, item.label)) }),
|
|
527
|
-
onLogout && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
528
|
-
onClick: onLogout,
|
|
529
|
-
className: "w-full flex items-center",
|
|
530
|
-
children: [/* @__PURE__ */ jsx(LogOut, { className: "mr-2 h-4 w-4" }), "Log out"]
|
|
531
|
-
})] })
|
|
532
|
-
]
|
|
533
|
-
})] }) })
|
|
534
|
-
});
|
|
725
|
+
})] });
|
|
535
726
|
}
|
|
536
727
|
|
|
537
728
|
//#endregion
|
|
@@ -572,7 +763,7 @@ function DropdownContent({ items, selected, onSelect, label, renderItem, isMobil
|
|
|
572
763
|
className: "text-xs text-muted-foreground",
|
|
573
764
|
children: item.code
|
|
574
765
|
}),
|
|
575
|
-
item.type && /* @__PURE__ */ jsxs(Fragment, { children: [item.code && /* @__PURE__ */ jsx("span", {
|
|
766
|
+
item.type && /* @__PURE__ */ jsxs(Fragment$1, { children: [item.code && /* @__PURE__ */ jsx("span", {
|
|
576
767
|
className: "text-xs text-muted-foreground",
|
|
577
768
|
children: "•"
|
|
578
769
|
}), /* @__PURE__ */ jsx(Badge, {
|
|
@@ -613,7 +804,7 @@ function SidebarLoadingSkeleton({ isCollapsed, isMobile }) {
|
|
|
613
804
|
}) }) });
|
|
614
805
|
}
|
|
615
806
|
function SidebarProjectDisplay({ project, Icon, isCollapsed, isMobile }) {
|
|
616
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground shrink-0" }), (!isCollapsed || isMobile) && /* @__PURE__ */ jsxs("div", {
|
|
807
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground shrink-0" }), (!isCollapsed || isMobile) && /* @__PURE__ */ jsxs("div", {
|
|
617
808
|
className: "flex-1 min-w-0 leading-tight text-left",
|
|
618
809
|
children: [/* @__PURE__ */ jsx("p", {
|
|
619
810
|
className: "text-xs font-medium truncate",
|
|
@@ -634,7 +825,7 @@ function StandaloneLoadingSkeleton({ size }) {
|
|
|
634
825
|
});
|
|
635
826
|
}
|
|
636
827
|
function StandaloneProjectDisplay({ project, Icon, size }) {
|
|
637
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
828
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
638
829
|
/* @__PURE__ */ jsx(Icon, { className: cn("text-muted-foreground shrink-0", size === "sm" ? "h-3.5 w-3.5" : "h-4 w-4") }),
|
|
639
830
|
/* @__PURE__ */ jsx("span", {
|
|
640
831
|
className: cn("font-medium truncate max-w-[120px]", size === "sm" ? "text-xs" : "text-sm"),
|
|
@@ -755,372 +946,118 @@ function ProjectSwitcher({ items, selected, onSelect, isLoading = false, label =
|
|
|
755
946
|
}
|
|
756
947
|
|
|
757
948
|
//#endregion
|
|
758
|
-
//#region src/dashboard/
|
|
759
|
-
function BreadcrumbNav({ items }) {
|
|
760
|
-
return /* @__PURE__ */ jsx(Breadcrumb, { children: /* @__PURE__ */ jsx(BreadcrumbList, { children: items.map((item, index) => /* @__PURE__ */ jsxs(React$1.Fragment, { children: [/* @__PURE__ */ jsx(BreadcrumbItem, {
|
|
761
|
-
className: item.current ? "" : "hidden md:block",
|
|
762
|
-
children: item.current ? /* @__PURE__ */ jsx(BreadcrumbPage, { children: item.label }) : /* @__PURE__ */ jsx(BreadcrumbLink, {
|
|
763
|
-
render: /* @__PURE__ */ jsx(Link, { href: item.href || "#" }),
|
|
764
|
-
children: item.label
|
|
765
|
-
})
|
|
766
|
-
}), index < items.length - 1 && /* @__PURE__ */ jsx(BreadcrumbSeparator, { className: "hidden md:block" })] }, `breadcrumb-${index}`)) }) });
|
|
767
|
-
}
|
|
768
|
-
/**
|
|
769
|
-
* DashboardHeader - Top header bar for dashboard pages.
|
|
770
|
-
* Includes sidebar trigger, breadcrumbs, and action slots.
|
|
771
|
-
*/
|
|
772
|
-
function DashboardHeader({ breadcrumbs, leftContent, rightContent, showSidebarTrigger = true, customTrigger, className, children }) {
|
|
773
|
-
return /* @__PURE__ */ jsxs("header", {
|
|
774
|
-
className: cn("flex h-12 shrink-0 items-center justify-between gap-2 px-4", className),
|
|
775
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
776
|
-
className: "flex items-center gap-2",
|
|
777
|
-
children: [
|
|
778
|
-
showSidebarTrigger && !customTrigger && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(SidebarTrigger, { className: "-ml-1" }), /* @__PURE__ */ jsx(Separator, {
|
|
779
|
-
orientation: "vertical",
|
|
780
|
-
className: "mx-2 !h-4 !self-auto"
|
|
781
|
-
})] }),
|
|
782
|
-
customTrigger,
|
|
783
|
-
children ? children : /* @__PURE__ */ jsxs(Fragment, { children: [breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsx(BreadcrumbNav, { items: breadcrumbs }), leftContent] })
|
|
784
|
-
]
|
|
785
|
-
}), rightContent && /* @__PURE__ */ jsx("div", {
|
|
786
|
-
className: "flex items-center gap-2",
|
|
787
|
-
children: rightContent
|
|
788
|
-
})]
|
|
789
|
-
});
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
//#endregion
|
|
793
|
-
//#region src/dashboard/presets/inset-sidebar.tsx
|
|
949
|
+
//#region src/dashboard/sidebar-brand.tsx
|
|
794
950
|
/**
|
|
795
|
-
*
|
|
796
|
-
*
|
|
951
|
+
* SidebarBrand - Logo and title section for dashboard sidebar.
|
|
952
|
+
* Automatically handles collapsed state styling.
|
|
797
953
|
*/
|
|
798
|
-
function
|
|
799
|
-
const { isMobile } = useSidebar();
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
collapsible: "icon",
|
|
954
|
+
function SidebarBrand({ title, icon, href = "/", className, tooltip }) {
|
|
955
|
+
const { isMobile, state } = useSidebar();
|
|
956
|
+
const isCollapsed = state === "collapsed";
|
|
957
|
+
return /* @__PURE__ */ jsx(SidebarMenu, {
|
|
803
958
|
className,
|
|
804
|
-
children:
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
className: "
|
|
811
|
-
children: [
|
|
812
|
-
/* @__PURE__ */ jsx(SidebarBrand, {
|
|
813
|
-
title: brand.title,
|
|
814
|
-
icon: brand.icon,
|
|
815
|
-
href: brand.href
|
|
816
|
-
}),
|
|
817
|
-
project && /* @__PURE__ */ jsx(ProjectSwitcher, {
|
|
818
|
-
items: project.items,
|
|
819
|
-
selected: project.selected,
|
|
820
|
-
onSelect: project.onSelect,
|
|
821
|
-
isLoading: project.isLoading,
|
|
822
|
-
label: project.label,
|
|
823
|
-
icon: project.icon
|
|
824
|
-
}),
|
|
825
|
-
headerContent
|
|
826
|
-
]
|
|
959
|
+
children: /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(SidebarMenuButton, {
|
|
960
|
+
size: "lg",
|
|
961
|
+
className: "group-data-[collapsible=icon]:p-0!",
|
|
962
|
+
tooltip: isCollapsed ? tooltip || title : void 0,
|
|
963
|
+
render: /* @__PURE__ */ jsx(Link, {
|
|
964
|
+
href,
|
|
965
|
+
className: cn("flex items-center text-sidebar-foreground", isCollapsed && !isMobile && "justify-center")
|
|
827
966
|
}),
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
})
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
}) })
|
|
837
|
-
]
|
|
967
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
968
|
+
className: cn("rounded-lg flex items-center justify-center bg-sidebar-primary text-sidebar-primary-foreground shadow-sm flex-shrink-0 transition-[width,height]", isCollapsed && !isMobile ? "h-6 w-6" : "h-8 w-8"),
|
|
969
|
+
children: icon
|
|
970
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
971
|
+
className: cn("ml-2 font-semibold tracking-tight truncate", !isMobile && isCollapsed && "hidden"),
|
|
972
|
+
children: title
|
|
973
|
+
})]
|
|
974
|
+
}) })
|
|
838
975
|
});
|
|
839
976
|
}
|
|
840
977
|
|
|
841
978
|
//#endregion
|
|
842
|
-
//#region src/dashboard/
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
const SIDEBAR_PADDING = "0.5rem";
|
|
848
|
-
const railVariants$1 = {
|
|
849
|
-
default: "bg-sidebar",
|
|
850
|
-
dark: "bg-zinc-900 dark:bg-zinc-950",
|
|
851
|
-
primary: "bg-primary",
|
|
852
|
-
muted: "bg-muted"
|
|
853
|
-
};
|
|
854
|
-
const railTextVariants$1 = {
|
|
855
|
-
default: "text-sidebar-foreground",
|
|
856
|
-
dark: "text-zinc-100",
|
|
857
|
-
primary: "text-primary-foreground",
|
|
858
|
-
muted: "text-muted-foreground"
|
|
979
|
+
//#region src/dashboard/sidebar-nav.tsx
|
|
980
|
+
const DENSITY_CLASSES = {
|
|
981
|
+
compact: "h-7 text-xs",
|
|
982
|
+
default: "",
|
|
983
|
+
comfortable: "h-10 text-sm [&_svg]:size-5"
|
|
859
984
|
};
|
|
860
985
|
/**
|
|
861
|
-
*
|
|
862
|
-
* Mimics the inset sidebar variant structure for consistent styling.
|
|
986
|
+
* SidebarNavItem - Single navigation item with optional sub-items.
|
|
863
987
|
*/
|
|
864
|
-
function
|
|
865
|
-
const
|
|
866
|
-
const
|
|
867
|
-
const
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
const handleCategorySelect = (id) => {
|
|
876
|
-
setActiveCategory(id);
|
|
877
|
-
onCategoryChange?.(id);
|
|
878
|
-
if (!isExpanded) setIsExpanded(true);
|
|
879
|
-
};
|
|
880
|
-
const handleMobileCategorySelect = (id) => {
|
|
881
|
-
setActiveCategory(id);
|
|
882
|
-
onCategoryChange?.(id);
|
|
883
|
-
};
|
|
884
|
-
const toggleExpanded = () => {
|
|
885
|
-
setIsExpanded(!isExpanded);
|
|
886
|
-
};
|
|
887
|
-
const activeCategoryData = categories.find((c) => c.id === activeCategory) || null;
|
|
888
|
-
const totalWidth = `calc(${isExpanded ? SIDEBAR_WIDTH_EXPANDED : SIDEBAR_WIDTH_COLLAPSED} + ${SIDEBAR_PADDING} * 2)`;
|
|
889
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Sheet, {
|
|
890
|
-
open: mobileOpen,
|
|
891
|
-
onOpenChange: setMobileOpen,
|
|
892
|
-
children: [!isControlledMobile && /* @__PURE__ */ jsx(SheetTrigger, {
|
|
893
|
-
render: /* @__PURE__ */ jsx(Button, {
|
|
894
|
-
variant: "outline",
|
|
895
|
-
size: "icon",
|
|
896
|
-
className: "md:hidden fixed top-2 left-2 z-40 h-9 w-9 bg-background/80 backdrop-blur-sm shadow-sm",
|
|
897
|
-
"aria-label": "Open navigation"
|
|
898
|
-
}),
|
|
899
|
-
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
900
|
-
}), /* @__PURE__ */ jsxs(SheetContent, {
|
|
901
|
-
side: "left",
|
|
902
|
-
className: "w-72 p-0",
|
|
988
|
+
function SidebarNavItem({ item, density = "default", onClick }) {
|
|
989
|
+
const densityClass = DENSITY_CLASSES[density];
|
|
990
|
+
const hasSubItems = item.items && item.items.length > 0;
|
|
991
|
+
const Icon = item.icon;
|
|
992
|
+
const [open, setOpen] = useState(item.defaultOpen ?? item.isActive ?? false);
|
|
993
|
+
if (hasSubItems) return /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(Collapsible, {
|
|
994
|
+
open,
|
|
995
|
+
onOpenChange: setOpen,
|
|
996
|
+
className: "group/collapsible",
|
|
997
|
+
children: [/* @__PURE__ */ jsxs(CollapsibleTrigger, {
|
|
998
|
+
render: /* @__PURE__ */ jsx(SidebarMenuButton, { className: densityClass }),
|
|
903
999
|
children: [
|
|
904
|
-
/* @__PURE__ */ jsx(
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
className: "sr-only",
|
|
910
|
-
children: "Navigation menu with category icons and expanded content"
|
|
1000
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
1001
|
+
/* @__PURE__ */ jsx("span", { children: item.title }),
|
|
1002
|
+
item.badge && /* @__PURE__ */ jsx("span", {
|
|
1003
|
+
className: "ml-auto text-xs bg-sidebar-accent text-sidebar-accent-foreground px-1.5 py-0.5 rounded",
|
|
1004
|
+
children: item.badge
|
|
911
1005
|
}),
|
|
912
|
-
/* @__PURE__ */
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}, category.id);
|
|
937
|
-
})
|
|
938
|
-
})]
|
|
939
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
940
|
-
className: "flex-1 flex flex-col bg-sidebar overflow-hidden",
|
|
941
|
-
children: activeCategoryData ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
942
|
-
/* @__PURE__ */ jsx(SidebarHeader, {
|
|
943
|
-
className: "h-14 border-b border-sidebar-border px-4 flex items-center shrink-0",
|
|
944
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
945
|
-
className: "flex items-center gap-2",
|
|
946
|
-
children: [/* @__PURE__ */ jsx(activeCategoryData.icon, { className: "h-5 w-5 text-sidebar-foreground" }), /* @__PURE__ */ jsx("span", {
|
|
947
|
-
className: "font-semibold text-sidebar-foreground truncate",
|
|
948
|
-
children: activeCategoryData.name
|
|
949
|
-
})]
|
|
950
|
-
})
|
|
951
|
-
}),
|
|
952
|
-
/* @__PURE__ */ jsx(SidebarContent, {
|
|
953
|
-
className: "flex-1 overflow-y-auto",
|
|
954
|
-
children: /* @__PURE__ */ jsx(SidebarGroup, {
|
|
955
|
-
className: "p-2",
|
|
956
|
-
children: /* @__PURE__ */ jsx(SidebarMenu, { children: activeCategoryData.items.map((item, idx) => {
|
|
957
|
-
const Icon = item.icon;
|
|
958
|
-
return /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(SidebarMenuButton, {
|
|
959
|
-
isActive: item.isActive,
|
|
960
|
-
render: /* @__PURE__ */ jsx(Link, {
|
|
961
|
-
href: item.url,
|
|
962
|
-
onClick: () => setMobileOpen(false)
|
|
963
|
-
}),
|
|
964
|
-
children: [
|
|
965
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
966
|
-
/* @__PURE__ */ jsx("span", {
|
|
967
|
-
className: "truncate",
|
|
968
|
-
children: item.title
|
|
969
|
-
}),
|
|
970
|
-
item.badge && /* @__PURE__ */ jsx("span", {
|
|
971
|
-
className: "ml-auto text-xs bg-sidebar-accent text-sidebar-accent-foreground px-1.5 py-0.5 rounded shrink-0",
|
|
972
|
-
children: item.badge
|
|
973
|
-
})
|
|
974
|
-
]
|
|
975
|
-
}) }, `mobile-${activeCategoryData.id}-${item.title}-${idx}`);
|
|
976
|
-
}) })
|
|
977
|
-
})
|
|
978
|
-
}),
|
|
979
|
-
user && /* @__PURE__ */ jsx(SidebarFooter, {
|
|
980
|
-
className: "border-t border-sidebar-border shrink-0",
|
|
981
|
-
children: /* @__PURE__ */ jsx(SidebarUserMenu, {
|
|
982
|
-
user: user.data,
|
|
983
|
-
menuItems: user.menuItems,
|
|
984
|
-
onLogout: user.onLogout
|
|
985
|
-
})
|
|
986
|
-
})
|
|
987
|
-
] }) : /* @__PURE__ */ jsx("div", {
|
|
988
|
-
className: "flex-1 flex items-center justify-center text-sidebar-foreground/60 text-sm",
|
|
989
|
-
children: "Select a category"
|
|
990
|
-
})
|
|
991
|
-
})]
|
|
1006
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "ml-auto h-4 w-4 transition-transform duration-200 group-data-open/collapsible:rotate-90" })
|
|
1007
|
+
]
|
|
1008
|
+
}), /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx(SidebarMenuSub, { children: item.items.map((subItem, idx) => /* @__PURE__ */ jsx(SidebarMenuSubItem, { children: /* @__PURE__ */ jsxs(SidebarMenuSubButton, {
|
|
1009
|
+
render: /* @__PURE__ */ jsx(Link, { href: subItem.url }),
|
|
1010
|
+
isActive: subItem.isActive,
|
|
1011
|
+
children: [/* @__PURE__ */ jsx("span", { children: subItem.title }), subItem.badge && /* @__PURE__ */ jsx("span", {
|
|
1012
|
+
className: "ml-auto text-xs opacity-60",
|
|
1013
|
+
children: subItem.badge
|
|
1014
|
+
})]
|
|
1015
|
+
}) }, `${item.title}-${subItem.title}-${idx}`)) }) })]
|
|
1016
|
+
}) });
|
|
1017
|
+
return /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsx(Link, {
|
|
1018
|
+
href: item.url,
|
|
1019
|
+
className: "w-full",
|
|
1020
|
+
children: /* @__PURE__ */ jsxs(SidebarMenuButton, {
|
|
1021
|
+
isActive: item.isActive,
|
|
1022
|
+
onClick,
|
|
1023
|
+
className: densityClass,
|
|
1024
|
+
children: [
|
|
1025
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
1026
|
+
/* @__PURE__ */ jsx("span", { children: item.title }),
|
|
1027
|
+
item.badge && /* @__PURE__ */ jsx("span", {
|
|
1028
|
+
className: "ml-auto text-xs bg-sidebar-accent text-sidebar-accent-foreground px-1.5 py-0.5 rounded",
|
|
1029
|
+
children: item.badge
|
|
992
1030
|
})
|
|
993
1031
|
]
|
|
994
|
-
})
|
|
995
|
-
})
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
children: [
|
|
1024
|
-
/* @__PURE__ */ jsx("div", {
|
|
1025
|
-
className: "flex items-center justify-center p-2 h-14",
|
|
1026
|
-
children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
1027
|
-
render: /* @__PURE__ */ jsx(Link, {
|
|
1028
|
-
href: brand.href || "/",
|
|
1029
|
-
className: cn("flex items-center justify-center h-10 w-10 rounded-lg shadow-sm transition-colors", railVariant === "dark" || railVariant === "primary" ? "bg-white/10 hover:bg-white/20 text-inherit" : "bg-sidebar-primary text-sidebar-primary-foreground")
|
|
1030
|
-
}),
|
|
1031
|
-
children: brand.icon
|
|
1032
|
-
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
1033
|
-
side: "right",
|
|
1034
|
-
children: brand.title
|
|
1035
|
-
})] })
|
|
1036
|
-
}),
|
|
1037
|
-
/* @__PURE__ */ jsx("div", {
|
|
1038
|
-
className: "flex-1 flex flex-col gap-1 p-2",
|
|
1039
|
-
children: categories.map((category) => {
|
|
1040
|
-
const Icon = category.icon;
|
|
1041
|
-
return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
1042
|
-
render: /* @__PURE__ */ jsx("button", {
|
|
1043
|
-
onClick: () => handleCategorySelect(category.id),
|
|
1044
|
-
className: cn("flex items-center justify-center h-10 w-10 rounded-lg transition-colors", activeCategory === category.id ? railVariant === "dark" || railVariant === "primary" ? "bg-white/20" : "bg-sidebar-accent text-sidebar-accent-foreground" : railVariant === "dark" || railVariant === "primary" ? "hover:bg-white/10" : "hover:bg-sidebar-accent/50")
|
|
1045
|
-
}),
|
|
1046
|
-
children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5" })
|
|
1047
|
-
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
1048
|
-
side: "right",
|
|
1049
|
-
children: category.name
|
|
1050
|
-
})] }, category.id);
|
|
1051
|
-
})
|
|
1052
|
-
}),
|
|
1053
|
-
/* @__PURE__ */ jsx("div", {
|
|
1054
|
-
className: "p-2",
|
|
1055
|
-
children: /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
1056
|
-
render: /* @__PURE__ */ jsx("button", {
|
|
1057
|
-
onClick: toggleExpanded,
|
|
1058
|
-
className: cn("flex items-center justify-center h-10 w-10 rounded-lg transition-colors", railVariant === "dark" || railVariant === "primary" ? "hover:bg-white/10" : "hover:bg-sidebar-accent/50")
|
|
1059
|
-
}),
|
|
1060
|
-
children: isExpanded ? /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" })
|
|
1061
|
-
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
1062
|
-
side: "right",
|
|
1063
|
-
children: isExpanded ? "Collapse sidebar" : "Expand sidebar"
|
|
1064
|
-
})] })
|
|
1065
|
-
})
|
|
1066
|
-
]
|
|
1067
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
1068
|
-
className: cn("flex flex-col bg-sidebar overflow-hidden transition-[width,opacity] duration-200 ease-linear", isExpanded ? "opacity-100" : "opacity-0 w-0"),
|
|
1069
|
-
style: { width: isExpanded ? SIDEBAR_PANEL_WIDTH : 0 },
|
|
1070
|
-
children: activeCategoryData ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1071
|
-
/* @__PURE__ */ jsx(SidebarHeader, {
|
|
1072
|
-
className: "h-14 border-b border-sidebar-border px-4 flex items-center shrink-0",
|
|
1073
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
1074
|
-
className: "flex items-center gap-2",
|
|
1075
|
-
children: [/* @__PURE__ */ jsx(activeCategoryData.icon, { className: "h-5 w-5 text-sidebar-foreground" }), /* @__PURE__ */ jsx("span", {
|
|
1076
|
-
className: "font-semibold text-sidebar-foreground truncate",
|
|
1077
|
-
children: activeCategoryData.name
|
|
1078
|
-
})]
|
|
1079
|
-
})
|
|
1080
|
-
}),
|
|
1081
|
-
/* @__PURE__ */ jsx(SidebarContent, {
|
|
1082
|
-
className: "flex-1 overflow-y-auto",
|
|
1083
|
-
children: /* @__PURE__ */ jsxs(SidebarGroup, {
|
|
1084
|
-
className: "p-2",
|
|
1085
|
-
children: [/* @__PURE__ */ jsxs(SidebarGroupLabel, {
|
|
1086
|
-
className: "px-2 text-xs text-sidebar-foreground/60",
|
|
1087
|
-
children: [activeCategoryData.name, " Menu"]
|
|
1088
|
-
}), /* @__PURE__ */ jsx(SidebarMenu, { children: activeCategoryData.items.map((item, idx) => {
|
|
1089
|
-
const Icon = item.icon;
|
|
1090
|
-
return /* @__PURE__ */ jsx(SidebarMenuItem, { children: /* @__PURE__ */ jsxs(SidebarMenuButton, {
|
|
1091
|
-
isActive: item.isActive,
|
|
1092
|
-
render: /* @__PURE__ */ jsx(Link, { href: item.url }),
|
|
1093
|
-
children: [
|
|
1094
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
1095
|
-
/* @__PURE__ */ jsx("span", {
|
|
1096
|
-
className: "truncate",
|
|
1097
|
-
children: item.title
|
|
1098
|
-
}),
|
|
1099
|
-
item.badge && /* @__PURE__ */ jsx("span", {
|
|
1100
|
-
className: "ml-auto text-xs bg-sidebar-accent text-sidebar-accent-foreground px-1.5 py-0.5 rounded shrink-0",
|
|
1101
|
-
children: item.badge
|
|
1102
|
-
})
|
|
1103
|
-
]
|
|
1104
|
-
}) }, `${activeCategoryData.id}-${item.title}-${idx}`);
|
|
1105
|
-
}) })]
|
|
1106
|
-
})
|
|
1107
|
-
}),
|
|
1108
|
-
user && /* @__PURE__ */ jsx(SidebarFooter, {
|
|
1109
|
-
className: "border-t border-sidebar-border shrink-0",
|
|
1110
|
-
children: /* @__PURE__ */ jsx(SidebarUserMenu, {
|
|
1111
|
-
user: user.data,
|
|
1112
|
-
menuItems: user.menuItems,
|
|
1113
|
-
onLogout: user.onLogout
|
|
1114
|
-
})
|
|
1115
|
-
})
|
|
1116
|
-
] }) : /* @__PURE__ */ jsx("div", {
|
|
1117
|
-
className: "flex-1 flex items-center justify-center text-sidebar-foreground/60 text-sm",
|
|
1118
|
-
children: "Select a category"
|
|
1119
|
-
})
|
|
1120
|
-
})]
|
|
1121
|
-
})
|
|
1122
|
-
})]
|
|
1123
|
-
})] });
|
|
1032
|
+
})
|
|
1033
|
+
}) });
|
|
1034
|
+
}
|
|
1035
|
+
/**
|
|
1036
|
+
* SidebarNavGroup - A group of navigation items with optional title.
|
|
1037
|
+
*/
|
|
1038
|
+
function SidebarNavGroup({ title, items, density, className, onItemClick }) {
|
|
1039
|
+
return /* @__PURE__ */ jsxs(SidebarGroup, {
|
|
1040
|
+
className,
|
|
1041
|
+
children: [title && /* @__PURE__ */ jsx(SidebarGroupLabel, { children: title }), /* @__PURE__ */ jsx(SidebarMenu, { children: items.map((item, idx) => /* @__PURE__ */ jsx(SidebarNavItem, {
|
|
1042
|
+
item,
|
|
1043
|
+
density,
|
|
1044
|
+
onClick: () => onItemClick?.(item)
|
|
1045
|
+
}, `${item.title}-${idx}`)) })]
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* SidebarNav - Complete navigation section with multiple groups.
|
|
1050
|
+
*/
|
|
1051
|
+
function SidebarNav({ groups, density, className, onItemClick }) {
|
|
1052
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1053
|
+
className: cn("flex flex-col gap-2", className),
|
|
1054
|
+
children: groups.map((group, index) => /* @__PURE__ */ jsx(SidebarNavGroup, {
|
|
1055
|
+
title: group.title,
|
|
1056
|
+
items: group.items,
|
|
1057
|
+
density,
|
|
1058
|
+
onItemClick
|
|
1059
|
+
}, group.title || `nav-group-${index}`))
|
|
1060
|
+
});
|
|
1124
1061
|
}
|
|
1125
1062
|
|
|
1126
1063
|
//#endregion
|
|
@@ -1161,8 +1098,62 @@ function FloatingSidebar({ brand, navigation, secondaryNavigation, project, user
|
|
|
1161
1098
|
headerContent
|
|
1162
1099
|
]
|
|
1163
1100
|
}),
|
|
1164
|
-
/* @__PURE__ */ jsx(SidebarContent, { children: children || /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(SidebarNav, { groups: navigation }), secondaryNavigation && /* @__PURE__ */ jsx(SidebarNav, {
|
|
1101
|
+
/* @__PURE__ */ jsx(SidebarContent, { children: children || /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(SidebarNav, { groups: navigation }), secondaryNavigation && /* @__PURE__ */ jsx(SidebarNav, {
|
|
1102
|
+
groups: secondaryNavigation,
|
|
1103
|
+
className: "mt-auto"
|
|
1104
|
+
})] }) }),
|
|
1105
|
+
user && /* @__PURE__ */ jsx(SidebarFooter, { children: /* @__PURE__ */ jsx(SidebarUserMenu, {
|
|
1106
|
+
user: user.data,
|
|
1107
|
+
menuItems: user.menuItems,
|
|
1108
|
+
onLogout: user.onLogout
|
|
1109
|
+
}) })
|
|
1110
|
+
]
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
//#endregion
|
|
1115
|
+
//#region src/dashboard/presets/inset-sidebar.tsx
|
|
1116
|
+
/**
|
|
1117
|
+
* InsetSidebar - Complete sidebar preset with inset variant.
|
|
1118
|
+
* The standard dashboard sidebar with collapsible to icons.
|
|
1119
|
+
*/
|
|
1120
|
+
function InsetSidebar({ brand, navigation, secondaryNavigation, project, user, headerContent, sidebarStyle = "inset", density = "default", side = "left", ariaLabel = "Navigation Menu", className, children }) {
|
|
1121
|
+
const { isMobile } = useSidebar();
|
|
1122
|
+
return /* @__PURE__ */ jsxs(Sidebar, {
|
|
1123
|
+
variant: sidebarStyle === "flush" ? "sidebar" : "inset",
|
|
1124
|
+
collapsible: "icon",
|
|
1125
|
+
side,
|
|
1126
|
+
className,
|
|
1127
|
+
children: [
|
|
1128
|
+
isMobile && /* @__PURE__ */ jsxs("span", {
|
|
1129
|
+
className: "sr-only",
|
|
1130
|
+
children: [/* @__PURE__ */ jsx(SheetTitle, { children: ariaLabel }), /* @__PURE__ */ jsx(SheetDescription, { children: "Application navigation menu containing links to different sections" })]
|
|
1131
|
+
}),
|
|
1132
|
+
/* @__PURE__ */ jsxs(SidebarHeader, {
|
|
1133
|
+
className: "gap-1",
|
|
1134
|
+
children: [
|
|
1135
|
+
/* @__PURE__ */ jsx(SidebarBrand, {
|
|
1136
|
+
title: brand.title,
|
|
1137
|
+
icon: brand.icon,
|
|
1138
|
+
href: brand.href
|
|
1139
|
+
}),
|
|
1140
|
+
project && /* @__PURE__ */ jsx(ProjectSwitcher, {
|
|
1141
|
+
items: project.items,
|
|
1142
|
+
selected: project.selected,
|
|
1143
|
+
onSelect: project.onSelect,
|
|
1144
|
+
isLoading: project.isLoading,
|
|
1145
|
+
label: project.label,
|
|
1146
|
+
icon: project.icon
|
|
1147
|
+
}),
|
|
1148
|
+
headerContent
|
|
1149
|
+
]
|
|
1150
|
+
}),
|
|
1151
|
+
/* @__PURE__ */ jsx(SidebarContent, { children: children || /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(SidebarNav, {
|
|
1152
|
+
groups: navigation,
|
|
1153
|
+
density
|
|
1154
|
+
}), secondaryNavigation && /* @__PURE__ */ jsx(SidebarNav, {
|
|
1165
1155
|
groups: secondaryNavigation,
|
|
1156
|
+
density,
|
|
1166
1157
|
className: "mt-auto"
|
|
1167
1158
|
})] }) }),
|
|
1168
1159
|
user && /* @__PURE__ */ jsx(SidebarFooter, { children: /* @__PURE__ */ jsx(SidebarUserMenu, {
|
|
@@ -1204,7 +1195,7 @@ function MiniSidebar({ brand, navigation, secondaryNavigation, user, headerConte
|
|
|
1204
1195
|
children: brand.title
|
|
1205
1196
|
})]
|
|
1206
1197
|
}) }) }), headerContent] }),
|
|
1207
|
-
/* @__PURE__ */ jsx(SidebarContent, { children: children || /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(NavGroups, { groups: navigation }), secondaryNavigation && /* @__PURE__ */ jsx(NavGroups, {
|
|
1198
|
+
/* @__PURE__ */ jsx(SidebarContent, { children: children || /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(NavGroups, { groups: navigation }), secondaryNavigation && /* @__PURE__ */ jsx(NavGroups, {
|
|
1208
1199
|
groups: secondaryNavigation,
|
|
1209
1200
|
className: "mt-auto"
|
|
1210
1201
|
})] }) }),
|
|
@@ -1218,7 +1209,7 @@ function MiniSidebar({ brand, navigation, secondaryNavigation, user, headerConte
|
|
|
1218
1209
|
});
|
|
1219
1210
|
}
|
|
1220
1211
|
function NavGroups({ groups, className }) {
|
|
1221
|
-
return /* @__PURE__ */ jsx(Fragment, { children: groups.map((group, groupIndex) => /* @__PURE__ */ jsxs(SidebarGroup, {
|
|
1212
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: groups.map((group, groupIndex) => /* @__PURE__ */ jsxs(SidebarGroup, {
|
|
1222
1213
|
className: cn(groupIndex === groups.length - 1 && className),
|
|
1223
1214
|
children: [group.title && /* @__PURE__ */ jsx(SidebarGroupLabel, { children: group.title }), /* @__PURE__ */ jsx(SidebarMenu, { children: group.items.map((item, itemIndex) => {
|
|
1224
1215
|
const Icon = item.icon;
|
|
@@ -1259,6 +1250,12 @@ const railTextVariants = {
|
|
|
1259
1250
|
primary: "text-primary-foreground",
|
|
1260
1251
|
muted: "text-muted-foreground"
|
|
1261
1252
|
};
|
|
1253
|
+
const railBorderVariants = {
|
|
1254
|
+
default: "border-sidebar-border",
|
|
1255
|
+
dark: "border-zinc-700 dark:border-zinc-800",
|
|
1256
|
+
primary: "border-primary/30",
|
|
1257
|
+
muted: "border-border"
|
|
1258
|
+
};
|
|
1262
1259
|
function getUserInitials(name) {
|
|
1263
1260
|
if (!name) return "?";
|
|
1264
1261
|
const names = name.trim().split(" ");
|
|
@@ -1331,7 +1328,7 @@ function TopbarUserMenu({ user, menuItems = [], onLogout }) {
|
|
|
1331
1328
|
className: "w-full flex items-center",
|
|
1332
1329
|
children: [item.icon && /* @__PURE__ */ jsx(item.icon, { className: "mr-2 h-4 w-4" }), item.label]
|
|
1333
1330
|
})] }, item.label)) }),
|
|
1334
|
-
onLogout && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
1331
|
+
onLogout && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DropdownMenuSeparator, {}), /* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
1335
1332
|
onClick: onLogout,
|
|
1336
1333
|
className: "w-full flex items-center",
|
|
1337
1334
|
children: "Log out"
|
|
@@ -1423,7 +1420,7 @@ function TopbarRail({ brand, categories, user, breadcrumbs, actions, defaultCate
|
|
|
1423
1420
|
children: brand.icon
|
|
1424
1421
|
})
|
|
1425
1422
|
}),
|
|
1426
|
-
breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Separator, {
|
|
1423
|
+
breadcrumbs && breadcrumbs.length > 0 && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Separator, {
|
|
1427
1424
|
orientation: "vertical",
|
|
1428
1425
|
className: "mx-2 !h-4 !self-auto hidden md:block"
|
|
1429
1426
|
}), /* @__PURE__ */ jsx(TopbarBreadcrumbs, { items: breadcrumbs })] }),
|
|
@@ -1442,7 +1439,7 @@ function TopbarRail({ brand, categories, user, breadcrumbs, actions, defaultCate
|
|
|
1442
1439
|
className: "flex flex-1 overflow-hidden",
|
|
1443
1440
|
children: [
|
|
1444
1441
|
/* @__PURE__ */ jsx("nav", {
|
|
1445
|
-
className: cn("hidden md:flex flex-col shrink-0 border-r", railVariants[railVariant], railTextVariants[railVariant]),
|
|
1442
|
+
className: cn("hidden md:flex flex-col shrink-0 border-r", railVariants[railVariant], railTextVariants[railVariant], railBorderVariants[railVariant]),
|
|
1446
1443
|
style: {
|
|
1447
1444
|
width: RAIL_WIDTH,
|
|
1448
1445
|
minWidth: RAIL_WIDTH
|
|
@@ -1454,6 +1451,7 @@ function TopbarRail({ brand, categories, user, breadcrumbs, actions, defaultCate
|
|
|
1454
1451
|
const Icon = category.icon;
|
|
1455
1452
|
return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
|
|
1456
1453
|
render: /* @__PURE__ */ jsx("button", {
|
|
1454
|
+
type: "button",
|
|
1457
1455
|
onClick: () => handleCategorySelect(category.id),
|
|
1458
1456
|
className: cn("flex items-center justify-center h-10 w-10 rounded-lg transition-colors", activeCategory === category.id ? railVariant === "dark" || railVariant === "primary" ? "bg-white/20" : "bg-sidebar-accent text-sidebar-accent-foreground" : railVariant === "dark" || railVariant === "primary" ? "hover:bg-white/10" : "hover:bg-sidebar-accent/50")
|
|
1459
1457
|
}),
|
|
@@ -1468,7 +1466,7 @@ function TopbarRail({ brand, categories, user, breadcrumbs, actions, defaultCate
|
|
|
1468
1466
|
/* @__PURE__ */ jsx("aside", {
|
|
1469
1467
|
className: cn("hidden md:flex flex-col border-r bg-sidebar overflow-hidden transition-[width,opacity] duration-200 ease-linear", isDrawerOpen ? "opacity-100" : "opacity-0 w-0"),
|
|
1470
1468
|
style: { width: isDrawerOpen ? DRAWER_WIDTH : 0 },
|
|
1471
|
-
children: activeCategoryData && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(SidebarHeader, {
|
|
1469
|
+
children: activeCategoryData && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(SidebarHeader, {
|
|
1472
1470
|
className: "h-12 border-b border-sidebar-border px-4 flex items-center shrink-0",
|
|
1473
1471
|
children: /* @__PURE__ */ jsxs("div", {
|
|
1474
1472
|
className: "flex items-center gap-2",
|
|
@@ -1518,7 +1516,7 @@ function MobileNav({ brand, categories, user, activeCategory, onCategorySelect,
|
|
|
1518
1516
|
return /* @__PURE__ */ jsxs("div", {
|
|
1519
1517
|
className: "flex h-full flex-row",
|
|
1520
1518
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
1521
|
-
className: cn("flex flex-col shrink-0", railVariants[railVariant], railTextVariants[railVariant], "border-r
|
|
1519
|
+
className: cn("flex flex-col shrink-0", railVariants[railVariant], railTextVariants[railVariant], "border-r", railBorderVariants[railVariant]),
|
|
1522
1520
|
style: {
|
|
1523
1521
|
width: RAIL_WIDTH,
|
|
1524
1522
|
minWidth: RAIL_WIDTH
|
|
@@ -1536,6 +1534,7 @@ function MobileNav({ brand, categories, user, activeCategory, onCategorySelect,
|
|
|
1536
1534
|
children: categories.map((category) => {
|
|
1537
1535
|
const Icon = category.icon;
|
|
1538
1536
|
return /* @__PURE__ */ jsx("button", {
|
|
1537
|
+
type: "button",
|
|
1539
1538
|
onClick: () => onCategorySelect(category.id),
|
|
1540
1539
|
className: cn("flex items-center justify-center h-10 w-10 rounded-lg transition-colors", activeCategory === category.id ? railVariant === "dark" || railVariant === "primary" ? "bg-white/20" : "bg-sidebar-accent text-sidebar-accent-foreground" : railVariant === "dark" || railVariant === "primary" ? "hover:bg-white/10" : "hover:bg-sidebar-accent/50"),
|
|
1541
1540
|
children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5" })
|
|
@@ -1544,7 +1543,7 @@ function MobileNav({ brand, categories, user, activeCategory, onCategorySelect,
|
|
|
1544
1543
|
})]
|
|
1545
1544
|
}), /* @__PURE__ */ jsx("div", {
|
|
1546
1545
|
className: "flex-1 flex flex-col bg-sidebar overflow-hidden",
|
|
1547
|
-
children: activeCategoryData ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1546
|
+
children: activeCategoryData ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1548
1547
|
/* @__PURE__ */ jsx(SidebarHeader, {
|
|
1549
1548
|
className: "h-14 border-b border-sidebar-border px-4 flex items-center shrink-0",
|
|
1550
1549
|
children: /* @__PURE__ */ jsxs("div", {
|
|
@@ -1614,4 +1613,294 @@ function MobileNav({ brand, categories, user, activeCategory, onCategorySelect,
|
|
|
1614
1613
|
}
|
|
1615
1614
|
|
|
1616
1615
|
//#endregion
|
|
1617
|
-
|
|
1616
|
+
//#region src/dashboard/dashboard-layout.tsx
|
|
1617
|
+
const DualMobileTrigger = React$1.memo(function DualMobileTrigger({ onOpen }) {
|
|
1618
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
1619
|
+
variant: "ghost",
|
|
1620
|
+
size: "icon",
|
|
1621
|
+
className: "md:hidden -ml-1",
|
|
1622
|
+
onClick: onOpen,
|
|
1623
|
+
"aria-label": "Open navigation",
|
|
1624
|
+
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
1625
|
+
});
|
|
1626
|
+
});
|
|
1627
|
+
function renderSidebar(config, mobileOpen, setMobileOpen) {
|
|
1628
|
+
switch (config.variant) {
|
|
1629
|
+
case "dual": return /* @__PURE__ */ jsx(DualSidebar, {
|
|
1630
|
+
brand: config.brand,
|
|
1631
|
+
categories: config.categories,
|
|
1632
|
+
user: config.user,
|
|
1633
|
+
railVariant: config.railVariant,
|
|
1634
|
+
sidebarStyle: config.sidebarStyle,
|
|
1635
|
+
defaultCategoryId: config.defaultCategoryId,
|
|
1636
|
+
activeCategoryId: config.activeCategoryId,
|
|
1637
|
+
onCategoryChange: config.onCategoryChange,
|
|
1638
|
+
defaultCollapsed: config.defaultCollapsed,
|
|
1639
|
+
collapsed: config.collapsed,
|
|
1640
|
+
onCollapsedChange: config.onCollapsedChange,
|
|
1641
|
+
headerContent: config.headerContent,
|
|
1642
|
+
footerContent: config.footerContent,
|
|
1643
|
+
mobileOpen,
|
|
1644
|
+
onMobileOpenChange: setMobileOpen
|
|
1645
|
+
});
|
|
1646
|
+
case "inset": return /* @__PURE__ */ jsx(InsetSidebar, {
|
|
1647
|
+
brand: config.brand,
|
|
1648
|
+
navigation: config.navigation,
|
|
1649
|
+
secondaryNavigation: config.secondaryNavigation,
|
|
1650
|
+
project: config.project,
|
|
1651
|
+
user: config.user,
|
|
1652
|
+
headerContent: config.headerContent,
|
|
1653
|
+
sidebarStyle: config.sidebarStyle,
|
|
1654
|
+
density: config.density,
|
|
1655
|
+
side: config.side
|
|
1656
|
+
});
|
|
1657
|
+
case "floating": return /* @__PURE__ */ jsx(FloatingSidebar, {
|
|
1658
|
+
brand: config.brand,
|
|
1659
|
+
navigation: config.navigation,
|
|
1660
|
+
secondaryNavigation: config.secondaryNavigation,
|
|
1661
|
+
project: config.project,
|
|
1662
|
+
user: config.user,
|
|
1663
|
+
headerContent: config.headerContent,
|
|
1664
|
+
side: config.side
|
|
1665
|
+
});
|
|
1666
|
+
case "mini": return /* @__PURE__ */ jsx(MiniSidebar, {
|
|
1667
|
+
brand: config.brand,
|
|
1668
|
+
navigation: config.navigation,
|
|
1669
|
+
secondaryNavigation: config.secondaryNavigation,
|
|
1670
|
+
user: config.user,
|
|
1671
|
+
headerContent: config.headerContent,
|
|
1672
|
+
side: config.side,
|
|
1673
|
+
showRail: config.showRail
|
|
1674
|
+
});
|
|
1675
|
+
default: return null;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
function DashboardLayout({ sidebar, breadcrumbs, headerRight, headerLeft, header, stickyHeader = false, headerClassName, className, children }) {
|
|
1679
|
+
const [mobileOpen, setMobileOpen] = React$1.useState(false);
|
|
1680
|
+
const handleMobileOpen = React$1.useCallback(() => setMobileOpen(true), []);
|
|
1681
|
+
const bc = breadcrumbs !== false && breadcrumbs ? breadcrumbs : void 0;
|
|
1682
|
+
const autoBreadcrumbs = useDashboardBreadcrumbs(bc?.basePath ?? "/", bc);
|
|
1683
|
+
const breadcrumbItems = breadcrumbs === false ? void 0 : autoBreadcrumbs;
|
|
1684
|
+
const isDual = sidebar.variant === "dual";
|
|
1685
|
+
const isTopbarRail = sidebar.variant === "topbar-rail";
|
|
1686
|
+
const headerElement = header === false ? null : header ?? /* @__PURE__ */ jsx(DashboardHeader, {
|
|
1687
|
+
breadcrumbs: breadcrumbItems,
|
|
1688
|
+
leftContent: headerLeft,
|
|
1689
|
+
rightContent: headerRight,
|
|
1690
|
+
showSidebarTrigger: !isDual && !isTopbarRail,
|
|
1691
|
+
customTrigger: isDual ? /* @__PURE__ */ jsx(DualMobileTrigger, { onOpen: handleMobileOpen }) : void 0,
|
|
1692
|
+
className: cn(stickyHeader && "sticky top-0 z-10 bg-background/95 backdrop-blur-sm border-b", headerClassName)
|
|
1693
|
+
});
|
|
1694
|
+
if (isTopbarRail && sidebar.variant === "topbar-rail") return /* @__PURE__ */ jsx(ClientOnly, { children: /* @__PURE__ */ jsx(TopbarRail, {
|
|
1695
|
+
brand: sidebar.brand,
|
|
1696
|
+
categories: sidebar.categories,
|
|
1697
|
+
user: sidebar.user,
|
|
1698
|
+
breadcrumbs: breadcrumbItems?.map((b) => ({
|
|
1699
|
+
label: b.label,
|
|
1700
|
+
href: b.href,
|
|
1701
|
+
isCurrentPage: b.current
|
|
1702
|
+
})),
|
|
1703
|
+
actions: headerRight,
|
|
1704
|
+
railVariant: sidebar.railVariant,
|
|
1705
|
+
defaultCategoryId: sidebar.defaultCategoryId,
|
|
1706
|
+
defaultDrawerOpen: sidebar.defaultDrawerOpen,
|
|
1707
|
+
className,
|
|
1708
|
+
children
|
|
1709
|
+
}) });
|
|
1710
|
+
return /* @__PURE__ */ jsxs(SidebarProvider, { children: [renderSidebar(sidebar, mobileOpen, setMobileOpen), /* @__PURE__ */ jsxs(SidebarInset, {
|
|
1711
|
+
className: cn("min-w-0", className),
|
|
1712
|
+
children: [headerElement, /* @__PURE__ */ jsx(DashboardContent, { children })]
|
|
1713
|
+
})] });
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
//#endregion
|
|
1717
|
+
//#region src/dashboard/page-header.tsx
|
|
1718
|
+
function PageHeader({ items, className, actions }) {
|
|
1719
|
+
return /* @__PURE__ */ jsxs("header", {
|
|
1720
|
+
className: cn("flex h-12 shrink-0 items-center justify-between gap-2", className),
|
|
1721
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1722
|
+
className: "flex items-center gap-2",
|
|
1723
|
+
children: [
|
|
1724
|
+
/* @__PURE__ */ jsx(SidebarTrigger, { className: "-ml-1" }),
|
|
1725
|
+
/* @__PURE__ */ jsx(Separator, {
|
|
1726
|
+
orientation: "vertical",
|
|
1727
|
+
className: "!self-center h-4"
|
|
1728
|
+
}),
|
|
1729
|
+
/* @__PURE__ */ jsx(Breadcrumb, { children: /* @__PURE__ */ jsx(BreadcrumbList, { children: items.map((item, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [/* @__PURE__ */ jsx(BreadcrumbItem, {
|
|
1730
|
+
className: item.current ? "" : "hidden md:block",
|
|
1731
|
+
children: item.current ? /* @__PURE__ */ jsx(BreadcrumbPage, { children: item.label }) : /* @__PURE__ */ jsx(BreadcrumbLink, {
|
|
1732
|
+
render: /* @__PURE__ */ jsx(Link, { href: item.href || "#" }),
|
|
1733
|
+
children: item.label
|
|
1734
|
+
})
|
|
1735
|
+
}), index < items.length - 1 && /* @__PURE__ */ jsx(BreadcrumbSeparator, { className: "hidden md:block" })] }, `breadcrumb-${index}`)) }) })
|
|
1736
|
+
]
|
|
1737
|
+
}), actions]
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
//#endregion
|
|
1742
|
+
//#region src/dashboard/dashboard-page-layout.tsx
|
|
1743
|
+
const defaultFallback = /* @__PURE__ */ jsx("div", {
|
|
1744
|
+
className: "flex items-center justify-center py-12",
|
|
1745
|
+
children: /* @__PURE__ */ jsx("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-primary border-t-transparent" })
|
|
1746
|
+
});
|
|
1747
|
+
/**
|
|
1748
|
+
* DashboardPageLayout — Standard page shell for dashboard pages.
|
|
1749
|
+
* Combines PageHeader (breadcrumbs + actions) with optional Suspense boundary.
|
|
1750
|
+
*
|
|
1751
|
+
* @example
|
|
1752
|
+
* ```tsx
|
|
1753
|
+
* <DashboardPageLayout
|
|
1754
|
+
* breadcrumbs={[
|
|
1755
|
+
* { label: "Dashboard", href: "/dashboard" },
|
|
1756
|
+
* { label: "Posts", current: true },
|
|
1757
|
+
* ]}
|
|
1758
|
+
* actions={<Button>New Post</Button>}
|
|
1759
|
+
* fallback={<SkeletonTable rows={5} />}
|
|
1760
|
+
* >
|
|
1761
|
+
* <PostsList />
|
|
1762
|
+
* </DashboardPageLayout>
|
|
1763
|
+
* ```
|
|
1764
|
+
*/
|
|
1765
|
+
function DashboardPageLayout({ breadcrumbs, actions, suspense = true, fallback = defaultFallback, className, children }) {
|
|
1766
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(PageHeader, {
|
|
1767
|
+
items: breadcrumbs,
|
|
1768
|
+
actions
|
|
1769
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
1770
|
+
className: cn("flex-1", className),
|
|
1771
|
+
children: suspense ? /* @__PURE__ */ jsx(Suspense, {
|
|
1772
|
+
fallback,
|
|
1773
|
+
children
|
|
1774
|
+
}) : children
|
|
1775
|
+
})] });
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
//#endregion
|
|
1779
|
+
//#region src/dashboard/header-section.tsx
|
|
1780
|
+
const variants = {
|
|
1781
|
+
default: {
|
|
1782
|
+
wrapper: "px-6 py-5 rounded-xl shadow-sm border bg-background hover:shadow-md",
|
|
1783
|
+
title: "text-2xl font-semibold tracking-tight",
|
|
1784
|
+
description: "text-sm text-muted-foreground",
|
|
1785
|
+
iconSize: "size-10",
|
|
1786
|
+
iconInner: "size-5",
|
|
1787
|
+
spacing: "space-y-1"
|
|
1788
|
+
},
|
|
1789
|
+
compact: {
|
|
1790
|
+
wrapper: "px-4 py-3 rounded-lg border bg-background/50",
|
|
1791
|
+
title: "text-lg font-semibold",
|
|
1792
|
+
description: "text-xs text-muted-foreground",
|
|
1793
|
+
iconSize: "size-8",
|
|
1794
|
+
iconInner: "size-4",
|
|
1795
|
+
spacing: "space-y-0.5"
|
|
1796
|
+
},
|
|
1797
|
+
hero: {
|
|
1798
|
+
wrapper: "px-8 py-8 rounded-2xl shadow-lg border bg-gradient-to-br from-background to-muted/20",
|
|
1799
|
+
title: "text-3xl font-bold tracking-tight bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text text-transparent",
|
|
1800
|
+
description: "text-base text-muted-foreground mt-2",
|
|
1801
|
+
iconSize: "size-14",
|
|
1802
|
+
iconInner: "size-7",
|
|
1803
|
+
spacing: "space-y-2"
|
|
1804
|
+
},
|
|
1805
|
+
minimal: {
|
|
1806
|
+
wrapper: "px-0 py-2",
|
|
1807
|
+
title: "text-xl font-medium",
|
|
1808
|
+
description: "text-sm text-muted-foreground",
|
|
1809
|
+
iconSize: "size-8",
|
|
1810
|
+
iconInner: "size-4",
|
|
1811
|
+
spacing: "space-y-0.5"
|
|
1812
|
+
}
|
|
1813
|
+
};
|
|
1814
|
+
function HeaderSection({ title, description, actions = null, icon: Icon = null, iconClassName, loading = false, variant = "default", className, badge, breadcrumbs, metadata, children }) {
|
|
1815
|
+
const currentVariant = variants[variant] || variants.default;
|
|
1816
|
+
const isBadgeObject = (badge) => {
|
|
1817
|
+
return typeof badge === "object" && badge !== null && "text" in badge;
|
|
1818
|
+
};
|
|
1819
|
+
if (loading && !title) return /* @__PURE__ */ jsx("div", {
|
|
1820
|
+
className: cn(currentVariant.wrapper, "transition-all duration-300", className),
|
|
1821
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1822
|
+
className: "flex flex-col sm:flex-row sm:items-center sm:justify-between",
|
|
1823
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1824
|
+
className: "flex items-center gap-4",
|
|
1825
|
+
children: [Icon && /* @__PURE__ */ jsx(Skeleton, { className: cn(currentVariant.iconSize, "rounded-lg") }), /* @__PURE__ */ jsxs("div", {
|
|
1826
|
+
className: currentVariant.spacing,
|
|
1827
|
+
children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-48" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-64 mt-1" })]
|
|
1828
|
+
})]
|
|
1829
|
+
}), actions && /* @__PURE__ */ jsxs("div", {
|
|
1830
|
+
className: "flex gap-2 mt-4 sm:mt-0",
|
|
1831
|
+
children: [/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-24" }), /* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-24" })]
|
|
1832
|
+
})]
|
|
1833
|
+
})
|
|
1834
|
+
});
|
|
1835
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1836
|
+
className: cn("transition-all duration-300", currentVariant.wrapper, className),
|
|
1837
|
+
children: [
|
|
1838
|
+
breadcrumbs && /* @__PURE__ */ jsx("nav", {
|
|
1839
|
+
className: "mb-3 text-sm text-muted-foreground",
|
|
1840
|
+
children: breadcrumbs
|
|
1841
|
+
}),
|
|
1842
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1843
|
+
className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4",
|
|
1844
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1845
|
+
className: "flex items-start sm:items-center gap-4",
|
|
1846
|
+
children: [Icon && /* @__PURE__ */ jsx("div", {
|
|
1847
|
+
className: cn("flex aspect-square items-center justify-center rounded-lg flex-shrink-0", "bg-primary/10 text-primary", currentVariant.iconSize, iconClassName),
|
|
1848
|
+
children: /* @__PURE__ */ jsx(Icon, { className: currentVariant.iconInner })
|
|
1849
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1850
|
+
className: cn("flex-1", currentVariant.spacing),
|
|
1851
|
+
children: [
|
|
1852
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1853
|
+
className: "flex items-center gap-2 flex-wrap",
|
|
1854
|
+
children: [/* @__PURE__ */ jsx("h1", {
|
|
1855
|
+
className: cn("leading-none", currentVariant.title),
|
|
1856
|
+
children: title
|
|
1857
|
+
}), badge && (isBadgeObject(badge) ? /* @__PURE__ */ jsx(Badge, {
|
|
1858
|
+
variant: badge.variant || "secondary",
|
|
1859
|
+
className: cn("ml-2", badge.className),
|
|
1860
|
+
children: badge.text
|
|
1861
|
+
}) : badge)]
|
|
1862
|
+
}),
|
|
1863
|
+
description && /* @__PURE__ */ jsx("p", {
|
|
1864
|
+
className: currentVariant.description,
|
|
1865
|
+
children: description
|
|
1866
|
+
}),
|
|
1867
|
+
metadata && /* @__PURE__ */ jsx("div", {
|
|
1868
|
+
className: "flex items-center gap-4 mt-2 text-xs text-muted-foreground",
|
|
1869
|
+
children: metadata.map((item, index) => /* @__PURE__ */ jsxs("div", {
|
|
1870
|
+
className: "flex items-center gap-1",
|
|
1871
|
+
children: [item.icon && /* @__PURE__ */ jsx(item.icon, { className: "size-3" }), /* @__PURE__ */ jsx("span", { children: item.text })]
|
|
1872
|
+
}, index))
|
|
1873
|
+
})
|
|
1874
|
+
]
|
|
1875
|
+
})]
|
|
1876
|
+
}), actions && /* @__PURE__ */ jsx("div", {
|
|
1877
|
+
className: "flex flex-wrap gap-2 mt-4 sm:mt-0",
|
|
1878
|
+
children: actions.map((action, index) => {
|
|
1879
|
+
const isActionLoading = action.loading ?? false;
|
|
1880
|
+
const isDisabled = loading || action.disabled || isActionLoading;
|
|
1881
|
+
const displayText = isActionLoading ? action.loadingText || action.text : action.text;
|
|
1882
|
+
return /* @__PURE__ */ jsxs(Button, {
|
|
1883
|
+
onClick: action.onClick,
|
|
1884
|
+
disabled: isDisabled,
|
|
1885
|
+
variant: action.variant || "default",
|
|
1886
|
+
size: action.size || (variant === "compact" ? "sm" : "default"),
|
|
1887
|
+
className: cn(variant === "hero" && "shadow-md hover:shadow-lg", action.className),
|
|
1888
|
+
children: [
|
|
1889
|
+
action.icon && action.iconPosition !== "right" && /* @__PURE__ */ jsx(action.icon, { className: "size-4 mr-2" }),
|
|
1890
|
+
/* @__PURE__ */ jsx("span", { children: displayText }),
|
|
1891
|
+
action.icon && action.iconPosition === "right" && /* @__PURE__ */ jsx(action.icon, { className: "size-4 ml-2" })
|
|
1892
|
+
]
|
|
1893
|
+
}, index);
|
|
1894
|
+
})
|
|
1895
|
+
})]
|
|
1896
|
+
}),
|
|
1897
|
+
children && /* @__PURE__ */ jsx("div", {
|
|
1898
|
+
className: cn("mt-4 pt-4 border-t", variant === "minimal" && "border-0 pt-2"),
|
|
1899
|
+
children
|
|
1900
|
+
})
|
|
1901
|
+
]
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
//#endregion
|
|
1906
|
+
export { ClientOnly, DashboardContent, DashboardHeader, DashboardLayout, DashboardPageLayout, DualSidebar, FloatingSidebar, HeaderSection, InsetSidebar, MiniSidebar, PageHeader, ProjectSwitcher, SidebarBrand, SidebarNav, SidebarNavGroup, SidebarNavItem, SidebarUserMenu, TopbarRail, cn, findActiveCategory, findActiveItem, matchRoute, resolveCategories, resolveMiniNavigation, resolveNavigation, useDashboardBreadcrumbs };
|