@clayer/dashboard-layout 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -0
- package/dist/index.d.ts +473 -0
- package/dist/index.js +1393 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1393 @@
|
|
|
1
|
+
// src/dashboard-layout.tsx
|
|
2
|
+
import {
|
|
3
|
+
Fragment,
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
useState
|
|
9
|
+
} from "react";
|
|
10
|
+
import { useTheme } from "@clayer/theme";
|
|
11
|
+
import { cn } from "@clayer/utils";
|
|
12
|
+
import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
var defaultSidebar = {
|
|
14
|
+
collapsible: true,
|
|
15
|
+
defaultCollapsed: false,
|
|
16
|
+
width: 280,
|
|
17
|
+
collapsedWidth: 80,
|
|
18
|
+
showTooltipsWhenCollapsed: true,
|
|
19
|
+
closeOnNavigateMobile: true
|
|
20
|
+
};
|
|
21
|
+
var densityClasses = {
|
|
22
|
+
compact: {
|
|
23
|
+
header: "h-14",
|
|
24
|
+
content: "p-4",
|
|
25
|
+
navItem: "min-h-9 px-2.5 py-2 text-sm",
|
|
26
|
+
childItem: "min-h-8 px-3 py-1.5 text-sm"
|
|
27
|
+
},
|
|
28
|
+
comfortable: {
|
|
29
|
+
header: "h-16",
|
|
30
|
+
content: "p-4 lg:p-6",
|
|
31
|
+
navItem: "min-h-10 px-3 py-2 text-sm",
|
|
32
|
+
childItem: "min-h-9 px-3 py-2 text-sm"
|
|
33
|
+
},
|
|
34
|
+
spacious: {
|
|
35
|
+
header: "h-20",
|
|
36
|
+
content: "p-5 lg:p-8",
|
|
37
|
+
navItem: "min-h-11 px-4 py-2.5 text-base",
|
|
38
|
+
childItem: "min-h-10 px-4 py-2 text-sm"
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var maxWidthClasses = {
|
|
42
|
+
none: "max-w-none",
|
|
43
|
+
"screen-xl": "max-w-screen-xl",
|
|
44
|
+
"screen-2xl": "max-w-screen-2xl",
|
|
45
|
+
"7xl": "max-w-7xl"
|
|
46
|
+
};
|
|
47
|
+
var layoutPaddingClasses = {
|
|
48
|
+
none: "p-0",
|
|
49
|
+
sm: "p-0 lg:p-2",
|
|
50
|
+
md: "p-0 lg:p-3",
|
|
51
|
+
lg: "p-0 lg:p-4"
|
|
52
|
+
};
|
|
53
|
+
var layoutRadiusClasses = {
|
|
54
|
+
none: "rounded-none",
|
|
55
|
+
sm: "rounded-none lg:rounded-md",
|
|
56
|
+
md: "rounded-none lg:rounded-lg",
|
|
57
|
+
lg: "rounded-none lg:rounded-xl",
|
|
58
|
+
xl: "rounded-none lg:rounded-2xl"
|
|
59
|
+
};
|
|
60
|
+
var layoutShadowClasses = {
|
|
61
|
+
none: "shadow-none",
|
|
62
|
+
sm: "shadow-sm",
|
|
63
|
+
md: "shadow-md",
|
|
64
|
+
lg: "shadow-xl"
|
|
65
|
+
};
|
|
66
|
+
var userMenuRadiusClasses = {
|
|
67
|
+
none: "rounded-none",
|
|
68
|
+
sm: "rounded-md",
|
|
69
|
+
md: "rounded-lg",
|
|
70
|
+
lg: "rounded-xl",
|
|
71
|
+
full: "rounded-full"
|
|
72
|
+
};
|
|
73
|
+
var userMenuPaddingClasses = {
|
|
74
|
+
none: "p-0",
|
|
75
|
+
sm: "p-1.5",
|
|
76
|
+
md: "p-2"
|
|
77
|
+
};
|
|
78
|
+
var userMenuBackgroundClasses = {
|
|
79
|
+
transparent: "bg-transparent",
|
|
80
|
+
background: "bg-background",
|
|
81
|
+
muted: "bg-muted/60"
|
|
82
|
+
};
|
|
83
|
+
var userMenuShadowClasses = {
|
|
84
|
+
none: "shadow-none",
|
|
85
|
+
sm: "shadow-sm",
|
|
86
|
+
md: "shadow-md"
|
|
87
|
+
};
|
|
88
|
+
var defaultLayoutConfig = {
|
|
89
|
+
padding: "none",
|
|
90
|
+
bordered: false,
|
|
91
|
+
radius: "none",
|
|
92
|
+
shadow: "none",
|
|
93
|
+
borders: {
|
|
94
|
+
sidebar: true,
|
|
95
|
+
header: true,
|
|
96
|
+
footer: true,
|
|
97
|
+
mobileBottomNav: true
|
|
98
|
+
},
|
|
99
|
+
background: {
|
|
100
|
+
root: "bg-background",
|
|
101
|
+
shell: "bg-background",
|
|
102
|
+
sidebar: "bg-sidebar",
|
|
103
|
+
header: "bg-background/85",
|
|
104
|
+
breadcrumb: "bg-background/70",
|
|
105
|
+
main: "bg-muted/30",
|
|
106
|
+
content: void 0,
|
|
107
|
+
footer: "bg-background",
|
|
108
|
+
mobileBottomNav: "bg-background"
|
|
109
|
+
},
|
|
110
|
+
backgroundComponents: {}
|
|
111
|
+
};
|
|
112
|
+
function resolveSurfaceBackgroundClass({
|
|
113
|
+
value,
|
|
114
|
+
hasComponent
|
|
115
|
+
}) {
|
|
116
|
+
if (hasComponent || !value) {
|
|
117
|
+
return void 0;
|
|
118
|
+
}
|
|
119
|
+
if (value === "transparent") {
|
|
120
|
+
return "bg-transparent";
|
|
121
|
+
}
|
|
122
|
+
if (value === "inherit") {
|
|
123
|
+
return "bg-inherit";
|
|
124
|
+
}
|
|
125
|
+
return value;
|
|
126
|
+
}
|
|
127
|
+
function SurfaceBackground({ component }) {
|
|
128
|
+
if (!component) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
if (typeof component === "object" && component !== null && !Array.isArray(component) && "content" in component) {
|
|
132
|
+
return /* @__PURE__ */ jsx(
|
|
133
|
+
"div",
|
|
134
|
+
{
|
|
135
|
+
className: cn(
|
|
136
|
+
"absolute inset-0 z-0 h-full w-full",
|
|
137
|
+
!component.interactive && "pointer-events-none",
|
|
138
|
+
component.className
|
|
139
|
+
),
|
|
140
|
+
style: component.style,
|
|
141
|
+
"aria-hidden": !component.interactive,
|
|
142
|
+
children: component.content
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 z-0 h-full w-full", "aria-hidden": true, children: component });
|
|
147
|
+
}
|
|
148
|
+
function useControllableState({
|
|
149
|
+
value,
|
|
150
|
+
defaultValue,
|
|
151
|
+
onChange
|
|
152
|
+
}) {
|
|
153
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
154
|
+
const isControlled = value !== void 0;
|
|
155
|
+
const currentValue = isControlled ? value : internalValue;
|
|
156
|
+
const setValue = useCallback(
|
|
157
|
+
(nextValue) => {
|
|
158
|
+
if (!isControlled) {
|
|
159
|
+
setInternalValue(nextValue);
|
|
160
|
+
}
|
|
161
|
+
onChange?.(nextValue);
|
|
162
|
+
},
|
|
163
|
+
[isControlled, onChange]
|
|
164
|
+
);
|
|
165
|
+
return [currentValue, setValue];
|
|
166
|
+
}
|
|
167
|
+
function defaultIsItemActive(item, currentPath) {
|
|
168
|
+
if (!currentPath) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
return item.exact ? currentPath === item.href : currentPath === item.href || currentPath.startsWith(`${item.href}/`);
|
|
172
|
+
}
|
|
173
|
+
function DefaultLink({ href, children, className, onClick, external, "aria-current": ariaCurrent }) {
|
|
174
|
+
return /* @__PURE__ */ jsx(
|
|
175
|
+
"a",
|
|
176
|
+
{
|
|
177
|
+
href,
|
|
178
|
+
className,
|
|
179
|
+
onClick,
|
|
180
|
+
target: external ? "_blank" : void 0,
|
|
181
|
+
rel: external ? "noreferrer" : void 0,
|
|
182
|
+
"aria-current": ariaCurrent,
|
|
183
|
+
children
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
function getThemeStyles(styles) {
|
|
188
|
+
return styles;
|
|
189
|
+
}
|
|
190
|
+
function Badge({ children, className }) {
|
|
191
|
+
if (!children) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
return /* @__PURE__ */ jsx(
|
|
195
|
+
"span",
|
|
196
|
+
{
|
|
197
|
+
className: cn(
|
|
198
|
+
"ml-auto rounded-full bg-[hsl(var(--cx-sidebar-foreground)/0.12)] px-2 py-0.5 text-[11px] font-medium leading-5",
|
|
199
|
+
className
|
|
200
|
+
),
|
|
201
|
+
children
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
function NavIcon({ icon: Icon, className }) {
|
|
206
|
+
if (!Icon) {
|
|
207
|
+
return /* @__PURE__ */ jsx("span", { className: cn("grid size-5 shrink-0 place-items-center leading-none", className), "aria-hidden": true, children: /* @__PURE__ */ jsx("span", { className: "size-2 rounded-full bg-current opacity-60" }) });
|
|
208
|
+
}
|
|
209
|
+
return /* @__PURE__ */ jsx("span", { className: cn("grid size-5 shrink-0 place-items-center leading-none", className), "aria-hidden": true, children: /* @__PURE__ */ jsx(Icon, { className: "block size-4 shrink-0 leading-none", "aria-hidden": true }) });
|
|
210
|
+
}
|
|
211
|
+
function SidebarLink({
|
|
212
|
+
item,
|
|
213
|
+
active,
|
|
214
|
+
collapsed,
|
|
215
|
+
closeMobile,
|
|
216
|
+
onExpandPersist,
|
|
217
|
+
onCollapsedTooltipChange,
|
|
218
|
+
renderLink,
|
|
219
|
+
styles,
|
|
220
|
+
density
|
|
221
|
+
}) {
|
|
222
|
+
const handleClick = () => {
|
|
223
|
+
onCollapsedTooltipChange?.(null);
|
|
224
|
+
onExpandPersist?.();
|
|
225
|
+
item.onClick?.();
|
|
226
|
+
closeMobile?.();
|
|
227
|
+
};
|
|
228
|
+
const handleTooltipShow = (element) => {
|
|
229
|
+
if (!collapsed) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const rect = element.getBoundingClientRect();
|
|
233
|
+
onCollapsedTooltipChange?.({
|
|
234
|
+
label: item.label,
|
|
235
|
+
top: rect.top + rect.height / 2,
|
|
236
|
+
left: rect.right + 12
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
const className = cn(
|
|
240
|
+
"group relative flex w-full items-center gap-3 rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
241
|
+
active ? "bg-sidebar-active text-white shadow-sm" : "text-sidebar-foreground/78 transition-colors hover:bg-[hsl(var(--cx-sidebar-foreground)/0.08)] hover:text-sidebar-foreground motion-reduce:transition-none",
|
|
242
|
+
densityClasses[density].navItem,
|
|
243
|
+
item.disabled && "pointer-events-none opacity-45",
|
|
244
|
+
collapsed && "justify-center px-2",
|
|
245
|
+
styles?.sidebarNavItem,
|
|
246
|
+
active && styles?.sidebarNavItemActive
|
|
247
|
+
);
|
|
248
|
+
return /* @__PURE__ */ jsx(
|
|
249
|
+
"li",
|
|
250
|
+
{
|
|
251
|
+
onMouseEnter: (event) => handleTooltipShow(event.currentTarget),
|
|
252
|
+
onMouseLeave: () => onCollapsedTooltipChange?.(null),
|
|
253
|
+
onFocus: (event) => handleTooltipShow(event.currentTarget),
|
|
254
|
+
onBlur: () => onCollapsedTooltipChange?.(null),
|
|
255
|
+
children: renderLink({
|
|
256
|
+
href: item.href,
|
|
257
|
+
external: item.external,
|
|
258
|
+
onClick: handleClick,
|
|
259
|
+
className,
|
|
260
|
+
"aria-current": active ? "page" : void 0,
|
|
261
|
+
children: /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
262
|
+
/* @__PURE__ */ jsx(NavIcon, { icon: item.icon, className: styles?.sidebarNavItemIcon }),
|
|
263
|
+
/* @__PURE__ */ jsx("span", { className: cn("min-w-0 flex-1 truncate", collapsed && "sr-only", styles?.sidebarNavItemLabel), children: item.label }),
|
|
264
|
+
!collapsed ? /* @__PURE__ */ jsx(Badge, { className: styles?.sidebarNavItemBadge, children: item.badge }) : null
|
|
265
|
+
] })
|
|
266
|
+
})
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
function SidebarGroup({
|
|
271
|
+
item,
|
|
272
|
+
collapsed,
|
|
273
|
+
currentPath,
|
|
274
|
+
isItemActive,
|
|
275
|
+
closeMobile,
|
|
276
|
+
onExpandPersist,
|
|
277
|
+
onCollapsedTooltipChange,
|
|
278
|
+
renderLink,
|
|
279
|
+
styles,
|
|
280
|
+
density
|
|
281
|
+
}) {
|
|
282
|
+
const hasActiveChild = item.children.some((child) => isItemActive(child, currentPath));
|
|
283
|
+
const [open, setOpen] = useState(item.defaultOpen ?? hasActiveChild);
|
|
284
|
+
useEffect(() => {
|
|
285
|
+
if (hasActiveChild) {
|
|
286
|
+
setOpen(true);
|
|
287
|
+
}
|
|
288
|
+
}, [hasActiveChild]);
|
|
289
|
+
const handleTooltipShow = (element) => {
|
|
290
|
+
if (!collapsed) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const rect = element.getBoundingClientRect();
|
|
294
|
+
onCollapsedTooltipChange?.({
|
|
295
|
+
label: item.label,
|
|
296
|
+
top: rect.top + rect.height / 2,
|
|
297
|
+
left: rect.right + 12
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
return /* @__PURE__ */ jsxs(
|
|
301
|
+
"li",
|
|
302
|
+
{
|
|
303
|
+
className: cn("space-y-1", styles?.sidebarGroup),
|
|
304
|
+
onMouseLeave: () => onCollapsedTooltipChange?.(null),
|
|
305
|
+
onBlur: () => onCollapsedTooltipChange?.(null),
|
|
306
|
+
children: [
|
|
307
|
+
/* @__PURE__ */ jsxs(
|
|
308
|
+
"button",
|
|
309
|
+
{
|
|
310
|
+
type: "button",
|
|
311
|
+
onClick: () => {
|
|
312
|
+
onCollapsedTooltipChange?.(null);
|
|
313
|
+
onExpandPersist?.();
|
|
314
|
+
setOpen((value) => !value);
|
|
315
|
+
},
|
|
316
|
+
onMouseEnter: (event) => handleTooltipShow(event.currentTarget),
|
|
317
|
+
onFocus: (event) => handleTooltipShow(event.currentTarget),
|
|
318
|
+
"aria-expanded": open,
|
|
319
|
+
className: cn(
|
|
320
|
+
"group relative flex w-full items-center gap-3 rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
321
|
+
hasActiveChild ? "bg-sidebar-active text-white shadow-sm" : "text-sidebar-foreground/78 transition-colors hover:bg-[hsl(var(--cx-sidebar-foreground)/0.08)] hover:text-sidebar-foreground motion-reduce:transition-none",
|
|
322
|
+
densityClasses[density].navItem,
|
|
323
|
+
collapsed && "justify-center px-2",
|
|
324
|
+
styles?.sidebarGroupTrigger,
|
|
325
|
+
hasActiveChild && styles?.sidebarNavItemActive
|
|
326
|
+
),
|
|
327
|
+
children: [
|
|
328
|
+
/* @__PURE__ */ jsx(NavIcon, { icon: item.icon, className: styles?.sidebarNavItemIcon }),
|
|
329
|
+
/* @__PURE__ */ jsx("span", { className: cn("min-w-0 flex-1 truncate text-left", collapsed && "sr-only", styles?.sidebarNavItemLabel), children: item.label }),
|
|
330
|
+
!collapsed ? /* @__PURE__ */ jsx(Badge, { className: styles?.sidebarNavItemBadge, children: item.badge }) : null,
|
|
331
|
+
!collapsed ? /* @__PURE__ */ jsx("span", { className: cn("text-xs transition-transform", open && "rotate-90"), children: "\u203A" }) : null
|
|
332
|
+
]
|
|
333
|
+
}
|
|
334
|
+
),
|
|
335
|
+
!collapsed && open ? /* @__PURE__ */ jsx(
|
|
336
|
+
"ul",
|
|
337
|
+
{
|
|
338
|
+
className: cn(
|
|
339
|
+
"ml-5 space-y-1 border-l border-[hsl(var(--cx-sidebar-foreground)/0.12)] pl-3",
|
|
340
|
+
styles?.sidebarGroupContent
|
|
341
|
+
),
|
|
342
|
+
children: item.children.filter((child) => !child.hidden).map((child) => /* @__PURE__ */ jsx(
|
|
343
|
+
SidebarLink,
|
|
344
|
+
{
|
|
345
|
+
item: child,
|
|
346
|
+
active: isItemActive(child, currentPath),
|
|
347
|
+
collapsed: false,
|
|
348
|
+
closeMobile,
|
|
349
|
+
onExpandPersist,
|
|
350
|
+
onCollapsedTooltipChange,
|
|
351
|
+
renderLink,
|
|
352
|
+
styles: { ...styles, sidebarNavItem: cn(styles?.sidebarNavItem, styles?.sidebarGroupChildItem) },
|
|
353
|
+
density
|
|
354
|
+
},
|
|
355
|
+
child.href
|
|
356
|
+
))
|
|
357
|
+
}
|
|
358
|
+
) : null
|
|
359
|
+
]
|
|
360
|
+
}
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
function SidebarNav({
|
|
364
|
+
items,
|
|
365
|
+
collapsed,
|
|
366
|
+
currentPath,
|
|
367
|
+
isItemActive,
|
|
368
|
+
closeMobile,
|
|
369
|
+
onExpandPersist,
|
|
370
|
+
onCollapsedTooltipChange,
|
|
371
|
+
renderLink,
|
|
372
|
+
styles,
|
|
373
|
+
density
|
|
374
|
+
}) {
|
|
375
|
+
return /* @__PURE__ */ jsx(
|
|
376
|
+
"nav",
|
|
377
|
+
{
|
|
378
|
+
className: cn(
|
|
379
|
+
"min-h-0 flex-1 overflow-y-auto overflow-x-hidden px-3 py-4",
|
|
380
|
+
styles?.sidebarNav
|
|
381
|
+
),
|
|
382
|
+
"aria-label": "Sidebar navigation",
|
|
383
|
+
children: /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: items.filter((item) => item.type === "divider" || !item.hidden).map((item, index) => {
|
|
384
|
+
if (item.type === "divider") {
|
|
385
|
+
return /* @__PURE__ */ jsx("li", { className: cn("px-3 py-3", styles?.sidebarNavSection), children: item.label && !collapsed ? /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold uppercase tracking-wide text-sidebar-foreground/45", children: item.label }) : /* @__PURE__ */ jsx("span", { className: "block border-t border-[hsl(var(--cx-sidebar-foreground)/0.12)]" }) }, `${item.label ?? "divider"}-${index}`);
|
|
386
|
+
}
|
|
387
|
+
if (item.type === "group") {
|
|
388
|
+
return /* @__PURE__ */ jsx(
|
|
389
|
+
SidebarGroup,
|
|
390
|
+
{
|
|
391
|
+
item,
|
|
392
|
+
collapsed,
|
|
393
|
+
currentPath,
|
|
394
|
+
isItemActive,
|
|
395
|
+
closeMobile,
|
|
396
|
+
onExpandPersist,
|
|
397
|
+
onCollapsedTooltipChange,
|
|
398
|
+
renderLink,
|
|
399
|
+
styles,
|
|
400
|
+
density
|
|
401
|
+
},
|
|
402
|
+
item.label
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
return /* @__PURE__ */ jsx(
|
|
406
|
+
SidebarLink,
|
|
407
|
+
{
|
|
408
|
+
item,
|
|
409
|
+
active: isItemActive(item, currentPath),
|
|
410
|
+
collapsed,
|
|
411
|
+
closeMobile,
|
|
412
|
+
onExpandPersist,
|
|
413
|
+
onCollapsedTooltipChange,
|
|
414
|
+
renderLink,
|
|
415
|
+
styles,
|
|
416
|
+
density
|
|
417
|
+
},
|
|
418
|
+
item.href
|
|
419
|
+
);
|
|
420
|
+
}) })
|
|
421
|
+
}
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
function DashboardSidebar({
|
|
425
|
+
logo,
|
|
426
|
+
sidebarHeader,
|
|
427
|
+
sidebarBottomPanel,
|
|
428
|
+
sidebarFooter,
|
|
429
|
+
items,
|
|
430
|
+
collapsed,
|
|
431
|
+
onCollapsedChange,
|
|
432
|
+
sidebar,
|
|
433
|
+
currentPath,
|
|
434
|
+
isItemActive,
|
|
435
|
+
closeMobile,
|
|
436
|
+
renderLink,
|
|
437
|
+
styles,
|
|
438
|
+
density,
|
|
439
|
+
layout,
|
|
440
|
+
mobile
|
|
441
|
+
}) {
|
|
442
|
+
const effectiveCollapsed = collapsed;
|
|
443
|
+
const width = mobile ? sidebar.width : effectiveCollapsed ? sidebar.collapsedWidth : sidebar.width;
|
|
444
|
+
const [collapsedTooltip, setCollapsedTooltip] = useState(null);
|
|
445
|
+
const fallbackBottomPanel = sidebarFooter ? {
|
|
446
|
+
content: sidebarFooter
|
|
447
|
+
} : void 0;
|
|
448
|
+
const bottomPanel = sidebarBottomPanel ?? fallbackBottomPanel;
|
|
449
|
+
const showBottomPanel = bottomPanel && (mobile || !effectiveCollapsed);
|
|
450
|
+
const [bottomPanelCollapsed, setBottomPanelCollapsed] = useControllableState({
|
|
451
|
+
value: bottomPanel?.collapsed,
|
|
452
|
+
defaultValue: bottomPanel?.defaultCollapsed ?? false,
|
|
453
|
+
onChange: bottomPanel?.onCollapsedChange
|
|
454
|
+
});
|
|
455
|
+
const isSidebarCompact = !mobile && effectiveCollapsed;
|
|
456
|
+
const isPanelBodyCollapsed = Boolean(bottomPanel?.collapsible && bottomPanelCollapsed && !isSidebarCompact);
|
|
457
|
+
const bottomPanelContent = isSidebarCompact ? bottomPanel?.collapsedContent : bottomPanel?.content;
|
|
458
|
+
const bottomPanelHeight = isPanelBodyCollapsed ? bottomPanel?.collapsedHeight : bottomPanel?.height;
|
|
459
|
+
const bottomPanelStyle = bottomPanelHeight ? { height: bottomPanelHeight } : void 0;
|
|
460
|
+
const handleExpandPersist = () => {
|
|
461
|
+
if (!mobile && collapsed) {
|
|
462
|
+
setCollapsedTooltip(null);
|
|
463
|
+
onCollapsedChange(false);
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
const handleSidebarToggle = () => {
|
|
467
|
+
if (mobile) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
setCollapsedTooltip(null);
|
|
471
|
+
onCollapsedChange(!effectiveCollapsed);
|
|
472
|
+
};
|
|
473
|
+
return /* @__PURE__ */ jsxs(
|
|
474
|
+
"aside",
|
|
475
|
+
{
|
|
476
|
+
className: cn(
|
|
477
|
+
"relative flex h-full min-h-0 shrink-0 flex-col overflow-hidden text-sidebar-foreground transition-[width] duration-200 ease-out motion-reduce:transition-none",
|
|
478
|
+
resolveSurfaceBackgroundClass({
|
|
479
|
+
value: layout.background.sidebar,
|
|
480
|
+
hasComponent: Boolean(layout.backgroundComponents.sidebar)
|
|
481
|
+
}),
|
|
482
|
+
layout.borders.sidebar && "border-r border-[hsl(var(--cx-sidebar-foreground)/0.12)]",
|
|
483
|
+
!mobile && "hidden lg:flex",
|
|
484
|
+
styles?.sidebar
|
|
485
|
+
),
|
|
486
|
+
style: { width },
|
|
487
|
+
children: [
|
|
488
|
+
/* @__PURE__ */ jsx(SurfaceBackground, { component: layout.backgroundComponents.sidebar }),
|
|
489
|
+
/* @__PURE__ */ jsxs("div", { className: cn("relative z-10 flex min-h-0 flex-1 flex-col", styles?.sidebarInner), children: [
|
|
490
|
+
/* @__PURE__ */ jsxs(
|
|
491
|
+
"div",
|
|
492
|
+
{
|
|
493
|
+
className: cn(
|
|
494
|
+
"flex h-16 shrink-0 items-center gap-3 border-b border-[hsl(var(--cx-sidebar-foreground)/0.12)] px-4",
|
|
495
|
+
collapsed && !mobile && "justify-center px-2",
|
|
496
|
+
styles?.sidebarHeader
|
|
497
|
+
),
|
|
498
|
+
children: [
|
|
499
|
+
/* @__PURE__ */ jsx("div", { className: cn("min-w-0 flex-1", effectiveCollapsed && !mobile && "sr-only", styles?.sidebarLogo), children: sidebarHeader ?? logo }),
|
|
500
|
+
sidebar.collapsible && !mobile ? /* @__PURE__ */ jsx(
|
|
501
|
+
"button",
|
|
502
|
+
{
|
|
503
|
+
type: "button",
|
|
504
|
+
onClick: handleSidebarToggle,
|
|
505
|
+
className: cn(
|
|
506
|
+
"grid size-9 place-items-center rounded-lg text-sidebar-foreground/70 hover:bg-[hsl(var(--cx-sidebar-foreground)/0.08)] hover:text-sidebar-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
507
|
+
styles?.sidebarToggle
|
|
508
|
+
),
|
|
509
|
+
"aria-label": effectiveCollapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
510
|
+
children: effectiveCollapsed ? "\u203A" : "\u2039"
|
|
511
|
+
}
|
|
512
|
+
) : null
|
|
513
|
+
]
|
|
514
|
+
}
|
|
515
|
+
),
|
|
516
|
+
/* @__PURE__ */ jsx(
|
|
517
|
+
SidebarNav,
|
|
518
|
+
{
|
|
519
|
+
items,
|
|
520
|
+
collapsed: effectiveCollapsed && !mobile,
|
|
521
|
+
currentPath,
|
|
522
|
+
isItemActive,
|
|
523
|
+
closeMobile,
|
|
524
|
+
onExpandPersist: handleExpandPersist,
|
|
525
|
+
onCollapsedTooltipChange: sidebar.showTooltipsWhenCollapsed && effectiveCollapsed ? setCollapsedTooltip : void 0,
|
|
526
|
+
renderLink,
|
|
527
|
+
styles,
|
|
528
|
+
density
|
|
529
|
+
}
|
|
530
|
+
),
|
|
531
|
+
showBottomPanel && bottomPanelContent ? /* @__PURE__ */ jsxs(
|
|
532
|
+
"div",
|
|
533
|
+
{
|
|
534
|
+
className: cn(
|
|
535
|
+
bottomPanel.sticky !== false && "sticky bottom-0",
|
|
536
|
+
"shrink-0 overflow-hidden bg-transparent p-4",
|
|
537
|
+
bottomPanel.bordered !== false && "border-t border-[hsl(var(--cx-sidebar-foreground)/0.12)]",
|
|
538
|
+
effectiveCollapsed && !mobile && "px-2",
|
|
539
|
+
styles?.sidebarFooter,
|
|
540
|
+
bottomPanel.className
|
|
541
|
+
),
|
|
542
|
+
style: bottomPanelStyle,
|
|
543
|
+
children: [
|
|
544
|
+
!isSidebarCompact && (bottomPanel.title || bottomPanel.collapsible) ? /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between gap-3", children: [
|
|
545
|
+
bottomPanel.title ? /* @__PURE__ */ jsx("div", { className: "min-w-0 truncate font-semibold text-sidebar-foreground", children: bottomPanel.title }) : /* @__PURE__ */ jsx("span", {}),
|
|
546
|
+
bottomPanel.collapsible ? /* @__PURE__ */ jsx(
|
|
547
|
+
"button",
|
|
548
|
+
{
|
|
549
|
+
type: "button",
|
|
550
|
+
onClick: () => setBottomPanelCollapsed(!bottomPanelCollapsed),
|
|
551
|
+
className: "grid size-7 shrink-0 place-items-center rounded-md text-sidebar-foreground/70 hover:bg-[hsl(var(--cx-sidebar-foreground)/0.08)] hover:text-sidebar-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
552
|
+
"aria-expanded": !bottomPanelCollapsed,
|
|
553
|
+
"aria-label": bottomPanel.collapseButtonLabel ?? (bottomPanelCollapsed ? "Expand sidebar panel" : "Collapse sidebar panel"),
|
|
554
|
+
children: /* @__PURE__ */ jsx("span", { className: cn("transition-transform", bottomPanelCollapsed ? "-rotate-90" : "rotate-90"), children: "\u203A" })
|
|
555
|
+
}
|
|
556
|
+
) : null
|
|
557
|
+
] }) : null,
|
|
558
|
+
!isPanelBodyCollapsed ? /* @__PURE__ */ jsx("div", { className: "min-h-0 overflow-hidden", children: bottomPanelContent }) : null
|
|
559
|
+
]
|
|
560
|
+
}
|
|
561
|
+
) : null
|
|
562
|
+
] }),
|
|
563
|
+
sidebar.showTooltipsWhenCollapsed && effectiveCollapsed && collapsedTooltip ? /* @__PURE__ */ jsx(
|
|
564
|
+
"span",
|
|
565
|
+
{
|
|
566
|
+
className: "pointer-events-none fixed z-[70] -translate-y-1/2 whitespace-nowrap rounded-md bg-foreground px-2 py-1 text-xs text-background shadow-lg",
|
|
567
|
+
style: { top: collapsedTooltip.top, left: collapsedTooltip.left },
|
|
568
|
+
children: collapsedTooltip.label
|
|
569
|
+
}
|
|
570
|
+
) : null
|
|
571
|
+
]
|
|
572
|
+
}
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
function ActionButton({ action, styles }) {
|
|
576
|
+
const Icon = action.icon;
|
|
577
|
+
const isIcon = action.type === "icon";
|
|
578
|
+
return /* @__PURE__ */ jsxs(
|
|
579
|
+
"button",
|
|
580
|
+
{
|
|
581
|
+
type: "button",
|
|
582
|
+
onClick: action.onClick,
|
|
583
|
+
className: cn(
|
|
584
|
+
"relative inline-flex items-center justify-center gap-2 rounded-lg border border-border bg-background text-sm font-medium text-foreground transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
585
|
+
isIcon ? "size-9" : "h-9 px-3",
|
|
586
|
+
action.type === "button" && action.variant === "solid" && "border-primary bg-primary text-primary-foreground hover:bg-primary/90",
|
|
587
|
+
action.type === "button" && action.variant === "ghost" && "border-transparent bg-transparent hover:bg-muted",
|
|
588
|
+
action.type === "button" && action.variant === "soft" && "border-transparent bg-primary/10 text-primary hover:bg-primary/15",
|
|
589
|
+
action.type === "button" && action.tone === "danger" && "border-danger text-danger hover:bg-danger/10",
|
|
590
|
+
styles?.headerActionButton
|
|
591
|
+
),
|
|
592
|
+
"aria-label": action.label,
|
|
593
|
+
children: [
|
|
594
|
+
Icon ? /* @__PURE__ */ jsx(Icon, { className: "size-4", "aria-hidden": true }) : null,
|
|
595
|
+
!isIcon ? /* @__PURE__ */ jsx("span", { children: action.label }) : null,
|
|
596
|
+
isIcon && action.badge ? /* @__PURE__ */ jsx("span", { className: "absolute -right-1 -top-1 rounded-full bg-danger px-1.5 text-[10px] font-semibold text-white", children: action.badge }) : null
|
|
597
|
+
]
|
|
598
|
+
}
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
function DropdownAction({ action, styles }) {
|
|
602
|
+
const [open, setOpen] = useState(false);
|
|
603
|
+
const Icon = action.icon;
|
|
604
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
605
|
+
/* @__PURE__ */ jsxs(
|
|
606
|
+
"button",
|
|
607
|
+
{
|
|
608
|
+
type: "button",
|
|
609
|
+
onClick: () => setOpen((value) => !value),
|
|
610
|
+
"aria-expanded": open,
|
|
611
|
+
className: cn("inline-flex h-9 items-center gap-2 rounded-lg border border-border bg-background px-3 text-sm font-medium hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", styles?.headerActionButton),
|
|
612
|
+
children: [
|
|
613
|
+
Icon ? /* @__PURE__ */ jsx(Icon, { className: "size-4", "aria-hidden": true }) : null,
|
|
614
|
+
action.label
|
|
615
|
+
]
|
|
616
|
+
}
|
|
617
|
+
),
|
|
618
|
+
open ? /* @__PURE__ */ jsx("div", { className: "absolute right-0 top-11 z-50 w-52 rounded-lg border border-border bg-background p-1 shadow-lg", children: action.items.map((item) => {
|
|
619
|
+
const ItemIcon = item.icon;
|
|
620
|
+
return /* @__PURE__ */ jsxs(
|
|
621
|
+
"button",
|
|
622
|
+
{
|
|
623
|
+
type: "button",
|
|
624
|
+
onClick: () => {
|
|
625
|
+
item.onClick?.();
|
|
626
|
+
setOpen(false);
|
|
627
|
+
},
|
|
628
|
+
className: "flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm hover:bg-muted",
|
|
629
|
+
children: [
|
|
630
|
+
ItemIcon ? /* @__PURE__ */ jsx(ItemIcon, { className: "size-4", "aria-hidden": true }) : null,
|
|
631
|
+
item.label
|
|
632
|
+
]
|
|
633
|
+
},
|
|
634
|
+
item.label
|
|
635
|
+
);
|
|
636
|
+
}) }) : null
|
|
637
|
+
] });
|
|
638
|
+
}
|
|
639
|
+
function HeaderActions({
|
|
640
|
+
actions,
|
|
641
|
+
headerActions,
|
|
642
|
+
styles
|
|
643
|
+
}) {
|
|
644
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", styles?.headerActions), children: [
|
|
645
|
+
actions?.map((action, index) => {
|
|
646
|
+
if (action.type === "custom") {
|
|
647
|
+
return /* @__PURE__ */ jsx(Fragment, { children: action.render }, index);
|
|
648
|
+
}
|
|
649
|
+
if (action.type === "dropdown") {
|
|
650
|
+
return /* @__PURE__ */ jsx(DropdownAction, { action, styles }, `${action.label}-${index}`);
|
|
651
|
+
}
|
|
652
|
+
return /* @__PURE__ */ jsx(ActionButton, { action, styles }, `${action.label}-${index}`);
|
|
653
|
+
}),
|
|
654
|
+
headerActions
|
|
655
|
+
] });
|
|
656
|
+
}
|
|
657
|
+
function DashboardBreadcrumbs({
|
|
658
|
+
breadcrumbs,
|
|
659
|
+
renderLink,
|
|
660
|
+
styles
|
|
661
|
+
}) {
|
|
662
|
+
if (!breadcrumbs?.length) {
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
return /* @__PURE__ */ jsx("nav", { className: cn("flex min-w-0 items-center gap-1 overflow-x-auto whitespace-nowrap text-sm text-muted-foreground", styles?.headerBreadcrumbs), "aria-label": "Breadcrumbs", children: breadcrumbs.map((item, index) => {
|
|
666
|
+
const isLast = index === breadcrumbs.length - 1;
|
|
667
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
668
|
+
index > 0 ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "/" }) : null,
|
|
669
|
+
item.href && !isLast ? renderLink({ href: item.href, className: "hover:text-foreground", children: item.label }) : /* @__PURE__ */ jsx("span", { className: cn(isLast && "text-foreground"), children: item.label })
|
|
670
|
+
] }, `${item.label}-${index}`);
|
|
671
|
+
}) });
|
|
672
|
+
}
|
|
673
|
+
function DashboardUserMenu({
|
|
674
|
+
user,
|
|
675
|
+
config,
|
|
676
|
+
styles,
|
|
677
|
+
placement = "bottom",
|
|
678
|
+
triggerClassName,
|
|
679
|
+
menuClassName
|
|
680
|
+
}) {
|
|
681
|
+
const [open, setOpen] = useState(false);
|
|
682
|
+
const [avatarFailed, setAvatarFailed] = useState(false);
|
|
683
|
+
const initials = user?.initials ?? user?.name?.split(" ").map((part) => part[0]).join("").slice(0, 2).toUpperCase() ?? "U";
|
|
684
|
+
const UserIcon = user?.icon;
|
|
685
|
+
const userMenuConfig = {
|
|
686
|
+
bordered: false,
|
|
687
|
+
background: "transparent",
|
|
688
|
+
radius: "lg",
|
|
689
|
+
padding: "sm",
|
|
690
|
+
shadow: "none",
|
|
691
|
+
showRole: true,
|
|
692
|
+
...config
|
|
693
|
+
};
|
|
694
|
+
if (!user) {
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("relative", styles?.headerUserMenu), children: [
|
|
698
|
+
/* @__PURE__ */ jsxs(
|
|
699
|
+
"button",
|
|
700
|
+
{
|
|
701
|
+
type: "button",
|
|
702
|
+
onClick: () => setOpen((value) => !value),
|
|
703
|
+
"aria-expanded": open,
|
|
704
|
+
className: cn(
|
|
705
|
+
"flex items-center gap-2 transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
706
|
+
userMenuConfig.bordered && "border border-border",
|
|
707
|
+
userMenuBackgroundClasses[userMenuConfig.background],
|
|
708
|
+
userMenuRadiusClasses[userMenuConfig.radius],
|
|
709
|
+
userMenuPaddingClasses[userMenuConfig.padding],
|
|
710
|
+
userMenuShadowClasses[userMenuConfig.shadow],
|
|
711
|
+
triggerClassName
|
|
712
|
+
),
|
|
713
|
+
children: [
|
|
714
|
+
user.avatarUrl && !avatarFailed ? /* @__PURE__ */ jsx(
|
|
715
|
+
"img",
|
|
716
|
+
{
|
|
717
|
+
src: user.avatarUrl,
|
|
718
|
+
alt: "",
|
|
719
|
+
className: "size-8 rounded-full object-cover",
|
|
720
|
+
onError: () => setAvatarFailed(true)
|
|
721
|
+
}
|
|
722
|
+
) : /* @__PURE__ */ jsx("span", { className: "grid size-8 place-items-center rounded-full bg-primary text-xs font-semibold text-primary-foreground", children: initials }),
|
|
723
|
+
/* @__PURE__ */ jsxs("span", { className: "hidden min-w-0 text-left lg:block", children: [
|
|
724
|
+
/* @__PURE__ */ jsxs("span", { className: "flex max-w-36 items-center gap-1.5", children: [
|
|
725
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium", children: user.name }),
|
|
726
|
+
UserIcon ? /* @__PURE__ */ jsx(UserIcon, { className: "size-3.5 shrink-0 text-primary", "aria-hidden": true }) : null
|
|
727
|
+
] }),
|
|
728
|
+
userMenuConfig.showRole ? /* @__PURE__ */ jsx("span", { className: "block max-w-36 truncate text-xs text-muted-foreground", children: user.role ?? user.email }) : null
|
|
729
|
+
] })
|
|
730
|
+
]
|
|
731
|
+
}
|
|
732
|
+
),
|
|
733
|
+
open ? /* @__PURE__ */ jsxs(
|
|
734
|
+
"div",
|
|
735
|
+
{
|
|
736
|
+
className: cn(
|
|
737
|
+
"z-50 w-64 rounded-lg border border-border bg-background p-2 shadow-lg",
|
|
738
|
+
placement === "top" ? "absolute right-0 bottom-12" : "absolute right-0 top-12",
|
|
739
|
+
menuClassName
|
|
740
|
+
),
|
|
741
|
+
children: [
|
|
742
|
+
/* @__PURE__ */ jsxs("div", { className: "px-3 py-2", children: [
|
|
743
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: user.name }),
|
|
744
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
|
|
745
|
+
] }),
|
|
746
|
+
/* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
|
|
747
|
+
user.menuItems?.map((item, index) => {
|
|
748
|
+
if (item.type === "divider") {
|
|
749
|
+
return /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }, index);
|
|
750
|
+
}
|
|
751
|
+
const Icon = item.icon;
|
|
752
|
+
return item.href ? /* @__PURE__ */ jsxs("a", { href: item.href, className: "flex items-center gap-2 rounded-md px-3 py-2 text-sm hover:bg-muted", children: [
|
|
753
|
+
Icon ? /* @__PURE__ */ jsx(Icon, { className: "size-4", "aria-hidden": true }) : null,
|
|
754
|
+
item.label
|
|
755
|
+
] }, item.label) : /* @__PURE__ */ jsxs(
|
|
756
|
+
"button",
|
|
757
|
+
{
|
|
758
|
+
type: "button",
|
|
759
|
+
onClick: () => {
|
|
760
|
+
item.onClick?.();
|
|
761
|
+
setOpen(false);
|
|
762
|
+
},
|
|
763
|
+
className: "flex w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm hover:bg-muted",
|
|
764
|
+
children: [
|
|
765
|
+
Icon ? /* @__PURE__ */ jsx(Icon, { className: "size-4", "aria-hidden": true }) : null,
|
|
766
|
+
item.label
|
|
767
|
+
]
|
|
768
|
+
},
|
|
769
|
+
item.label
|
|
770
|
+
);
|
|
771
|
+
})
|
|
772
|
+
]
|
|
773
|
+
}
|
|
774
|
+
) : null
|
|
775
|
+
] });
|
|
776
|
+
}
|
|
777
|
+
function DashboardHeader({
|
|
778
|
+
header,
|
|
779
|
+
user,
|
|
780
|
+
mobile,
|
|
781
|
+
mobileOpen,
|
|
782
|
+
setMobileOpen,
|
|
783
|
+
renderLink,
|
|
784
|
+
headerActions,
|
|
785
|
+
styles,
|
|
786
|
+
density,
|
|
787
|
+
layout,
|
|
788
|
+
breadcrumbsVisible
|
|
789
|
+
}) {
|
|
790
|
+
const heightStyle = header?.height ? { height: header.height } : void 0;
|
|
791
|
+
const showMobileBottomNav = Boolean(mobile?.bottomNav);
|
|
792
|
+
const showMobileHeaderToggle = header?.showSidebarToggle !== false && !showMobileBottomNav;
|
|
793
|
+
return /* @__PURE__ */ jsxs(
|
|
794
|
+
"header",
|
|
795
|
+
{
|
|
796
|
+
className: cn(
|
|
797
|
+
"relative z-30 backdrop-blur-xl",
|
|
798
|
+
resolveSurfaceBackgroundClass({
|
|
799
|
+
value: layout.background.header,
|
|
800
|
+
hasComponent: Boolean(layout.backgroundComponents.header)
|
|
801
|
+
}),
|
|
802
|
+
layout.borders.header && "border-b border-border",
|
|
803
|
+
header?.sticky !== false && "sticky top-0",
|
|
804
|
+
styles?.header
|
|
805
|
+
),
|
|
806
|
+
children: [
|
|
807
|
+
/* @__PURE__ */ jsx(SurfaceBackground, { component: layout.backgroundComponents.header }),
|
|
808
|
+
/* @__PURE__ */ jsxs(
|
|
809
|
+
"div",
|
|
810
|
+
{
|
|
811
|
+
className: cn("relative z-10 flex items-center justify-between gap-4 px-4 lg:px-6", densityClasses[density].header, styles?.headerInner),
|
|
812
|
+
style: heightStyle,
|
|
813
|
+
children: [
|
|
814
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 items-center gap-3", styles?.headerLeft), children: [
|
|
815
|
+
showMobileHeaderToggle ? /* @__PURE__ */ jsx(
|
|
816
|
+
"button",
|
|
817
|
+
{
|
|
818
|
+
type: "button",
|
|
819
|
+
onClick: () => setMobileOpen(!mobileOpen),
|
|
820
|
+
className: "grid size-9 place-items-center rounded-lg border border-border bg-background text-foreground hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring lg:hidden",
|
|
821
|
+
"aria-label": "Open navigation",
|
|
822
|
+
"aria-expanded": mobileOpen,
|
|
823
|
+
children: "\u2630"
|
|
824
|
+
}
|
|
825
|
+
) : null,
|
|
826
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
827
|
+
header?.title ? /* @__PURE__ */ jsx("h1", { className: cn("truncate text-lg font-semibold", styles?.headerTitle), children: header.title }) : null,
|
|
828
|
+
header?.subtitle ? /* @__PURE__ */ jsx("p", { className: cn("truncate text-sm text-muted-foreground", styles?.headerSubtitle), children: header.subtitle }) : null
|
|
829
|
+
] })
|
|
830
|
+
] }),
|
|
831
|
+
/* @__PURE__ */ jsxs("div", { className: "hidden shrink-0 items-center gap-2 lg:flex", children: [
|
|
832
|
+
/* @__PURE__ */ jsx(HeaderActions, { actions: header?.actions, headerActions, styles }),
|
|
833
|
+
/* @__PURE__ */ jsx(DashboardUserMenu, { user, config: header?.userMenu, styles })
|
|
834
|
+
] })
|
|
835
|
+
]
|
|
836
|
+
}
|
|
837
|
+
),
|
|
838
|
+
header?.showBreadcrumbs ? /* @__PURE__ */ jsxs(
|
|
839
|
+
"div",
|
|
840
|
+
{
|
|
841
|
+
className: cn(
|
|
842
|
+
"relative overflow-hidden border-t border-border/70 transition-all duration-200 ease-out motion-reduce:transition-none",
|
|
843
|
+
resolveSurfaceBackgroundClass({
|
|
844
|
+
value: layout.background.breadcrumb,
|
|
845
|
+
hasComponent: Boolean(layout.backgroundComponents.breadcrumb)
|
|
846
|
+
}),
|
|
847
|
+
breadcrumbsVisible ? "max-h-12 opacity-100 translate-y-0" : "max-h-0 -translate-y-1 opacity-0"
|
|
848
|
+
),
|
|
849
|
+
"aria-hidden": !breadcrumbsVisible,
|
|
850
|
+
children: [
|
|
851
|
+
/* @__PURE__ */ jsx(SurfaceBackground, { component: layout.backgroundComponents.breadcrumb }),
|
|
852
|
+
/* @__PURE__ */ jsx("div", { className: "relative z-10 px-4 py-2 lg:px-6", children: /* @__PURE__ */ jsx(DashboardBreadcrumbs, { breadcrumbs: header.breadcrumbs, renderLink, styles }) })
|
|
853
|
+
]
|
|
854
|
+
}
|
|
855
|
+
) : null
|
|
856
|
+
]
|
|
857
|
+
}
|
|
858
|
+
);
|
|
859
|
+
}
|
|
860
|
+
function DashboardContent({
|
|
861
|
+
children,
|
|
862
|
+
content,
|
|
863
|
+
styles,
|
|
864
|
+
density,
|
|
865
|
+
layout
|
|
866
|
+
}) {
|
|
867
|
+
const padded = content?.padded !== false;
|
|
868
|
+
const maxWidth = content?.maxWidth ?? "none";
|
|
869
|
+
return /* @__PURE__ */ jsxs(
|
|
870
|
+
"div",
|
|
871
|
+
{
|
|
872
|
+
className: cn(
|
|
873
|
+
"relative",
|
|
874
|
+
resolveSurfaceBackgroundClass({
|
|
875
|
+
value: layout.background.main,
|
|
876
|
+
hasComponent: Boolean(layout.backgroundComponents.main)
|
|
877
|
+
}),
|
|
878
|
+
resolveSurfaceBackgroundClass({
|
|
879
|
+
value: layout.background.content,
|
|
880
|
+
hasComponent: Boolean(layout.backgroundComponents.content)
|
|
881
|
+
}),
|
|
882
|
+
padded && densityClasses[density].content,
|
|
883
|
+
styles?.content
|
|
884
|
+
),
|
|
885
|
+
children: [
|
|
886
|
+
/* @__PURE__ */ jsx(SurfaceBackground, { component: layout.backgroundComponents.main }),
|
|
887
|
+
/* @__PURE__ */ jsx(SurfaceBackground, { component: layout.backgroundComponents.content }),
|
|
888
|
+
/* @__PURE__ */ jsx(
|
|
889
|
+
"div",
|
|
890
|
+
{
|
|
891
|
+
className: cn(
|
|
892
|
+
"relative z-10",
|
|
893
|
+
maxWidthClasses[maxWidth],
|
|
894
|
+
content?.centered && "mx-auto",
|
|
895
|
+
styles?.pageContainer
|
|
896
|
+
),
|
|
897
|
+
children
|
|
898
|
+
}
|
|
899
|
+
)
|
|
900
|
+
]
|
|
901
|
+
}
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
function MobileDrawer({
|
|
905
|
+
open,
|
|
906
|
+
setOpen,
|
|
907
|
+
sidebarProps,
|
|
908
|
+
styles
|
|
909
|
+
}) {
|
|
910
|
+
const panelRef = useRef(null);
|
|
911
|
+
const [rendered, setRendered] = useState(open);
|
|
912
|
+
useEffect(() => {
|
|
913
|
+
if (open) {
|
|
914
|
+
setRendered(true);
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
917
|
+
const timeoutId = window.setTimeout(() => {
|
|
918
|
+
setRendered(false);
|
|
919
|
+
}, 220);
|
|
920
|
+
return () => window.clearTimeout(timeoutId);
|
|
921
|
+
}, [open]);
|
|
922
|
+
useEffect(() => {
|
|
923
|
+
if (!open) {
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
const handleKeyDown = (event) => {
|
|
927
|
+
if (event.key === "Escape") {
|
|
928
|
+
setOpen(false);
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
932
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
933
|
+
}, [open, setOpen]);
|
|
934
|
+
if (!rendered) {
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 lg:hidden", role: "dialog", "aria-modal": "true", children: [
|
|
938
|
+
/* @__PURE__ */ jsx(
|
|
939
|
+
"button",
|
|
940
|
+
{
|
|
941
|
+
className: cn(
|
|
942
|
+
"absolute inset-0 bg-black/50 transition-opacity duration-200 ease-out motion-reduce:transition-none",
|
|
943
|
+
open ? "opacity-100" : "opacity-0"
|
|
944
|
+
),
|
|
945
|
+
type: "button",
|
|
946
|
+
"aria-label": "Close navigation",
|
|
947
|
+
onClick: () => setOpen(false)
|
|
948
|
+
}
|
|
949
|
+
),
|
|
950
|
+
/* @__PURE__ */ jsx(
|
|
951
|
+
"div",
|
|
952
|
+
{
|
|
953
|
+
ref: panelRef,
|
|
954
|
+
className: cn(
|
|
955
|
+
"absolute inset-y-0 left-0 flex max-w-[86vw] shadow-2xl transition-transform duration-200 ease-out motion-reduce:transition-none",
|
|
956
|
+
open ? "translate-x-0" : "-translate-x-full",
|
|
957
|
+
styles?.mobileDrawer
|
|
958
|
+
),
|
|
959
|
+
children: /* @__PURE__ */ jsx(DashboardSidebar, { ...sidebarProps, mobile: true, closeMobile: () => setOpen(false), collapsed: false })
|
|
960
|
+
}
|
|
961
|
+
)
|
|
962
|
+
] });
|
|
963
|
+
}
|
|
964
|
+
function MobileBottomNav({
|
|
965
|
+
items,
|
|
966
|
+
currentPath,
|
|
967
|
+
isItemActive,
|
|
968
|
+
renderLink,
|
|
969
|
+
user,
|
|
970
|
+
userMenuConfig,
|
|
971
|
+
mobileOpen,
|
|
972
|
+
setMobileOpen,
|
|
973
|
+
utilityItems,
|
|
974
|
+
styles,
|
|
975
|
+
layout
|
|
976
|
+
}) {
|
|
977
|
+
const resolvedUtilityItems = utilityItems?.length ? utilityItems : [{ type: "sidebar-toggle" }, { type: "user-menu" }];
|
|
978
|
+
if (!items?.length && !resolvedUtilityItems.length) {
|
|
979
|
+
return null;
|
|
980
|
+
}
|
|
981
|
+
const mobileUserMenuConfig = {
|
|
982
|
+
...userMenuConfig,
|
|
983
|
+
bordered: false,
|
|
984
|
+
background: "transparent",
|
|
985
|
+
padding: "none",
|
|
986
|
+
shadow: "none",
|
|
987
|
+
showRole: false
|
|
988
|
+
};
|
|
989
|
+
const itemBaseClassName = "group flex h-full min-h-[4.5rem] w-full min-w-[4.75rem] flex-col items-center justify-center gap-1 px-2.5 py-2 text-[0.7rem] font-medium leading-none transition-colors";
|
|
990
|
+
const itemShellClassName = "relative flex min-w-[4.75rem] flex-1 basis-0 items-stretch before:absolute before:bottom-3 before:left-0 before:top-3 before:w-px before:bg-border/70 before:content-[''] first:before:hidden";
|
|
991
|
+
const iconWrapClassName = "grid size-6 place-items-center text-current transition-transform duration-200 motion-reduce:transition-none";
|
|
992
|
+
return /* @__PURE__ */ jsxs(
|
|
993
|
+
"nav",
|
|
994
|
+
{
|
|
995
|
+
className: cn(
|
|
996
|
+
"fixed inset-x-0 bottom-0 z-40 pb-[env(safe-area-inset-bottom)] lg:hidden",
|
|
997
|
+
resolveSurfaceBackgroundClass({
|
|
998
|
+
value: layout.background.mobileBottomNav,
|
|
999
|
+
hasComponent: Boolean(layout.backgroundComponents.mobileBottomNav)
|
|
1000
|
+
}),
|
|
1001
|
+
styles?.mobileBottomNav
|
|
1002
|
+
),
|
|
1003
|
+
"aria-label": "Mobile navigation",
|
|
1004
|
+
children: [
|
|
1005
|
+
/* @__PURE__ */ jsx(SurfaceBackground, { component: layout.backgroundComponents.mobileBottomNav }),
|
|
1006
|
+
/* @__PURE__ */ jsx("div", { className: "relative z-10 w-full", children: /* @__PURE__ */ jsx(
|
|
1007
|
+
"div",
|
|
1008
|
+
{
|
|
1009
|
+
className: cn(
|
|
1010
|
+
"flex min-h-[5.25rem] w-full items-stretch bg-background/95 backdrop-blur-2xl",
|
|
1011
|
+
layout.borders.mobileBottomNav && "border-t border-border/80"
|
|
1012
|
+
),
|
|
1013
|
+
children: /* @__PURE__ */ jsxs("ul", { className: "flex min-w-0 flex-1 items-stretch overflow-x-auto overflow-y-hidden", children: [
|
|
1014
|
+
items?.map((item) => {
|
|
1015
|
+
const active = isItemActive(item, currentPath);
|
|
1016
|
+
const Icon = item.icon;
|
|
1017
|
+
return /* @__PURE__ */ jsx("li", { className: itemShellClassName, children: renderLink({
|
|
1018
|
+
href: item.href,
|
|
1019
|
+
external: item.external,
|
|
1020
|
+
"aria-current": active ? "page" : void 0,
|
|
1021
|
+
className: cn(
|
|
1022
|
+
itemBaseClassName,
|
|
1023
|
+
active ? "text-primary" : "text-muted-foreground hover:text-foreground"
|
|
1024
|
+
),
|
|
1025
|
+
children: /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
1026
|
+
/* @__PURE__ */ jsx(
|
|
1027
|
+
"span",
|
|
1028
|
+
{
|
|
1029
|
+
className: cn(
|
|
1030
|
+
"h-0.5 w-8 rounded-full transition-all duration-200 motion-reduce:transition-none",
|
|
1031
|
+
active ? "bg-primary opacity-100" : "bg-transparent opacity-0"
|
|
1032
|
+
)
|
|
1033
|
+
}
|
|
1034
|
+
),
|
|
1035
|
+
Icon ? /* @__PURE__ */ jsx("span", { className: cn(iconWrapClassName, active && "-translate-y-px"), "aria-hidden": true, children: /* @__PURE__ */ jsx(Icon, { className: "size-[1.3rem]", "aria-hidden": true }) }) : /* @__PURE__ */ jsx("span", { className: cn(iconWrapClassName, active && "-translate-y-px"), "aria-hidden": true, children: /* @__PURE__ */ jsx("span", { className: "size-2.5 rounded-full bg-current" }) }),
|
|
1036
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-16 truncate leading-none", children: item.label })
|
|
1037
|
+
] })
|
|
1038
|
+
}) }, item.href);
|
|
1039
|
+
}),
|
|
1040
|
+
resolvedUtilityItems.map((item, index) => {
|
|
1041
|
+
if (item.type === "custom") {
|
|
1042
|
+
return /* @__PURE__ */ jsx("li", { className: itemShellClassName, children: item.render }, item.key);
|
|
1043
|
+
}
|
|
1044
|
+
if (item.type === "user-menu") {
|
|
1045
|
+
return user ? /* @__PURE__ */ jsx("li", { className: itemShellClassName, children: /* @__PURE__ */ jsx(
|
|
1046
|
+
DashboardUserMenu,
|
|
1047
|
+
{
|
|
1048
|
+
user,
|
|
1049
|
+
config: mobileUserMenuConfig,
|
|
1050
|
+
styles,
|
|
1051
|
+
placement: "top",
|
|
1052
|
+
menuClassName: "fixed bottom-[calc(env(safe-area-inset-bottom)+6.25rem)] right-3 z-[90] w-[min(16rem,calc(100vw-1.5rem))] max-w-[calc(100vw-1.5rem)] rounded-2xl border border-border/80 bg-background/98 shadow-[0_24px_60px_-24px_rgba(15,23,42,0.55)] backdrop-blur-xl",
|
|
1053
|
+
triggerClassName: cn(
|
|
1054
|
+
itemBaseClassName,
|
|
1055
|
+
"text-foreground hover:text-foreground"
|
|
1056
|
+
)
|
|
1057
|
+
}
|
|
1058
|
+
) }, `mobile-user-${index}`) : null;
|
|
1059
|
+
}
|
|
1060
|
+
const Icon = item.icon;
|
|
1061
|
+
return /* @__PURE__ */ jsx("li", { className: itemShellClassName, children: /* @__PURE__ */ jsxs(
|
|
1062
|
+
"button",
|
|
1063
|
+
{
|
|
1064
|
+
type: "button",
|
|
1065
|
+
onClick: () => setMobileOpen(!mobileOpen),
|
|
1066
|
+
className: cn(
|
|
1067
|
+
itemBaseClassName,
|
|
1068
|
+
"text-foreground hover:text-foreground"
|
|
1069
|
+
),
|
|
1070
|
+
"aria-label": item.label ?? "Open navigation",
|
|
1071
|
+
"aria-expanded": mobileOpen,
|
|
1072
|
+
children: [
|
|
1073
|
+
/* @__PURE__ */ jsx("span", { className: iconWrapClassName, "aria-hidden": true, children: Icon ? /* @__PURE__ */ jsx(Icon, { className: "size-[1.3rem]", "aria-hidden": true }) : "\u2630" }),
|
|
1074
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-16 truncate leading-none", children: item.label ?? "Menu" })
|
|
1075
|
+
]
|
|
1076
|
+
}
|
|
1077
|
+
) }, `mobile-sidebar-toggle-${index}`);
|
|
1078
|
+
})
|
|
1079
|
+
] })
|
|
1080
|
+
}
|
|
1081
|
+
) })
|
|
1082
|
+
]
|
|
1083
|
+
}
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
function DashboardLayout({
|
|
1087
|
+
children,
|
|
1088
|
+
logo,
|
|
1089
|
+
sidebarItems = [],
|
|
1090
|
+
currentPath,
|
|
1091
|
+
isItemActive = defaultIsItemActive,
|
|
1092
|
+
renderLink = DefaultLink,
|
|
1093
|
+
user,
|
|
1094
|
+
header,
|
|
1095
|
+
sidebar: sidebarConfig,
|
|
1096
|
+
mobile,
|
|
1097
|
+
content,
|
|
1098
|
+
layout: layoutConfig,
|
|
1099
|
+
headerActions,
|
|
1100
|
+
sidebarBottomPanel,
|
|
1101
|
+
sidebarFooter,
|
|
1102
|
+
sidebarHeader,
|
|
1103
|
+
footer,
|
|
1104
|
+
variant = "classic",
|
|
1105
|
+
density = "comfortable",
|
|
1106
|
+
styles,
|
|
1107
|
+
className,
|
|
1108
|
+
style
|
|
1109
|
+
}) {
|
|
1110
|
+
const theme = useTheme();
|
|
1111
|
+
const themeStyles = getThemeStyles(theme.layout?.DashboardLayout);
|
|
1112
|
+
const mergedStyles = useMemo(() => ({ ...themeStyles, ...styles }), [themeStyles, styles]);
|
|
1113
|
+
const layout = useMemo(
|
|
1114
|
+
() => ({
|
|
1115
|
+
...defaultLayoutConfig,
|
|
1116
|
+
...layoutConfig,
|
|
1117
|
+
borders: {
|
|
1118
|
+
...defaultLayoutConfig.borders,
|
|
1119
|
+
...layoutConfig?.borders
|
|
1120
|
+
},
|
|
1121
|
+
background: {
|
|
1122
|
+
...defaultLayoutConfig.background,
|
|
1123
|
+
...layoutConfig?.background
|
|
1124
|
+
},
|
|
1125
|
+
backgroundComponents: {
|
|
1126
|
+
...defaultLayoutConfig.backgroundComponents,
|
|
1127
|
+
...layoutConfig?.backgroundComponents
|
|
1128
|
+
}
|
|
1129
|
+
}),
|
|
1130
|
+
[layoutConfig]
|
|
1131
|
+
);
|
|
1132
|
+
const sidebar = { ...defaultSidebar, ...sidebarConfig };
|
|
1133
|
+
const [collapsed, setCollapsed] = useControllableState({
|
|
1134
|
+
value: sidebarConfig?.collapsed,
|
|
1135
|
+
defaultValue: sidebar.defaultCollapsed,
|
|
1136
|
+
onChange: sidebarConfig?.onCollapsedChange
|
|
1137
|
+
});
|
|
1138
|
+
const [mobileOpen, setMobileOpen] = useState(false);
|
|
1139
|
+
const [breadcrumbsVisible, setBreadcrumbsVisible] = useState(true);
|
|
1140
|
+
const lastMainScrollTopRef = useRef(0);
|
|
1141
|
+
const breadcrumbScrollIntentRef = useRef(0);
|
|
1142
|
+
const mobileOpenRef = useRef(mobileOpen);
|
|
1143
|
+
const mobileDrawerHistoryEntryActiveRef = useRef(false);
|
|
1144
|
+
const mobileDrawerClosingFromHistoryRef = useRef(false);
|
|
1145
|
+
const skipNextDrawerPopstateRef = useRef(false);
|
|
1146
|
+
const showBottomNav = Boolean(mobile?.bottomNav && (mobile.bottomNavItems?.length || mobile.utilityItems?.length));
|
|
1147
|
+
const breadcrumbBehavior = header?.breadcrumbBehavior ?? "hide-on-scroll";
|
|
1148
|
+
useEffect(() => {
|
|
1149
|
+
mobileOpenRef.current = mobileOpen;
|
|
1150
|
+
}, [mobileOpen]);
|
|
1151
|
+
useEffect(() => {
|
|
1152
|
+
setBreadcrumbsVisible(true);
|
|
1153
|
+
lastMainScrollTopRef.current = 0;
|
|
1154
|
+
breadcrumbScrollIntentRef.current = 0;
|
|
1155
|
+
}, [currentPath]);
|
|
1156
|
+
useEffect(() => {
|
|
1157
|
+
if (mobile?.drawer === false || typeof window === "undefined") {
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
const handlePopState = () => {
|
|
1161
|
+
if (skipNextDrawerPopstateRef.current) {
|
|
1162
|
+
skipNextDrawerPopstateRef.current = false;
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
if (mobileDrawerHistoryEntryActiveRef.current && mobileOpenRef.current) {
|
|
1166
|
+
mobileDrawerClosingFromHistoryRef.current = true;
|
|
1167
|
+
mobileDrawerHistoryEntryActiveRef.current = false;
|
|
1168
|
+
setMobileOpen(false);
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
window.addEventListener("popstate", handlePopState);
|
|
1172
|
+
return () => window.removeEventListener("popstate", handlePopState);
|
|
1173
|
+
}, [mobile?.drawer]);
|
|
1174
|
+
useEffect(() => {
|
|
1175
|
+
if (mobile?.drawer === false || typeof window === "undefined") {
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
if (mobileOpen) {
|
|
1179
|
+
if (!mobileDrawerHistoryEntryActiveRef.current) {
|
|
1180
|
+
window.history.pushState({ ...window.history.state ?? {}, __clayerMobileDrawer: true }, "", window.location.href);
|
|
1181
|
+
mobileDrawerHistoryEntryActiveRef.current = true;
|
|
1182
|
+
}
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
if (mobileDrawerClosingFromHistoryRef.current) {
|
|
1186
|
+
mobileDrawerClosingFromHistoryRef.current = false;
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
if (mobileDrawerHistoryEntryActiveRef.current) {
|
|
1190
|
+
mobileDrawerHistoryEntryActiveRef.current = false;
|
|
1191
|
+
skipNextDrawerPopstateRef.current = true;
|
|
1192
|
+
window.history.back();
|
|
1193
|
+
}
|
|
1194
|
+
}, [mobile?.drawer, mobileOpen]);
|
|
1195
|
+
const handleMainScroll = useCallback(
|
|
1196
|
+
(event) => {
|
|
1197
|
+
if (!header?.showBreadcrumbs || breadcrumbBehavior === "static") {
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
const nextScrollTop = event.currentTarget.scrollTop;
|
|
1201
|
+
const maxScrollTop = Math.max(0, event.currentTarget.scrollHeight - event.currentTarget.clientHeight);
|
|
1202
|
+
const previousScrollTop = lastMainScrollTopRef.current;
|
|
1203
|
+
const delta = nextScrollTop - previousScrollTop;
|
|
1204
|
+
const clampedNextScrollTop = Math.max(0, nextScrollTop);
|
|
1205
|
+
const distanceFromBottom = Math.max(0, maxScrollTop - clampedNextScrollTop);
|
|
1206
|
+
if (Math.abs(delta) < 2) {
|
|
1207
|
+
lastMainScrollTopRef.current = clampedNextScrollTop;
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
if (clampedNextScrollTop < 16) {
|
|
1211
|
+
if (!breadcrumbsVisible) {
|
|
1212
|
+
setBreadcrumbsVisible(true);
|
|
1213
|
+
}
|
|
1214
|
+
lastMainScrollTopRef.current = clampedNextScrollTop;
|
|
1215
|
+
breadcrumbScrollIntentRef.current = 0;
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
const isSameDirection = Math.sign(delta) === Math.sign(breadcrumbScrollIntentRef.current) || breadcrumbScrollIntentRef.current === 0;
|
|
1219
|
+
breadcrumbScrollIntentRef.current = isSameDirection ? breadcrumbScrollIntentRef.current + delta : delta;
|
|
1220
|
+
const hideThreshold = 18;
|
|
1221
|
+
const showThreshold = 14;
|
|
1222
|
+
const minHideScrollTop = 56;
|
|
1223
|
+
const showResetScrollTop = 28;
|
|
1224
|
+
const bottomIgnoreZone = 24;
|
|
1225
|
+
if (distanceFromBottom < bottomIgnoreZone && delta > 0) {
|
|
1226
|
+
lastMainScrollTopRef.current = clampedNextScrollTop;
|
|
1227
|
+
breadcrumbScrollIntentRef.current = Math.max(0, breadcrumbScrollIntentRef.current);
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
if (breadcrumbsVisible && clampedNextScrollTop > minHideScrollTop && breadcrumbScrollIntentRef.current > hideThreshold) {
|
|
1231
|
+
setBreadcrumbsVisible(false);
|
|
1232
|
+
breadcrumbScrollIntentRef.current = 0;
|
|
1233
|
+
} else if (!breadcrumbsVisible && (clampedNextScrollTop < showResetScrollTop || breadcrumbScrollIntentRef.current < -showThreshold)) {
|
|
1234
|
+
setBreadcrumbsVisible(true);
|
|
1235
|
+
breadcrumbScrollIntentRef.current = 0;
|
|
1236
|
+
}
|
|
1237
|
+
lastMainScrollTopRef.current = clampedNextScrollTop;
|
|
1238
|
+
},
|
|
1239
|
+
[breadcrumbBehavior, breadcrumbsVisible, header?.showBreadcrumbs]
|
|
1240
|
+
);
|
|
1241
|
+
const sidebarProps = {
|
|
1242
|
+
logo,
|
|
1243
|
+
sidebarHeader,
|
|
1244
|
+
sidebarBottomPanel,
|
|
1245
|
+
sidebarFooter,
|
|
1246
|
+
items: sidebarItems,
|
|
1247
|
+
collapsed,
|
|
1248
|
+
onCollapsedChange: setCollapsed,
|
|
1249
|
+
sidebar,
|
|
1250
|
+
currentPath,
|
|
1251
|
+
isItemActive,
|
|
1252
|
+
renderLink,
|
|
1253
|
+
styles: mergedStyles,
|
|
1254
|
+
density,
|
|
1255
|
+
layout
|
|
1256
|
+
};
|
|
1257
|
+
return /* @__PURE__ */ jsxs(
|
|
1258
|
+
"div",
|
|
1259
|
+
{
|
|
1260
|
+
className: cn(
|
|
1261
|
+
"relative h-screen overflow-hidden text-foreground",
|
|
1262
|
+
resolveSurfaceBackgroundClass({
|
|
1263
|
+
value: layout.background.root,
|
|
1264
|
+
hasComponent: Boolean(layout.backgroundComponents.root)
|
|
1265
|
+
}),
|
|
1266
|
+
layoutPaddingClasses[layout.padding],
|
|
1267
|
+
mergedStyles.root,
|
|
1268
|
+
className
|
|
1269
|
+
),
|
|
1270
|
+
style,
|
|
1271
|
+
children: [
|
|
1272
|
+
/* @__PURE__ */ jsx(SurfaceBackground, { component: layout.backgroundComponents.root }),
|
|
1273
|
+
/* @__PURE__ */ jsxs(
|
|
1274
|
+
"div",
|
|
1275
|
+
{
|
|
1276
|
+
className: cn(
|
|
1277
|
+
"relative z-10 flex h-full min-h-0 overflow-hidden",
|
|
1278
|
+
resolveSurfaceBackgroundClass({
|
|
1279
|
+
value: layout.background.shell,
|
|
1280
|
+
hasComponent: Boolean(layout.backgroundComponents.shell)
|
|
1281
|
+
}),
|
|
1282
|
+
layoutRadiusClasses[layout.radius],
|
|
1283
|
+
layoutShadowClasses[layout.shadow],
|
|
1284
|
+
layout.bordered && "border border-border",
|
|
1285
|
+
variant === "floating" && !layoutConfig && "rounded-none lg:rounded-xl lg:border lg:border-border",
|
|
1286
|
+
mergedStyles.shell
|
|
1287
|
+
),
|
|
1288
|
+
children: [
|
|
1289
|
+
/* @__PURE__ */ jsx(SurfaceBackground, { component: layout.backgroundComponents.shell }),
|
|
1290
|
+
/* @__PURE__ */ jsx(DashboardSidebar, { ...sidebarProps }),
|
|
1291
|
+
/* @__PURE__ */ jsxs("div", { className: cn("relative z-10 flex min-h-0 min-w-0 flex-1 flex-col", mergedStyles.main), children: [
|
|
1292
|
+
mobile?.showHeader !== false ? /* @__PURE__ */ jsx(
|
|
1293
|
+
DashboardHeader,
|
|
1294
|
+
{
|
|
1295
|
+
header,
|
|
1296
|
+
user,
|
|
1297
|
+
mobile,
|
|
1298
|
+
mobileOpen,
|
|
1299
|
+
setMobileOpen,
|
|
1300
|
+
renderLink,
|
|
1301
|
+
headerActions,
|
|
1302
|
+
styles: mergedStyles,
|
|
1303
|
+
density,
|
|
1304
|
+
layout,
|
|
1305
|
+
breadcrumbsVisible: breadcrumbBehavior === "static" ? true : breadcrumbsVisible
|
|
1306
|
+
}
|
|
1307
|
+
) : null,
|
|
1308
|
+
/* @__PURE__ */ jsx(
|
|
1309
|
+
"main",
|
|
1310
|
+
{
|
|
1311
|
+
className: cn("relative min-h-0 min-w-0 flex-1 overflow-y-auto", showBottomNav && "pb-20 lg:pb-0", mergedStyles.main),
|
|
1312
|
+
onScroll: handleMainScroll,
|
|
1313
|
+
children: /* @__PURE__ */ jsx("div", { className: "relative z-10", children: /* @__PURE__ */ jsx(DashboardContent, { content, styles: mergedStyles, density, layout, children }) })
|
|
1314
|
+
}
|
|
1315
|
+
),
|
|
1316
|
+
footer ? /* @__PURE__ */ jsxs(
|
|
1317
|
+
"footer",
|
|
1318
|
+
{
|
|
1319
|
+
className: cn(
|
|
1320
|
+
"relative px-4 py-3",
|
|
1321
|
+
resolveSurfaceBackgroundClass({
|
|
1322
|
+
value: layout.background.footer,
|
|
1323
|
+
hasComponent: Boolean(layout.backgroundComponents.footer)
|
|
1324
|
+
}),
|
|
1325
|
+
layout.borders.footer && "border-t border-border",
|
|
1326
|
+
mergedStyles.footer
|
|
1327
|
+
),
|
|
1328
|
+
children: [
|
|
1329
|
+
/* @__PURE__ */ jsx(SurfaceBackground, { component: layout.backgroundComponents.footer }),
|
|
1330
|
+
/* @__PURE__ */ jsx("div", { className: "relative z-10", children: footer })
|
|
1331
|
+
]
|
|
1332
|
+
}
|
|
1333
|
+
) : null
|
|
1334
|
+
] })
|
|
1335
|
+
]
|
|
1336
|
+
}
|
|
1337
|
+
),
|
|
1338
|
+
mobile?.drawer !== false ? /* @__PURE__ */ jsx(MobileDrawer, { open: mobileOpen, setOpen: setMobileOpen, sidebarProps, styles: mergedStyles }) : null,
|
|
1339
|
+
mobile?.bottomNav ? /* @__PURE__ */ jsx(
|
|
1340
|
+
MobileBottomNav,
|
|
1341
|
+
{
|
|
1342
|
+
items: mobile.bottomNavItems,
|
|
1343
|
+
currentPath,
|
|
1344
|
+
isItemActive,
|
|
1345
|
+
renderLink,
|
|
1346
|
+
user,
|
|
1347
|
+
userMenuConfig: header?.userMenu,
|
|
1348
|
+
mobileOpen,
|
|
1349
|
+
setMobileOpen,
|
|
1350
|
+
utilityItems: mobile.utilityItems,
|
|
1351
|
+
styles: mergedStyles,
|
|
1352
|
+
layout
|
|
1353
|
+
}
|
|
1354
|
+
) : null
|
|
1355
|
+
]
|
|
1356
|
+
}
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
function DashboardShell({ className, ...props }) {
|
|
1360
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex min-h-screen overflow-hidden bg-inherit text-foreground", className), ...props });
|
|
1361
|
+
}
|
|
1362
|
+
function DashboardMain({ className, ...props }) {
|
|
1363
|
+
return /* @__PURE__ */ jsx("main", { className: cn("min-w-0 flex-1 overflow-y-auto bg-transparent", className), ...props });
|
|
1364
|
+
}
|
|
1365
|
+
function DashboardFooter({ className, ...props }) {
|
|
1366
|
+
return /* @__PURE__ */ jsx("footer", { className: cn("border-t border-border bg-transparent px-4 py-3", className), ...props });
|
|
1367
|
+
}
|
|
1368
|
+
function DashboardActionGroup({ className, ...props }) {
|
|
1369
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-2", className), ...props });
|
|
1370
|
+
}
|
|
1371
|
+
function SidebarNavItem({ className, ...props }) {
|
|
1372
|
+
return /* @__PURE__ */ jsx("li", { className: cn("list-none", className), ...props });
|
|
1373
|
+
}
|
|
1374
|
+
function SidebarNavGroup({ className, ...props }) {
|
|
1375
|
+
return /* @__PURE__ */ jsx("li", { className: cn("list-none space-y-1", className), ...props });
|
|
1376
|
+
}
|
|
1377
|
+
export {
|
|
1378
|
+
DashboardActionGroup,
|
|
1379
|
+
DashboardBreadcrumbs,
|
|
1380
|
+
DashboardContent,
|
|
1381
|
+
DashboardFooter,
|
|
1382
|
+
DashboardHeader,
|
|
1383
|
+
DashboardLayout,
|
|
1384
|
+
DashboardMain,
|
|
1385
|
+
MobileBottomNav as DashboardMobileNav,
|
|
1386
|
+
DashboardShell,
|
|
1387
|
+
DashboardSidebar,
|
|
1388
|
+
DashboardUserMenu,
|
|
1389
|
+
SidebarNav,
|
|
1390
|
+
SidebarNavGroup,
|
|
1391
|
+
SidebarNavItem
|
|
1392
|
+
};
|
|
1393
|
+
//# sourceMappingURL=index.js.map
|