@fayz-ai/ui 0.11.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics/AnalyticsCard.d.ts.map +1 -1
- package/dist/analytics/AnalyticsControls.d.ts +5 -1
- package/dist/analytics/AnalyticsControls.d.ts.map +1 -1
- package/dist/analytics/derive.d.ts.map +1 -1
- package/dist/{chunk-JGJNRNQW.js → chunk-2EFRFYGF.js} +117 -41
- package/dist/chunk-2EFRFYGF.js.map +1 -0
- package/dist/{chunk-7BMXN7MP.js → chunk-46UZAKFJ.js} +24 -24
- package/dist/chunk-46UZAKFJ.js.map +1 -0
- package/dist/chunk-5KAGNW45.js +152 -0
- package/dist/chunk-5KAGNW45.js.map +1 -0
- package/dist/{chunk-XRDCYOND.js → chunk-77CUIMOI.js} +289 -166
- package/dist/chunk-77CUIMOI.js.map +1 -0
- package/dist/chunk-LFZFBN3P.js +142 -0
- package/dist/chunk-LFZFBN3P.js.map +1 -0
- package/dist/{chunk-QSHA2Q27.js → chunk-MVH64JFG.js} +1049 -771
- package/dist/chunk-MVH64JFG.js.map +1 -0
- package/dist/{chunk-22EVFMS3.js → chunk-UZYXFEQS.js} +4 -3
- package/dist/chunk-UZYXFEQS.js.map +1 -0
- package/dist/dashboard/DashboardCanvas.d.ts.map +1 -1
- package/dist/dashboard/DashboardCustomizeMenu.d.ts.map +1 -1
- package/dist/dashboard/DashboardRange.d.ts +8 -2
- package/dist/dashboard/DashboardRange.d.ts.map +1 -1
- package/dist/dashboard/index.js +4 -5
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +72 -98
- package/dist/index.js.map +1 -1
- package/dist/layout/AppShell.d.ts +1 -0
- package/dist/layout/AppShell.d.ts.map +1 -1
- package/dist/layout/AppShellSkeleton.d.ts +1 -1
- package/dist/layout/ModulePage.d.ts +24 -1
- package/dist/layout/ModulePage.d.ts.map +1 -1
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/index.d.ts +2 -1
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +4 -5
- package/dist/layout/rail-flyout.d.ts +10 -0
- package/dist/layout/rail-flyout.d.ts.map +1 -1
- package/dist/primitives/bulk-action-bar.d.ts +3 -1
- package/dist/primitives/bulk-action-bar.d.ts.map +1 -1
- package/dist/primitives/header-tabs.d.ts +36 -6
- package/dist/primitives/header-tabs.d.ts.map +1 -1
- package/dist/primitives/index.d.ts +3 -1
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +5 -4
- package/dist/primitives/list-view.d.ts +3 -1
- package/dist/primitives/list-view.d.ts.map +1 -1
- package/dist/primitives/page-toolbar.d.ts +61 -0
- package/dist/primitives/page-toolbar.d.ts.map +1 -0
- package/dist/primitives/refresh-button.d.ts +18 -0
- package/dist/primitives/refresh-button.d.ts.map +1 -0
- package/dist/primitives/search-select.d.ts +3 -1
- package/dist/primitives/search-select.d.ts.map +1 -1
- package/dist/primitives/tabs.d.ts.map +1 -1
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.d.ts.map +1 -1
- package/dist/stores/page-header.store.d.ts +26 -2
- package/dist/stores/page-header.store.d.ts.map +1 -1
- package/dist/styles.css +224 -0
- package/package.json +5 -5
- package/dist/chunk-22EVFMS3.js.map +0 -1
- package/dist/chunk-7BMXN7MP.js.map +0 -1
- package/dist/chunk-JGJNRNQW.js.map +0 -1
- package/dist/chunk-OKZ6WQB3.js +0 -57
- package/dist/chunk-OKZ6WQB3.js.map +0 -1
- package/dist/chunk-QSHA2Q27.js.map +0 -1
- package/dist/chunk-VAKWUUVC.js +0 -425
- package/dist/chunk-VAKWUUVC.js.map +0 -1
- package/dist/chunk-XRDCYOND.js.map +0 -1
- package/dist/chunk-ZBQ772DU.js +0 -17
- package/dist/chunk-ZBQ772DU.js.map +0 -1
|
@@ -1,40 +1,720 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import { cn, useHasPageHeaderHost, usePageHeader, useLayoutStore, useRegisterPageActions, useHasPageHeaderBack, usePageHeaderSlot, useHasPageHeaderOutlet, useHasPageActions, usePageHeaderHost } from './chunk-LFZFBN3P.js';
|
|
2
|
+
import { ICON_MAP } from './chunk-UZYXFEQS.js';
|
|
3
|
+
import * as React7 from 'react';
|
|
4
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
5
|
+
import { X, Loader2, ArrowLeft, Menu, Search, Crown, PanelLeftClose, PanelLeft, Bell, Repeat, GraduationCap, Newspaper, Clapperboard, Zap, Star, UserCheck, Inbox, Clock3, Ban, Map, ListPlus, CalendarX, CalendarCheck2, Mic, UsersRound, Disc3, ListMusic, Eye, Music, Layers, Banknote, BadgeDollarSign, CalendarDays, Radio, PartyPopper, FileCheck2, Box, Boxes, TreePalm, UserX, UserPlus, ArrowDownRight, ArrowUpRight, Ruler, Warehouse, Wallet, Sparkles, AlertTriangle, Clock, CircleDollarSign, TrendingUp, Receipt, Landmark, TableProperties, SlidersHorizontal, Plug, ArrowLeftRight, ArrowUpCircle, ArrowDownCircle, Wheat, Egg, Apple, LeafyGreen, LayoutTemplate, Heart, PawPrint, Cat, Dog, FolderOpen, ListChecks, List, Shield, Plus, Filter, ChevronDown, Building2, Contact, Handshake, MapPin, UtensilsCrossed, Camera, Tags, Tag, Percent, BookOpenCheck, Globe, MessageCircle, BookOpen, UserCog, Briefcase, ClipboardCheck, ClipboardList, Wrench, Target, ShoppingBag, ShoppingCart, Megaphone, DollarSign, Mail, FileText, BarChart3, Activity, Package, CalendarClock, Calendar, CreditCard, Settings, Users, Home, ChevronRight, ChevronLeft, ExternalLink, PiggyBank, LogOut } from 'lucide-react';
|
|
6
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
import { useTranslation, useNavReferrer, routeModule, navHistoryBack } from '@fayz-ai/core';
|
|
8
8
|
import * as Popover from '@radix-ui/react-popover';
|
|
9
9
|
import { createPortal } from 'react-dom';
|
|
10
|
-
import { useTranslation } from '@fayz-ai/core';
|
|
11
10
|
|
|
11
|
+
var STYLE_ID = "saas-sheet-styles";
|
|
12
|
+
if (typeof document !== "undefined" && !document.getElementById(STYLE_ID)) {
|
|
13
|
+
const style = document.createElement("style");
|
|
14
|
+
style.id = STYLE_ID;
|
|
15
|
+
style.textContent = `
|
|
16
|
+
@keyframes saas-sheet-fade-in { from { opacity: 0 } to { opacity: 1 } }
|
|
17
|
+
@keyframes saas-sheet-fade-out { from { opacity: 1 } to { opacity: 0 } }
|
|
18
|
+
@keyframes saas-sheet-slide-in { from { transform: translateX(100%) } to { transform: translateX(0) } }
|
|
19
|
+
@keyframes saas-sheet-slide-out { from { transform: translateX(0) } to { transform: translateX(100%) } }
|
|
20
|
+
.saas-sheet-overlay[data-state="open"] { animation: saas-sheet-fade-in 200ms ease-out }
|
|
21
|
+
.saas-sheet-overlay[data-state="closed"] { animation: saas-sheet-fade-out 150ms ease-in }
|
|
22
|
+
.saas-sheet-content[data-state="open"] { animation: saas-sheet-slide-in 280ms cubic-bezier(0.16, 1, 0.3, 1) }
|
|
23
|
+
.saas-sheet-content[data-state="closed"] { animation: saas-sheet-slide-out 200ms ease-in }
|
|
24
|
+
`;
|
|
25
|
+
document.head.appendChild(style);
|
|
26
|
+
}
|
|
27
|
+
var Sheet = DialogPrimitive.Root;
|
|
28
|
+
var SheetTrigger = DialogPrimitive.Trigger;
|
|
29
|
+
var SheetClose = DialogPrimitive.Close;
|
|
30
|
+
var SheetPortal = DialogPrimitive.Portal;
|
|
31
|
+
var SheetOverlay = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
32
|
+
DialogPrimitive.Overlay,
|
|
33
|
+
{
|
|
34
|
+
ref,
|
|
35
|
+
className: cn(
|
|
36
|
+
"saas-sheet-overlay fixed inset-0 z-50 bg-black/40 backdrop-blur-[2px]",
|
|
37
|
+
className
|
|
38
|
+
),
|
|
39
|
+
...props
|
|
40
|
+
}
|
|
41
|
+
));
|
|
42
|
+
SheetOverlay.displayName = "SheetOverlay";
|
|
43
|
+
var SheetContent = React7.forwardRef(({ className, children, width, hideClose, overlay = "blur", ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [
|
|
44
|
+
/* @__PURE__ */ jsx(SheetOverlay, { className: overlay === "none" ? "bg-transparent backdrop-blur-0" : overlay === "dim" ? "backdrop-blur-0" : void 0 }),
|
|
45
|
+
/* @__PURE__ */ jsxs(
|
|
46
|
+
DialogPrimitive.Content,
|
|
47
|
+
{
|
|
48
|
+
ref,
|
|
49
|
+
className: cn(
|
|
50
|
+
"fayz-glass-surface saas-sheet-content fixed inset-y-0 right-0 z-50 flex flex-col border-l bg-card shadow-2xl outline-none",
|
|
51
|
+
"w-full",
|
|
52
|
+
width ?? "max-w-md",
|
|
53
|
+
className
|
|
54
|
+
),
|
|
55
|
+
...props,
|
|
56
|
+
children: [
|
|
57
|
+
children,
|
|
58
|
+
!hideClose && /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 z-10 flex h-7 w-7 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring", children: [
|
|
59
|
+
/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
|
|
60
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
61
|
+
] })
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
] }));
|
|
66
|
+
SheetContent.displayName = "SheetContent";
|
|
67
|
+
function SheetHeader({ className, ...props }) {
|
|
68
|
+
return /* @__PURE__ */ jsx(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
className: cn("flex flex-col gap-1 px-5 py-4 border-b shrink-0", className),
|
|
72
|
+
...props
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
SheetHeader.displayName = "SheetHeader";
|
|
77
|
+
function SheetFooter({ className, ...props }) {
|
|
78
|
+
return /* @__PURE__ */ jsx(
|
|
79
|
+
"div",
|
|
80
|
+
{
|
|
81
|
+
className: cn("flex items-center gap-2 px-5 py-3 border-t shrink-0", className),
|
|
82
|
+
...props
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
SheetFooter.displayName = "SheetFooter";
|
|
87
|
+
var SheetTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
88
|
+
DialogPrimitive.Title,
|
|
89
|
+
{
|
|
90
|
+
ref,
|
|
91
|
+
className: cn("text-sm font-semibold leading-none tracking-tight", className),
|
|
92
|
+
...props
|
|
93
|
+
}
|
|
94
|
+
));
|
|
95
|
+
SheetTitle.displayName = "SheetTitle";
|
|
96
|
+
var SheetDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
97
|
+
DialogPrimitive.Description,
|
|
98
|
+
{
|
|
99
|
+
ref,
|
|
100
|
+
className: cn("text-xs text-muted-foreground", className),
|
|
101
|
+
...props
|
|
102
|
+
}
|
|
103
|
+
));
|
|
104
|
+
SheetDescription.displayName = "SheetDescription";
|
|
105
|
+
function SheetBody({ className, ...props }) {
|
|
106
|
+
return /* @__PURE__ */ jsx(
|
|
107
|
+
"div",
|
|
108
|
+
{
|
|
109
|
+
className: cn("flex-1 overflow-y-auto px-5 py-5", className),
|
|
110
|
+
...props
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
SheetBody.displayName = "SheetBody";
|
|
115
|
+
var SaveBarApiContext = React7.createContext(null);
|
|
116
|
+
var SaveBarStateContext = React7.createContext({ visible: false, saving: false });
|
|
117
|
+
var _saveBarVisible = false;
|
|
118
|
+
var _saveBarSubs = /* @__PURE__ */ new Set();
|
|
119
|
+
function setSaveBarVisibleSignal(v) {
|
|
120
|
+
if (v === _saveBarVisible) return;
|
|
121
|
+
_saveBarVisible = v;
|
|
122
|
+
_saveBarSubs.forEach((fn) => fn());
|
|
123
|
+
}
|
|
124
|
+
function useSaveBarVisible() {
|
|
125
|
+
return React7.useSyncExternalStore(
|
|
126
|
+
(cb) => {
|
|
127
|
+
_saveBarSubs.add(cb);
|
|
128
|
+
return () => {
|
|
129
|
+
_saveBarSubs.delete(cb);
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
() => _saveBarVisible,
|
|
133
|
+
() => false
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
function SaveBarProvider({ children }) {
|
|
137
|
+
const [state, setState] = React7.useState({ visible: false, saving: false });
|
|
138
|
+
const handlers = React7.useRef({});
|
|
139
|
+
const api = React7.useMemo(() => ({ setState, handlers }), []);
|
|
140
|
+
const backStack = React7.useRef([]);
|
|
141
|
+
const backApi = React7.useMemo(() => ({ stack: backStack }), []);
|
|
142
|
+
React7.useEffect(() => {
|
|
143
|
+
setSaveBarVisibleSignal(state.visible);
|
|
144
|
+
}, [state.visible]);
|
|
145
|
+
React7.useEffect(() => {
|
|
146
|
+
function onKey(e) {
|
|
147
|
+
if (state.visible && !state.saving) {
|
|
148
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
149
|
+
e.preventDefault();
|
|
150
|
+
handlers.current.onSave?.();
|
|
151
|
+
} else if (e.key === "Escape") {
|
|
152
|
+
e.preventDefault();
|
|
153
|
+
handlers.current.onDiscard?.();
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (e.key === "Escape" && !e.defaultPrevented && !isEditableTarget(e.target)) {
|
|
158
|
+
for (let i = backStack.current.length - 1; i >= 0; i--) {
|
|
159
|
+
const back = backStack.current[i].current;
|
|
160
|
+
if (back) {
|
|
161
|
+
e.preventDefault();
|
|
162
|
+
back();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
window.addEventListener("keydown", onKey);
|
|
169
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
170
|
+
}, [state.visible, state.saving]);
|
|
171
|
+
return /* @__PURE__ */ jsx(SaveBarApiContext.Provider, { value: api, children: /* @__PURE__ */ jsx(BackNavApiContext.Provider, { value: backApi, children: /* @__PURE__ */ jsx(SaveBarStateContext.Provider, { value: state, children }) }) });
|
|
172
|
+
}
|
|
173
|
+
var BackNavApiContext = React7.createContext(null);
|
|
174
|
+
function isEditableTarget(target) {
|
|
175
|
+
const el = target;
|
|
176
|
+
if (!el || !el.tagName) return false;
|
|
177
|
+
const tag = el.tagName.toLowerCase();
|
|
178
|
+
return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
|
|
179
|
+
}
|
|
180
|
+
function useBackHandler(onBack) {
|
|
181
|
+
const api = React7.useContext(BackNavApiContext);
|
|
182
|
+
const ref = React7.useRef(onBack);
|
|
183
|
+
React7.useEffect(() => {
|
|
184
|
+
ref.current = onBack;
|
|
185
|
+
});
|
|
186
|
+
React7.useEffect(() => {
|
|
187
|
+
if (!api) return;
|
|
188
|
+
const stack = api.stack.current;
|
|
189
|
+
stack.push(ref);
|
|
190
|
+
return () => {
|
|
191
|
+
const i = stack.indexOf(ref);
|
|
192
|
+
if (i !== -1) stack.splice(i, 1);
|
|
193
|
+
};
|
|
194
|
+
}, [api]);
|
|
195
|
+
}
|
|
196
|
+
function useSaveBar({ dirty, saving = false, onSave, onDiscard, message, saveLabel, discardLabel }) {
|
|
197
|
+
const api = React7.useContext(SaveBarApiContext);
|
|
198
|
+
React7.useEffect(() => {
|
|
199
|
+
if (api) api.handlers.current = { onSave, onDiscard };
|
|
200
|
+
});
|
|
201
|
+
React7.useEffect(() => {
|
|
202
|
+
if (!api) return;
|
|
203
|
+
api.setState({ visible: dirty, saving, message, saveLabel, discardLabel });
|
|
204
|
+
return () => api.setState((s) => ({ ...s, visible: false }));
|
|
205
|
+
}, [api, dirty, saving, message, saveLabel, discardLabel]);
|
|
206
|
+
}
|
|
207
|
+
function SaveBar({ className }) {
|
|
208
|
+
const state = React7.useContext(SaveBarStateContext);
|
|
209
|
+
const api = React7.useContext(SaveBarApiContext);
|
|
210
|
+
const t = useTranslation();
|
|
211
|
+
const [rendered, setRendered] = React7.useState(state.visible);
|
|
212
|
+
const [closing, setClosing] = React7.useState(false);
|
|
213
|
+
React7.useEffect(() => {
|
|
214
|
+
if (state.visible) {
|
|
215
|
+
setRendered(true);
|
|
216
|
+
setClosing(false);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
if (!rendered) return;
|
|
220
|
+
setClosing(true);
|
|
221
|
+
const id = setTimeout(() => {
|
|
222
|
+
setRendered(false);
|
|
223
|
+
setClosing(false);
|
|
224
|
+
}, 200);
|
|
225
|
+
return () => clearTimeout(id);
|
|
226
|
+
}, [state.visible, rendered]);
|
|
227
|
+
if (!api || !rendered) return null;
|
|
228
|
+
const { saving, message, saveLabel, discardLabel } = state;
|
|
229
|
+
return /* @__PURE__ */ jsx("div", { className: cn("pointer-events-none fixed inset-x-0 bottom-5 z-40 flex justify-center px-4", className), children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
230
|
+
"pointer-events-auto flex items-center gap-5 rounded-full bg-foreground py-2 pl-5 pr-2 text-background shadow-xl ring-1 ring-black/5 duration-200",
|
|
231
|
+
closing ? "animate-out fade-out slide-out-to-bottom-2" : "animate-in fade-in slide-in-from-bottom-2"
|
|
232
|
+
), children: [
|
|
233
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
|
|
234
|
+
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400" }),
|
|
235
|
+
message ?? t("saveBar.unsaved")
|
|
236
|
+
] }),
|
|
237
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
238
|
+
/* @__PURE__ */ jsx(
|
|
239
|
+
"button",
|
|
240
|
+
{
|
|
241
|
+
type: "button",
|
|
242
|
+
disabled: saving,
|
|
243
|
+
onClick: () => api.handlers.current.onDiscard?.(),
|
|
244
|
+
className: "rounded-full px-3 py-1.5 text-sm font-medium text-background/70 transition-colors hover:bg-background/10 hover:text-background disabled:opacity-50",
|
|
245
|
+
children: discardLabel ?? t("common.discard")
|
|
246
|
+
}
|
|
247
|
+
),
|
|
248
|
+
/* @__PURE__ */ jsxs(
|
|
249
|
+
"button",
|
|
250
|
+
{
|
|
251
|
+
type: "button",
|
|
252
|
+
disabled: saving,
|
|
253
|
+
onClick: () => api.handlers.current.onSave?.(),
|
|
254
|
+
className: "inline-flex items-center rounded-full bg-background px-4 py-1.5 text-sm font-semibold text-foreground transition-colors hover:bg-background/90 disabled:opacity-60",
|
|
255
|
+
children: [
|
|
256
|
+
saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
|
|
257
|
+
saveLabel ?? t("common.save")
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
] })
|
|
262
|
+
] }) });
|
|
263
|
+
}
|
|
264
|
+
function useCrossModuleBack(onBack, parentLabel) {
|
|
265
|
+
const currentPath = typeof window !== "undefined" ? (window.location.hash ? window.location.hash.slice(1) : window.location.pathname) || "/" : "/";
|
|
266
|
+
const referrer = useNavReferrer(currentPath);
|
|
267
|
+
const cross = referrer && referrer.label && routeModule(referrer.path) !== routeModule(currentPath) ? referrer : null;
|
|
268
|
+
return {
|
|
269
|
+
onBack: cross ? () => navHistoryBack(onBack) : onBack,
|
|
270
|
+
parentLabel: cross ? cross.label : parentLabel,
|
|
271
|
+
isCrossModule: !!cross
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function PageHeaderCrumb({
|
|
275
|
+
title,
|
|
276
|
+
parentLabel,
|
|
277
|
+
onBack
|
|
278
|
+
}) {
|
|
279
|
+
if (!onBack) {
|
|
280
|
+
return title ? /* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate text-lg font-semibold", children: title }) : null;
|
|
281
|
+
}
|
|
282
|
+
return /* @__PURE__ */ jsxs("nav", { className: "flex min-w-0 items-center gap-1.5 text-sm text-muted-foreground", children: [
|
|
283
|
+
/* @__PURE__ */ jsxs(
|
|
284
|
+
"button",
|
|
285
|
+
{
|
|
286
|
+
type: "button",
|
|
287
|
+
onClick: onBack,
|
|
288
|
+
className: "inline-flex shrink-0 items-center gap-1 transition-colors hover:text-foreground",
|
|
289
|
+
children: [
|
|
290
|
+
/* @__PURE__ */ jsx(ArrowLeft, { className: "h-3.5 w-3.5" }),
|
|
291
|
+
parentLabel ?? "Voltar"
|
|
292
|
+
]
|
|
293
|
+
}
|
|
294
|
+
),
|
|
295
|
+
title != null && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
296
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0", children: "/" }),
|
|
297
|
+
title === "" ? (
|
|
298
|
+
// Title still waiting on data: a pulsing bar, otherwise the line
|
|
299
|
+
// appears from nowhere and shoves the rest when the record lands.
|
|
300
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block h-4 w-24 animate-pulse rounded bg-muted align-middle" })
|
|
301
|
+
) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-foreground", children: title })
|
|
302
|
+
] })
|
|
303
|
+
] });
|
|
304
|
+
}
|
|
305
|
+
function PageHeader({ title, onBack, parentLabel, backLabel }) {
|
|
306
|
+
const hasHost = useHasPageHeaderHost();
|
|
307
|
+
const { onBack: effectiveOnBack, parentLabel: effectiveParent } = useCrossModuleBack(onBack, parentLabel);
|
|
308
|
+
useBackHandler(effectiveOnBack);
|
|
309
|
+
const node = React7.useMemo(() => /* @__PURE__ */ jsx(
|
|
310
|
+
PageHeaderCrumb,
|
|
311
|
+
{
|
|
312
|
+
title,
|
|
313
|
+
parentLabel: effectiveParent ?? backLabel,
|
|
314
|
+
onBack: effectiveOnBack
|
|
315
|
+
}
|
|
316
|
+
), [effectiveOnBack, effectiveParent, backLabel, title]);
|
|
317
|
+
usePageHeader(hasHost ? node : null, { back: !!effectiveOnBack });
|
|
318
|
+
if (hasHost) return null;
|
|
319
|
+
return /* @__PURE__ */ jsx("div", { className: "mb-4", children: node });
|
|
320
|
+
}
|
|
321
|
+
function TopbarUserMenu({
|
|
322
|
+
user,
|
|
323
|
+
userPlan,
|
|
324
|
+
billingLabel = "Subscription",
|
|
325
|
+
onSignOut,
|
|
326
|
+
onProfile,
|
|
327
|
+
onSettings,
|
|
328
|
+
onBilling,
|
|
329
|
+
userMenuExtras
|
|
330
|
+
}) {
|
|
331
|
+
const [open, setOpen] = React7.useState(false);
|
|
332
|
+
if (!user) return null;
|
|
333
|
+
const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
|
|
334
|
+
const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
|
|
335
|
+
"button",
|
|
336
|
+
{
|
|
337
|
+
onClick: () => {
|
|
338
|
+
setOpen(false);
|
|
339
|
+
onClick();
|
|
340
|
+
},
|
|
341
|
+
className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
|
|
342
|
+
children: [
|
|
343
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground" }),
|
|
344
|
+
/* @__PURE__ */ jsx("span", { children: label })
|
|
345
|
+
]
|
|
346
|
+
},
|
|
347
|
+
label
|
|
348
|
+
) : null;
|
|
349
|
+
return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
|
|
350
|
+
/* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
351
|
+
"button",
|
|
352
|
+
{
|
|
353
|
+
"aria-label": "User menu",
|
|
354
|
+
className: "ml-1 flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-primary text-xs font-semibold text-primary-foreground ring-2 ring-transparent transition hover:ring-sidebar-border",
|
|
355
|
+
children: user.avatarUrl ? (
|
|
356
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
357
|
+
/* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" })
|
|
358
|
+
) : initial
|
|
359
|
+
}
|
|
360
|
+
) }),
|
|
361
|
+
/* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(
|
|
362
|
+
Popover.Content,
|
|
363
|
+
{
|
|
364
|
+
align: "end",
|
|
365
|
+
sideOffset: 8,
|
|
366
|
+
className: "fayz-glass-surface z-50 min-w-[220px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95",
|
|
367
|
+
children: [
|
|
368
|
+
/* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
|
|
369
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: user.fullName || user.email }),
|
|
370
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
|
|
371
|
+
] }),
|
|
372
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5", children: [
|
|
373
|
+
item("Perfil", onProfile, UserCog),
|
|
374
|
+
item("Configura\xE7\xF5es", onSettings, Settings),
|
|
375
|
+
onBilling && /* @__PURE__ */ jsxs(
|
|
376
|
+
"button",
|
|
377
|
+
{
|
|
378
|
+
onClick: () => {
|
|
379
|
+
setOpen(false);
|
|
380
|
+
onBilling();
|
|
381
|
+
},
|
|
382
|
+
className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
|
|
383
|
+
children: [
|
|
384
|
+
/* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" }),
|
|
385
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: billingLabel }),
|
|
386
|
+
userPlan && /* @__PURE__ */ jsxs(
|
|
387
|
+
"span",
|
|
388
|
+
{
|
|
389
|
+
className: userPlan.paid ? "inline-flex items-center gap-1 rounded-full border border-amber-400/40 bg-amber-400/15 px-2 py-0.5 text-[10px] font-semibold text-amber-600 dark:text-amber-300" : "inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground",
|
|
390
|
+
children: [
|
|
391
|
+
userPlan.paid ? /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
|
|
392
|
+
userPlan.label
|
|
393
|
+
]
|
|
394
|
+
}
|
|
395
|
+
)
|
|
396
|
+
]
|
|
397
|
+
},
|
|
398
|
+
"billing"
|
|
399
|
+
),
|
|
400
|
+
userMenuExtras?.map((e) => item(e.label, e.onClick)),
|
|
401
|
+
onSignOut && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
402
|
+
/* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
|
|
403
|
+
item("Sair", onSignOut, LogOut)
|
|
404
|
+
] })
|
|
405
|
+
] })
|
|
406
|
+
]
|
|
407
|
+
}
|
|
408
|
+
) })
|
|
409
|
+
] });
|
|
410
|
+
}
|
|
411
|
+
var ICON_MAP2 = {
|
|
412
|
+
Home,
|
|
413
|
+
Users,
|
|
414
|
+
Settings,
|
|
415
|
+
CreditCard,
|
|
416
|
+
Bell,
|
|
417
|
+
Calendar,
|
|
418
|
+
CalendarClock,
|
|
419
|
+
Package,
|
|
420
|
+
Activity,
|
|
421
|
+
BarChart3,
|
|
422
|
+
FileText,
|
|
423
|
+
Mail,
|
|
424
|
+
DollarSign,
|
|
425
|
+
Megaphone,
|
|
426
|
+
ShoppingCart,
|
|
427
|
+
ShoppingBag,
|
|
428
|
+
Target,
|
|
429
|
+
Wrench,
|
|
430
|
+
ClipboardList,
|
|
431
|
+
ClipboardCheck,
|
|
432
|
+
Briefcase,
|
|
433
|
+
UserCog,
|
|
434
|
+
BookOpen,
|
|
435
|
+
MessageCircle,
|
|
436
|
+
Globe,
|
|
437
|
+
BookOpenCheck,
|
|
438
|
+
Percent,
|
|
439
|
+
Tag,
|
|
440
|
+
Tags,
|
|
441
|
+
Camera,
|
|
442
|
+
UtensilsCrossed,
|
|
443
|
+
Search,
|
|
444
|
+
MapPin,
|
|
445
|
+
Handshake,
|
|
446
|
+
Contact,
|
|
447
|
+
Building2,
|
|
448
|
+
ChevronDown,
|
|
449
|
+
Filter,
|
|
450
|
+
Plus,
|
|
451
|
+
Shield,
|
|
452
|
+
List,
|
|
453
|
+
ListChecks,
|
|
454
|
+
FolderOpen,
|
|
455
|
+
Dog,
|
|
456
|
+
Cat,
|
|
457
|
+
PawPrint,
|
|
458
|
+
Heart,
|
|
459
|
+
LayoutTemplate,
|
|
460
|
+
LeafyGreen,
|
|
461
|
+
Apple,
|
|
462
|
+
Egg,
|
|
463
|
+
Wheat,
|
|
464
|
+
ArrowDownCircle,
|
|
465
|
+
ArrowUpCircle,
|
|
466
|
+
ArrowLeftRight,
|
|
467
|
+
Plug,
|
|
468
|
+
SlidersHorizontal,
|
|
469
|
+
TableProperties,
|
|
470
|
+
Landmark,
|
|
471
|
+
Receipt,
|
|
472
|
+
TrendingUp,
|
|
473
|
+
CircleDollarSign,
|
|
474
|
+
Clock,
|
|
475
|
+
AlertTriangle,
|
|
476
|
+
Sparkles,
|
|
477
|
+
Wallet,
|
|
478
|
+
Warehouse,
|
|
479
|
+
Ruler,
|
|
480
|
+
ArrowUpRight,
|
|
481
|
+
ArrowDownRight,
|
|
482
|
+
UserPlus,
|
|
483
|
+
UserX,
|
|
484
|
+
TreePalm,
|
|
485
|
+
Boxes,
|
|
486
|
+
Box,
|
|
487
|
+
FileCheck2,
|
|
488
|
+
PartyPopper,
|
|
489
|
+
Radio,
|
|
490
|
+
CalendarDays,
|
|
491
|
+
BadgeDollarSign,
|
|
492
|
+
Banknote,
|
|
493
|
+
Layers,
|
|
494
|
+
Music,
|
|
495
|
+
Eye,
|
|
496
|
+
ListMusic,
|
|
497
|
+
Disc3,
|
|
498
|
+
UsersRound,
|
|
499
|
+
Mic,
|
|
500
|
+
CalendarCheck2,
|
|
501
|
+
CalendarX,
|
|
502
|
+
ListPlus,
|
|
503
|
+
Map,
|
|
504
|
+
Ban,
|
|
505
|
+
Clock3,
|
|
506
|
+
Inbox,
|
|
507
|
+
UserCheck,
|
|
508
|
+
Star,
|
|
509
|
+
Zap,
|
|
510
|
+
Clapperboard,
|
|
511
|
+
Newspaper,
|
|
512
|
+
GraduationCap,
|
|
513
|
+
Repeat
|
|
514
|
+
};
|
|
515
|
+
function getIcon(name) {
|
|
516
|
+
return ICON_MAP2[name] ?? Home;
|
|
517
|
+
}
|
|
518
|
+
function NavDropdown({ item, currentPath, onNavigate }) {
|
|
519
|
+
const [open, setOpen] = React7.useState(false);
|
|
520
|
+
const Icon = getIcon(item.icon);
|
|
521
|
+
const isChildActive = item.children?.some(
|
|
522
|
+
(child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
|
|
523
|
+
);
|
|
524
|
+
return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
|
|
525
|
+
/* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
526
|
+
"button",
|
|
527
|
+
{
|
|
528
|
+
"data-nav-active": isChildActive || void 0,
|
|
529
|
+
className: cn(
|
|
530
|
+
"flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
|
|
531
|
+
isChildActive ? "border-sidebar-accent-foreground font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
|
|
532
|
+
),
|
|
533
|
+
children: [
|
|
534
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
535
|
+
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
536
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: cn("h-3 w-3 transition-transform", open && "rotate-180") })
|
|
537
|
+
]
|
|
538
|
+
}
|
|
539
|
+
) }),
|
|
540
|
+
/* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
|
|
541
|
+
Popover.Content,
|
|
542
|
+
{
|
|
543
|
+
align: "start",
|
|
544
|
+
sideOffset: 4,
|
|
545
|
+
className: "fayz-glass-surface z-50 min-w-[200px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95",
|
|
546
|
+
children: item.children?.map((child) => {
|
|
547
|
+
const ChildIcon = getIcon(child.icon);
|
|
548
|
+
const isActive = currentPath === child.route || currentPath.startsWith(child.route + "/");
|
|
549
|
+
return /* @__PURE__ */ jsxs(
|
|
550
|
+
"button",
|
|
551
|
+
{
|
|
552
|
+
onClick: () => {
|
|
553
|
+
onNavigate(child.route);
|
|
554
|
+
setOpen(false);
|
|
555
|
+
},
|
|
556
|
+
className: cn(
|
|
557
|
+
"flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors",
|
|
558
|
+
isActive ? "bg-muted font-medium text-foreground" : "text-popover-foreground hover:bg-muted/70"
|
|
559
|
+
),
|
|
560
|
+
children: [
|
|
561
|
+
/* @__PURE__ */ jsx(ChildIcon, { className: "h-4 w-4 text-muted-foreground" }),
|
|
562
|
+
/* @__PURE__ */ jsx("span", { children: child.label })
|
|
563
|
+
]
|
|
564
|
+
},
|
|
565
|
+
child.id
|
|
566
|
+
);
|
|
567
|
+
})
|
|
568
|
+
}
|
|
569
|
+
) })
|
|
570
|
+
] });
|
|
571
|
+
}
|
|
572
|
+
function Topbar({
|
|
573
|
+
navigation,
|
|
574
|
+
logo,
|
|
575
|
+
user,
|
|
576
|
+
onMenuClick,
|
|
577
|
+
orgSwitcher,
|
|
578
|
+
leftContent,
|
|
579
|
+
rightContent,
|
|
580
|
+
currentPath = "/",
|
|
581
|
+
onNavigate,
|
|
582
|
+
searchPlaceholder = "Search...",
|
|
583
|
+
onSignOut,
|
|
584
|
+
onProfile,
|
|
585
|
+
onSettings,
|
|
586
|
+
onBilling,
|
|
587
|
+
userPlan,
|
|
588
|
+
billingLabel,
|
|
589
|
+
userMenuExtras
|
|
590
|
+
}) {
|
|
591
|
+
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
592
|
+
const mainNav = navigation.filter((item) => item.section === "main");
|
|
593
|
+
const secondaryNav = navigation.filter((item) => item.section === "secondary");
|
|
594
|
+
const allNav = [...mainNav, ...secondaryNav];
|
|
595
|
+
const handleNavigate = (route) => {
|
|
596
|
+
onNavigate?.(route);
|
|
597
|
+
};
|
|
598
|
+
return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
|
|
599
|
+
/* @__PURE__ */ jsx("div", { className: "border-b border-sidebar-border bg-sidebar text-sidebar-foreground", children: /* @__PURE__ */ jsxs("div", { className: "flex h-14 w-full items-center justify-between px-4 md:px-6", children: [
|
|
600
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
601
|
+
onMenuClick && /* @__PURE__ */ jsx(
|
|
602
|
+
"button",
|
|
603
|
+
{
|
|
604
|
+
onClick: onMenuClick,
|
|
605
|
+
className: "inline-flex items-center justify-center rounded-md p-2 text-sidebar-foreground/70 hover:bg-sidebar/30 hover:text-sidebar-foreground md:hidden",
|
|
606
|
+
"aria-label": "Open menu",
|
|
607
|
+
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
608
|
+
}
|
|
609
|
+
),
|
|
610
|
+
/* @__PURE__ */ jsx("div", { className: "hidden shrink-0 items-center md:flex", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
|
|
611
|
+
] }),
|
|
612
|
+
/* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
|
|
613
|
+
"button",
|
|
614
|
+
{
|
|
615
|
+
onClick: () => setCommandPaletteOpen(true),
|
|
616
|
+
className: "relative flex h-8 w-full items-center rounded-md border border-sidebar-border bg-sidebar-accent px-3 pl-8 text-sm text-sidebar-muted shadow-sm transition-colors hover:border-sidebar-foreground/25 hover:bg-sidebar-foreground/10 hover:text-sidebar-foreground",
|
|
617
|
+
children: [
|
|
618
|
+
/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-sidebar-muted" }),
|
|
619
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs", children: searchPlaceholder }),
|
|
620
|
+
/* @__PURE__ */ jsx("kbd", { className: "ml-auto hidden rounded border border-sidebar-border bg-sidebar-accent px-1.5 py-0.5 text-[10px] font-medium text-sidebar-muted sm:inline-block", children: "\u2318K" })
|
|
621
|
+
]
|
|
622
|
+
}
|
|
623
|
+
) }),
|
|
624
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [
|
|
625
|
+
orgSwitcher && /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 items-center md:flex", children: orgSwitcher }),
|
|
626
|
+
leftContent,
|
|
627
|
+
rightContent,
|
|
628
|
+
/* @__PURE__ */ jsx(
|
|
629
|
+
TopbarUserMenu,
|
|
630
|
+
{
|
|
631
|
+
user,
|
|
632
|
+
userPlan,
|
|
633
|
+
billingLabel,
|
|
634
|
+
onSignOut,
|
|
635
|
+
onProfile,
|
|
636
|
+
onSettings,
|
|
637
|
+
onBilling,
|
|
638
|
+
userMenuExtras
|
|
639
|
+
}
|
|
640
|
+
)
|
|
641
|
+
] })
|
|
642
|
+
] }) }),
|
|
643
|
+
/* @__PURE__ */ jsx("div", { className: "hidden md:block bg-sidebar", children: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-full items-center px-4 md:px-6", children: /* @__PURE__ */ jsx("nav", { ref: (el) => {
|
|
644
|
+
if (!el) return;
|
|
645
|
+
const active = el.querySelector('[data-nav-active="true"]');
|
|
646
|
+
if (active) {
|
|
647
|
+
const left = active.offsetLeft - el.offsetWidth / 2 + active.offsetWidth / 2;
|
|
648
|
+
el.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
|
|
649
|
+
}
|
|
650
|
+
}, className: "flex items-center gap-0.5 overflow-x-auto scrollbar-none", children: allNav.map((item) => {
|
|
651
|
+
if (item.children && item.children.length > 0) {
|
|
652
|
+
return /* @__PURE__ */ jsx(
|
|
653
|
+
NavDropdown,
|
|
654
|
+
{
|
|
655
|
+
item,
|
|
656
|
+
currentPath,
|
|
657
|
+
onNavigate: handleNavigate
|
|
658
|
+
},
|
|
659
|
+
item.id
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
const Icon = getIcon(item.icon);
|
|
663
|
+
const isActive = item.route === "/" ? currentPath === "/" : currentPath === item.route || currentPath.startsWith(item.route + "/");
|
|
664
|
+
return /* @__PURE__ */ jsxs(
|
|
665
|
+
"button",
|
|
666
|
+
{
|
|
667
|
+
"data-nav-active": isActive || void 0,
|
|
668
|
+
onClick: () => handleNavigate(item.route),
|
|
669
|
+
className: cn(
|
|
670
|
+
"flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
|
|
671
|
+
isActive ? "border-primary font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
|
|
672
|
+
),
|
|
673
|
+
children: [
|
|
674
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
675
|
+
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
676
|
+
item.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 text-amber-500", "aria-hidden": true }),
|
|
677
|
+
item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary px-1.5 py-0.5 text-[10px] font-semibold text-primary-foreground", children: item.badge })
|
|
678
|
+
]
|
|
679
|
+
},
|
|
680
|
+
item.id
|
|
681
|
+
);
|
|
682
|
+
}) }) }) })
|
|
683
|
+
] });
|
|
684
|
+
}
|
|
12
685
|
var GUTTER_PX = 8;
|
|
13
686
|
var CLOSE_DELAY_MS = 140;
|
|
14
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ?
|
|
687
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
|
|
15
688
|
function iconOf(name) {
|
|
16
689
|
if (!name) return null;
|
|
17
690
|
return ICON_MAP[name] ?? null;
|
|
18
691
|
}
|
|
692
|
+
var RailFlyoutContext = React7.createContext(null);
|
|
693
|
+
var RailFlyoutProvider = RailFlyoutContext.Provider;
|
|
694
|
+
function useRailRow(content) {
|
|
695
|
+
const flyout = React7.useContext(RailFlyoutContext);
|
|
696
|
+
if (!flyout || !content) return {};
|
|
697
|
+
return flyout.rowProps(content);
|
|
698
|
+
}
|
|
19
699
|
function useRailFlyout({
|
|
20
700
|
enabled,
|
|
21
701
|
railRef,
|
|
22
702
|
activeRoute,
|
|
23
703
|
onNavigate
|
|
24
704
|
}) {
|
|
25
|
-
const [state, setState] =
|
|
26
|
-
const [pos, setPos] =
|
|
27
|
-
const [animate, setAnimate] =
|
|
28
|
-
const panelRef =
|
|
29
|
-
const closeTimer =
|
|
30
|
-
const lastContent =
|
|
31
|
-
const cancelClose =
|
|
705
|
+
const [state, setState] = React7.useState(null);
|
|
706
|
+
const [pos, setPos] = React7.useState({ top: 0, arrow: 20 });
|
|
707
|
+
const [animate, setAnimate] = React7.useState(false);
|
|
708
|
+
const panelRef = React7.useRef(null);
|
|
709
|
+
const closeTimer = React7.useRef(null);
|
|
710
|
+
const lastContent = React7.useRef(null);
|
|
711
|
+
const cancelClose = React7.useCallback(() => {
|
|
32
712
|
if (closeTimer.current) {
|
|
33
713
|
clearTimeout(closeTimer.current);
|
|
34
714
|
closeTimer.current = null;
|
|
35
715
|
}
|
|
36
716
|
}, []);
|
|
37
|
-
const show =
|
|
717
|
+
const show = React7.useCallback(
|
|
38
718
|
(anchor, content2) => {
|
|
39
719
|
if (!enabled) return;
|
|
40
720
|
cancelClose();
|
|
@@ -42,19 +722,19 @@ function useRailFlyout({
|
|
|
42
722
|
},
|
|
43
723
|
[enabled, cancelClose]
|
|
44
724
|
);
|
|
45
|
-
const hide =
|
|
725
|
+
const hide = React7.useCallback(() => {
|
|
46
726
|
cancelClose();
|
|
47
727
|
closeTimer.current = setTimeout(() => setState(null), CLOSE_DELAY_MS);
|
|
48
728
|
}, [cancelClose]);
|
|
49
|
-
const dismiss =
|
|
729
|
+
const dismiss = React7.useCallback(() => {
|
|
50
730
|
cancelClose();
|
|
51
731
|
setState(null);
|
|
52
732
|
}, [cancelClose]);
|
|
53
|
-
|
|
54
|
-
|
|
733
|
+
React7.useEffect(() => () => cancelClose(), [cancelClose]);
|
|
734
|
+
React7.useEffect(() => {
|
|
55
735
|
if (!enabled) dismiss();
|
|
56
736
|
}, [enabled, dismiss]);
|
|
57
|
-
const reposition =
|
|
737
|
+
const reposition = React7.useCallback(() => {
|
|
58
738
|
const rail = railRef.current;
|
|
59
739
|
const anchor = state?.anchor;
|
|
60
740
|
if (!rail || !anchor) return;
|
|
@@ -69,7 +749,7 @@ function useRailFlyout({
|
|
|
69
749
|
setPos({ top, arrow });
|
|
70
750
|
}, [railRef, state]);
|
|
71
751
|
useIsomorphicLayoutEffect(reposition, [reposition, state?.content.key]);
|
|
72
|
-
|
|
752
|
+
React7.useEffect(() => {
|
|
73
753
|
if (!state) return;
|
|
74
754
|
const rail = railRef.current;
|
|
75
755
|
const onScroll = () => reposition();
|
|
@@ -81,7 +761,7 @@ function useRailFlyout({
|
|
|
81
761
|
};
|
|
82
762
|
}, [state, railRef, reposition]);
|
|
83
763
|
const visible = !!state;
|
|
84
|
-
|
|
764
|
+
React7.useEffect(() => {
|
|
85
765
|
if (!visible) {
|
|
86
766
|
setAnimate(false);
|
|
87
767
|
return;
|
|
@@ -89,7 +769,7 @@ function useRailFlyout({
|
|
|
89
769
|
const id = requestAnimationFrame(() => setAnimate(true));
|
|
90
770
|
return () => cancelAnimationFrame(id);
|
|
91
771
|
}, [visible]);
|
|
92
|
-
|
|
772
|
+
React7.useEffect(() => {
|
|
93
773
|
if (!visible) return;
|
|
94
774
|
const onKey = (event) => {
|
|
95
775
|
if (event.key === "Escape") dismiss();
|
|
@@ -97,7 +777,7 @@ function useRailFlyout({
|
|
|
97
777
|
window.addEventListener("keydown", onKey);
|
|
98
778
|
return () => window.removeEventListener("keydown", onKey);
|
|
99
779
|
}, [visible, dismiss]);
|
|
100
|
-
const rowProps =
|
|
780
|
+
const rowProps = React7.useCallback(
|
|
101
781
|
(content2) => ({
|
|
102
782
|
onMouseEnter: (event) => show(event.currentTarget, content2),
|
|
103
783
|
onFocus: (event) => show(event.currentTarget, content2),
|
|
@@ -214,8 +894,8 @@ function FlyoutBody({
|
|
|
214
894
|
] });
|
|
215
895
|
}
|
|
216
896
|
var RAIL_PUSH_MS = 260;
|
|
217
|
-
var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ?
|
|
218
|
-
function
|
|
897
|
+
var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
|
|
898
|
+
function getIcon2(name) {
|
|
219
899
|
return ICON_MAP[name] ?? Home;
|
|
220
900
|
}
|
|
221
901
|
function shortcutKey() {
|
|
@@ -248,11 +928,15 @@ function NavItem({
|
|
|
248
928
|
onNavigate,
|
|
249
929
|
flyout
|
|
250
930
|
}) {
|
|
251
|
-
const Icon =
|
|
931
|
+
const Icon = getIcon2(item.icon);
|
|
252
932
|
const isExternal = !!item.href;
|
|
253
933
|
const hoverProps = collapsed && flyout ? flyout.rowProps(flyoutContentOf(item)) : void 0;
|
|
254
934
|
const className = cn(
|
|
255
|
-
|
|
935
|
+
// 36px rows. The rail was condensed to 28px while it listed every
|
|
936
|
+
// destination flat; with the destinations folded into collapsible groups it
|
|
937
|
+
// shows a handful of rows at rest, and at that length the tight rhythm read
|
|
938
|
+
// as cramped rather than as dense.
|
|
939
|
+
"flex w-full items-center gap-3 rounded-button px-2.5 py-2 text-[13px] font-medium transition-colors",
|
|
256
940
|
"hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground",
|
|
257
941
|
isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground shadow-sm" : "text-sidebar-foreground/80",
|
|
258
942
|
collapsed && "justify-center px-2"
|
|
@@ -310,8 +994,8 @@ function NavGroup({
|
|
|
310
994
|
flyout
|
|
311
995
|
}) {
|
|
312
996
|
const isChildActive = item.children?.some((c) => c.route === activeRoute);
|
|
313
|
-
const [open, setOpen] =
|
|
314
|
-
const Icon =
|
|
997
|
+
const [open, setOpen] = React7.useState(() => isChildActive ?? false);
|
|
998
|
+
const Icon = getIcon2(item.icon);
|
|
315
999
|
if (collapsed) {
|
|
316
1000
|
const hoverProps = flyout?.rowProps(flyoutContentOf(item));
|
|
317
1001
|
return /* @__PURE__ */ jsx(
|
|
@@ -337,7 +1021,9 @@ function NavGroup({
|
|
|
337
1021
|
{
|
|
338
1022
|
onClick: () => setOpen(!open),
|
|
339
1023
|
className: cn(
|
|
340
|
-
|
|
1024
|
+
// Same metrics as NavItem — a group header and a plain link are peers
|
|
1025
|
+
// in the rail and must share a baseline.
|
|
1026
|
+
"flex w-full items-center gap-3 rounded-button px-2.5 py-2 text-[13px] font-medium transition-colors",
|
|
341
1027
|
"hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground",
|
|
342
1028
|
isChildActive ? "text-sidebar-accent-foreground" : "text-sidebar-foreground/80"
|
|
343
1029
|
),
|
|
@@ -364,7 +1050,7 @@ function NavGroup({
|
|
|
364
1050
|
// along with the labels and land the arrowhead on top of the text.
|
|
365
1051
|
childConnector ? "relative" : "ml-4 space-y-0.5 border-l border-sidebar-border/50 pl-3"
|
|
366
1052
|
), children: item.children?.map((child, childIndex) => {
|
|
367
|
-
const ChildIcon =
|
|
1053
|
+
const ChildIcon = getIcon2(child.icon);
|
|
368
1054
|
const isActive = child.route === activeRoute;
|
|
369
1055
|
const childExternal = !!child.href;
|
|
370
1056
|
const childClass = cn(
|
|
@@ -382,7 +1068,7 @@ function NavGroup({
|
|
|
382
1068
|
child.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 shrink-0 text-amber-500", "aria-hidden": true })
|
|
383
1069
|
] });
|
|
384
1070
|
const row = childExternal ? /* @__PURE__ */ jsx("a", { href: child.href, target: "_blank", rel: "noopener noreferrer", className: childClass, children: childInner }) : /* @__PURE__ */ jsx("button", { onClick: () => onNavigate(child.route), className: childClass, children: childInner });
|
|
385
|
-
if (!childConnector) return /* @__PURE__ */ jsx(
|
|
1071
|
+
if (!childConnector) return /* @__PURE__ */ jsx(React7.Fragment, { children: row }, child.id);
|
|
386
1072
|
const activeIndex = item.children.findIndex((c) => c.route === activeRoute);
|
|
387
1073
|
const onPath = activeIndex >= 0 && childIndex < activeIndex;
|
|
388
1074
|
return (
|
|
@@ -444,9 +1130,9 @@ function NavGroup({
|
|
|
444
1130
|
] });
|
|
445
1131
|
}
|
|
446
1132
|
function withCollapsed(slot, collapsed) {
|
|
447
|
-
if (!
|
|
1133
|
+
if (!React7.isValidElement(slot)) return slot;
|
|
448
1134
|
if (typeof slot.type === "string") return slot;
|
|
449
|
-
return
|
|
1135
|
+
return React7.cloneElement(slot, { collapsed });
|
|
450
1136
|
}
|
|
451
1137
|
function Sidebar({
|
|
452
1138
|
navigation,
|
|
@@ -473,7 +1159,7 @@ function Sidebar({
|
|
|
473
1159
|
const currentPath = currentPathProp;
|
|
474
1160
|
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
475
1161
|
const activeRoute = resolveActiveRoute(navigation, currentPath);
|
|
476
|
-
const railRef =
|
|
1162
|
+
const railRef = React7.useRef(null);
|
|
477
1163
|
const flyout = useRailFlyout({
|
|
478
1164
|
enabled: collapsed,
|
|
479
1165
|
railRef,
|
|
@@ -486,7 +1172,7 @@ function Sidebar({
|
|
|
486
1172
|
const handleNavigate = (route) => {
|
|
487
1173
|
onNavigate?.(route);
|
|
488
1174
|
};
|
|
489
|
-
const sectionHeading = (label) => label && !collapsed ? /* @__PURE__ */ jsx("p", { className: "px-2.5 pb-
|
|
1175
|
+
const sectionHeading = (label) => label && !collapsed ? /* @__PURE__ */ jsx("p", { className: "px-2.5 pb-0.5 pt-1 text-[10px] font-semibold uppercase tracking-wider text-sidebar-muted", children: label }) : null;
|
|
490
1176
|
const groupRuns = (items) => {
|
|
491
1177
|
const runs = [];
|
|
492
1178
|
for (const item of items) {
|
|
@@ -498,10 +1184,14 @@ function Sidebar({
|
|
|
498
1184
|
};
|
|
499
1185
|
const renderSection = (items, label) => {
|
|
500
1186
|
const runs = groupRuns(items);
|
|
501
|
-
return runs.map((run, i) =>
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
1187
|
+
return runs.map((run, i) => (
|
|
1188
|
+
// 4px between rows, 20px between runs (a run being a `group` heading's
|
|
1189
|
+
// worth of entries — see NavigationItem.group).
|
|
1190
|
+
/* @__PURE__ */ jsxs("div", { className: i === 0 ? "space-y-1" : "mt-5 space-y-1", children: [
|
|
1191
|
+
sectionHeading(run.group ?? label),
|
|
1192
|
+
run.items.map(renderItem)
|
|
1193
|
+
] }, run.group ?? `ungrouped-${i}`)
|
|
1194
|
+
));
|
|
505
1195
|
};
|
|
506
1196
|
const renderItem = (item) => item.children && item.children.length > 0 ? /* @__PURE__ */ jsx(NavGroup, { item, collapsed, activeRoute, onNavigate: handleNavigate, childConnector: navChildConnector, flyout }, item.id) : /* @__PURE__ */ jsx(
|
|
507
1197
|
NavItem,
|
|
@@ -519,10 +1209,10 @@ function Sidebar({
|
|
|
519
1209
|
mainItems.length > 0 && renderSection(mainItems, sectionLabels?.main),
|
|
520
1210
|
secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6", children: renderSection(secondaryItems, sectionLabels?.secondary) })
|
|
521
1211
|
] });
|
|
522
|
-
const [outgoing, setOutgoing] =
|
|
523
|
-
const lastBodyRef =
|
|
524
|
-
const lastKeyRef =
|
|
525
|
-
const timerRef =
|
|
1212
|
+
const [outgoing, setOutgoing] = React7.useState(null);
|
|
1213
|
+
const lastBodyRef = React7.useRef(null);
|
|
1214
|
+
const lastKeyRef = React7.useRef(navKey);
|
|
1215
|
+
const timerRef = React7.useRef(null);
|
|
526
1216
|
useIsomorphicLayoutEffect2(() => {
|
|
527
1217
|
if (lastKeyRef.current !== navKey) {
|
|
528
1218
|
const previousBody = lastBodyRef.current;
|
|
@@ -535,533 +1225,183 @@ function Sidebar({
|
|
|
535
1225
|
}
|
|
536
1226
|
lastBodyRef.current = navBody;
|
|
537
1227
|
});
|
|
538
|
-
|
|
1228
|
+
React7.useEffect(() => () => {
|
|
539
1229
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
540
1230
|
}, []);
|
|
541
|
-
return
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
"
|
|
546
|
-
onBlur: (event) => {
|
|
547
|
-
if (!event.currentTarget.contains(event.relatedTarget)) flyout.dismiss();
|
|
548
|
-
},
|
|
549
|
-
className: cn(
|
|
550
|
-
// `relative` anchors the flyout, which hangs off the rail's right edge
|
|
551
|
-
// and has to escape the nav's own scroll box to do it.
|
|
552
|
-
"relative flex h-full flex-col bg-sidebar text-sidebar-foreground transition-all duration-200",
|
|
553
|
-
!borderless && "border-r border-sidebar-border",
|
|
554
|
-
collapsed ? "w-16" : "w-64"
|
|
555
|
-
),
|
|
556
|
-
children: [
|
|
557
|
-
/* @__PURE__ */ jsxs(
|
|
558
|
-
"div",
|
|
559
|
-
{
|
|
560
|
-
className: cn(
|
|
561
|
-
"flex h-14 items-center justify-between px-4",
|
|
562
|
-
!borderless && "border-b border-sidebar-border",
|
|
563
|
-
collapsed && "justify-center px-2"
|
|
564
|
-
),
|
|
565
|
-
children: [
|
|
566
|
-
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
567
|
-
logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }),
|
|
568
|
-
/* @__PURE__ */ jsx(
|
|
569
|
-
"button",
|
|
570
|
-
{
|
|
571
|
-
onClick: onToggle,
|
|
572
|
-
className: "ml-auto rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
573
|
-
"aria-label": "Collapse sidebar",
|
|
574
|
-
children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
|
|
575
|
-
}
|
|
576
|
-
)
|
|
577
|
-
] }),
|
|
578
|
-
collapsed && /* @__PURE__ */ jsx(
|
|
579
|
-
"button",
|
|
580
|
-
{
|
|
581
|
-
onClick: onToggle,
|
|
582
|
-
className: "rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
583
|
-
"aria-label": "Expand sidebar",
|
|
584
|
-
children: /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" })
|
|
585
|
-
}
|
|
586
|
-
)
|
|
587
|
-
]
|
|
588
|
-
}
|
|
589
|
-
),
|
|
590
|
-
topContent && /* @__PURE__ */ jsx("div", { className: "px-2 pt-2", children: withCollapsed(topContent, collapsed) }),
|
|
591
|
-
/* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: /* @__PURE__ */ jsxs(
|
|
592
|
-
"button",
|
|
593
|
-
{
|
|
594
|
-
onClick: () => setCommandPaletteOpen(true),
|
|
595
|
-
"aria-keyshortcuts": "Meta+K Control+K",
|
|
596
|
-
className: cn(
|
|
597
|
-
// Tonal surface, no outline. On a dark rail a 1px border is a hard
|
|
598
|
-
// edge competing with every other hard edge; a lighter fill of the
|
|
599
|
-
// same hue reads as "a field" without drawing a box around it — and
|
|
600
|
-
// leaves the outline treatment free to mean focus, which it must.
|
|
601
|
-
"fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
|
|
602
|
-
"hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
|
|
603
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
|
|
604
|
-
collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
|
|
605
|
-
),
|
|
606
|
-
"aria-label": searchLabel,
|
|
607
|
-
title: collapsed ? searchLabel : void 0,
|
|
608
|
-
children: [
|
|
609
|
-
/* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
|
|
610
|
-
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
611
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
|
|
612
|
-
/* @__PURE__ */ jsxs("span", { className: "shrink-0 text-[11px] font-medium tabular-nums text-sidebar-muted/60 transition-opacity group-hover:opacity-100 md:opacity-70", children: [
|
|
613
|
-
shortcutKey(),
|
|
614
|
-
"K"
|
|
615
|
-
] })
|
|
616
|
-
] })
|
|
617
|
-
]
|
|
618
|
-
}
|
|
619
|
-
) }),
|
|
620
|
-
/* @__PURE__ */ jsxs(
|
|
621
|
-
"nav",
|
|
622
|
-
{
|
|
623
|
-
className: cn(
|
|
624
|
-
"relative flex-1 p-2",
|
|
625
|
-
// scrollbar-none: the rail still scrolls, but without the native
|
|
626
|
-
// scrollbar gutter eating into the nav items.
|
|
627
|
-
outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
|
|
628
|
-
),
|
|
629
|
-
children: [
|
|
630
|
-
outgoing && /* @__PURE__ */ jsx(
|
|
631
|
-
"div",
|
|
632
|
-
{
|
|
633
|
-
"aria-hidden": true,
|
|
634
|
-
className: cn(
|
|
635
|
-
"pointer-events-none absolute inset-x-2 top-2 space-y-1",
|
|
636
|
-
outgoing.direction === "back" ? "saas-rail-out-back" : "saas-rail-out-forward"
|
|
637
|
-
),
|
|
638
|
-
children: outgoing.body
|
|
639
|
-
}
|
|
640
|
-
),
|
|
641
|
-
/* @__PURE__ */ jsx(
|
|
642
|
-
"div",
|
|
643
|
-
{
|
|
644
|
-
className: cn(
|
|
645
|
-
"space-y-1",
|
|
646
|
-
outgoing && (outgoing.direction === "back" ? "saas-rail-in-back" : "saas-rail-in-forward")
|
|
647
|
-
),
|
|
648
|
-
children: navBody
|
|
649
|
-
},
|
|
650
|
-
navKey
|
|
651
|
-
)
|
|
652
|
-
]
|
|
653
|
-
}
|
|
654
|
-
),
|
|
655
|
-
settingsItems.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("p-2 space-y-1", !borderless && "border-t border-sidebar-border"), children: settingsItems.map((item) => /* @__PURE__ */ jsx(
|
|
656
|
-
NavItem,
|
|
657
|
-
{
|
|
658
|
-
item,
|
|
659
|
-
collapsed,
|
|
660
|
-
isActive: item.route === activeRoute,
|
|
661
|
-
onNavigate: handleNavigate,
|
|
662
|
-
flyout
|
|
663
|
-
},
|
|
664
|
-
item.id
|
|
665
|
-
)) }),
|
|
666
|
-
footerContent && // `empty:hidden` — slot children may all render null (e.g. the
|
|
667
|
-
// SeuSiteWidget self-hides when the tenant has no project); without
|
|
668
|
-
// it the padded/bordered wrapper shows as a blank block.
|
|
669
|
-
/* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
|
|
670
|
-
withCollapsed(orgSwitcher, collapsed),
|
|
671
|
-
/* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
672
|
-
"flex items-center rounded-button",
|
|
673
|
-
collapsed ? "flex-col gap-1" : "gap-1 px-1"
|
|
674
|
-
), children: [
|
|
675
|
-
userMenuSlot,
|
|
676
|
-
!collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
677
|
-
notificationSlot ?? /* @__PURE__ */ jsxs(
|
|
678
|
-
"button",
|
|
679
|
-
{
|
|
680
|
-
className: "fz-tap relative inline-flex shrink-0 items-center justify-center rounded-button p-2 text-sidebar-muted transition-colors hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
|
|
681
|
-
"aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
|
|
682
|
-
children: [
|
|
683
|
-
/* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
|
|
684
|
-
unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
|
|
685
|
-
]
|
|
686
|
-
}
|
|
687
|
-
)
|
|
688
|
-
] }) }),
|
|
689
|
-
flyout.panel
|
|
690
|
-
]
|
|
691
|
-
}
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
function TopbarUserMenu({
|
|
695
|
-
user,
|
|
696
|
-
userPlan,
|
|
697
|
-
billingLabel = "Subscription",
|
|
698
|
-
onSignOut,
|
|
699
|
-
onProfile,
|
|
700
|
-
onSettings,
|
|
701
|
-
onBilling,
|
|
702
|
-
userMenuExtras
|
|
703
|
-
}) {
|
|
704
|
-
const [open, setOpen] = React.useState(false);
|
|
705
|
-
if (!user) return null;
|
|
706
|
-
const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
|
|
707
|
-
const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
|
|
708
|
-
"button",
|
|
709
|
-
{
|
|
710
|
-
onClick: () => {
|
|
711
|
-
setOpen(false);
|
|
712
|
-
onClick();
|
|
713
|
-
},
|
|
714
|
-
className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
|
|
715
|
-
children: [
|
|
716
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground" }),
|
|
717
|
-
/* @__PURE__ */ jsx("span", { children: label })
|
|
718
|
-
]
|
|
719
|
-
},
|
|
720
|
-
label
|
|
721
|
-
) : null;
|
|
722
|
-
return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
|
|
723
|
-
/* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
724
|
-
"button",
|
|
725
|
-
{
|
|
726
|
-
"aria-label": "User menu",
|
|
727
|
-
className: "ml-1 flex h-9 w-9 items-center justify-center overflow-hidden rounded-full bg-primary text-xs font-semibold text-primary-foreground ring-2 ring-transparent transition hover:ring-sidebar-border",
|
|
728
|
-
children: user.avatarUrl ? (
|
|
729
|
-
// eslint-disable-next-line @next/next/no-img-element
|
|
730
|
-
/* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" })
|
|
731
|
-
) : initial
|
|
732
|
-
}
|
|
733
|
-
) }),
|
|
734
|
-
/* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(
|
|
735
|
-
Popover.Content,
|
|
736
|
-
{
|
|
737
|
-
align: "end",
|
|
738
|
-
sideOffset: 8,
|
|
739
|
-
className: "fayz-glass-surface z-50 min-w-[220px] rounded-xl border border-border bg-popover p-1.5 shadow-lg animate-in fade-in-0 zoom-in-95",
|
|
740
|
-
children: [
|
|
741
|
-
/* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
|
|
742
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: user.fullName || user.email }),
|
|
743
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
|
|
744
|
-
] }),
|
|
745
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5", children: [
|
|
746
|
-
item("Perfil", onProfile, UserCog),
|
|
747
|
-
item("Configura\xE7\xF5es", onSettings, Settings),
|
|
748
|
-
onBilling && /* @__PURE__ */ jsxs(
|
|
749
|
-
"button",
|
|
750
|
-
{
|
|
751
|
-
onClick: () => {
|
|
752
|
-
setOpen(false);
|
|
753
|
-
onBilling();
|
|
754
|
-
},
|
|
755
|
-
className: "flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-popover-foreground transition-colors hover:bg-muted/70",
|
|
756
|
-
children: [
|
|
757
|
-
/* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" }),
|
|
758
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: billingLabel }),
|
|
759
|
-
userPlan && /* @__PURE__ */ jsxs(
|
|
760
|
-
"span",
|
|
761
|
-
{
|
|
762
|
-
className: userPlan.paid ? "inline-flex items-center gap-1 rounded-full border border-amber-400/40 bg-amber-400/15 px-2 py-0.5 text-[10px] font-semibold text-amber-600 dark:text-amber-300" : "inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground",
|
|
763
|
-
children: [
|
|
764
|
-
userPlan.paid ? /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
|
|
765
|
-
userPlan.label
|
|
766
|
-
]
|
|
767
|
-
}
|
|
768
|
-
)
|
|
769
|
-
]
|
|
770
|
-
},
|
|
771
|
-
"billing"
|
|
772
|
-
),
|
|
773
|
-
userMenuExtras?.map((e) => item(e.label, e.onClick)),
|
|
774
|
-
onSignOut && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
775
|
-
/* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
|
|
776
|
-
item("Sair", onSignOut, LogOut)
|
|
777
|
-
] })
|
|
778
|
-
] })
|
|
779
|
-
]
|
|
780
|
-
}
|
|
781
|
-
) })
|
|
782
|
-
] });
|
|
783
|
-
}
|
|
784
|
-
var ICON_MAP2 = {
|
|
785
|
-
Home: Home,
|
|
786
|
-
Users,
|
|
787
|
-
Settings: Settings,
|
|
788
|
-
CreditCard,
|
|
789
|
-
Bell: Bell,
|
|
790
|
-
Calendar,
|
|
791
|
-
CalendarClock,
|
|
792
|
-
Package,
|
|
793
|
-
Activity,
|
|
794
|
-
BarChart3,
|
|
795
|
-
FileText,
|
|
796
|
-
Mail,
|
|
797
|
-
DollarSign,
|
|
798
|
-
Megaphone,
|
|
799
|
-
ShoppingCart,
|
|
800
|
-
ShoppingBag,
|
|
801
|
-
Target,
|
|
802
|
-
Wrench,
|
|
803
|
-
ClipboardList,
|
|
804
|
-
ClipboardCheck,
|
|
805
|
-
Briefcase,
|
|
806
|
-
UserCog,
|
|
807
|
-
BookOpen,
|
|
808
|
-
MessageCircle,
|
|
809
|
-
Globe,
|
|
810
|
-
BookOpenCheck,
|
|
811
|
-
Percent,
|
|
812
|
-
Tag,
|
|
813
|
-
Tags,
|
|
814
|
-
Camera,
|
|
815
|
-
UtensilsCrossed,
|
|
816
|
-
Search: Search,
|
|
817
|
-
MapPin,
|
|
818
|
-
Handshake,
|
|
819
|
-
Contact,
|
|
820
|
-
Building2,
|
|
821
|
-
ChevronDown: ChevronDown,
|
|
822
|
-
Filter,
|
|
823
|
-
Plus,
|
|
824
|
-
Shield,
|
|
825
|
-
List,
|
|
826
|
-
ListChecks,
|
|
827
|
-
FolderOpen,
|
|
828
|
-
Dog,
|
|
829
|
-
Cat,
|
|
830
|
-
PawPrint,
|
|
831
|
-
Heart,
|
|
832
|
-
LayoutTemplate,
|
|
833
|
-
LeafyGreen,
|
|
834
|
-
Apple,
|
|
835
|
-
Egg,
|
|
836
|
-
Wheat,
|
|
837
|
-
ArrowDownCircle,
|
|
838
|
-
ArrowUpCircle,
|
|
839
|
-
ArrowLeftRight,
|
|
840
|
-
Plug,
|
|
841
|
-
SlidersHorizontal,
|
|
842
|
-
TableProperties,
|
|
843
|
-
Landmark,
|
|
844
|
-
Receipt,
|
|
845
|
-
TrendingUp,
|
|
846
|
-
CircleDollarSign,
|
|
847
|
-
Clock,
|
|
848
|
-
AlertTriangle,
|
|
849
|
-
Sparkles,
|
|
850
|
-
Wallet,
|
|
851
|
-
Warehouse,
|
|
852
|
-
Ruler,
|
|
853
|
-
ArrowUpRight,
|
|
854
|
-
ArrowDownRight,
|
|
855
|
-
UserPlus,
|
|
856
|
-
UserX,
|
|
857
|
-
TreePalm,
|
|
858
|
-
Boxes,
|
|
859
|
-
Box,
|
|
860
|
-
FileCheck2,
|
|
861
|
-
PartyPopper,
|
|
862
|
-
Radio,
|
|
863
|
-
CalendarDays,
|
|
864
|
-
BadgeDollarSign,
|
|
865
|
-
Banknote,
|
|
866
|
-
Layers,
|
|
867
|
-
Music,
|
|
868
|
-
Eye,
|
|
869
|
-
ListMusic,
|
|
870
|
-
Disc3,
|
|
871
|
-
UsersRound,
|
|
872
|
-
Mic,
|
|
873
|
-
CalendarCheck2,
|
|
874
|
-
CalendarX,
|
|
875
|
-
ListPlus,
|
|
876
|
-
Map,
|
|
877
|
-
Ban,
|
|
878
|
-
Clock3,
|
|
879
|
-
Inbox,
|
|
880
|
-
UserCheck,
|
|
881
|
-
Star,
|
|
882
|
-
Zap,
|
|
883
|
-
Clapperboard,
|
|
884
|
-
Newspaper,
|
|
885
|
-
GraduationCap,
|
|
886
|
-
Repeat
|
|
887
|
-
};
|
|
888
|
-
function getIcon2(name) {
|
|
889
|
-
return ICON_MAP2[name] ?? Home;
|
|
890
|
-
}
|
|
891
|
-
function NavDropdown({ item, currentPath, onNavigate }) {
|
|
892
|
-
const [open, setOpen] = React.useState(false);
|
|
893
|
-
const Icon = getIcon2(item.icon);
|
|
894
|
-
const isChildActive = item.children?.some(
|
|
895
|
-
(child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
|
|
896
|
-
);
|
|
897
|
-
return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
|
|
898
|
-
/* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
899
|
-
"button",
|
|
1231
|
+
return (
|
|
1232
|
+
// O handle do flyout desce por contexto para QUALQUER linha da trilha —
|
|
1233
|
+
// inclusive os widgets que os plugins montam no pé dela. Ver useRailRow.
|
|
1234
|
+
/* @__PURE__ */ jsx(RailFlyoutProvider, { value: flyout, children: /* @__PURE__ */ jsxs(
|
|
1235
|
+
"aside",
|
|
900
1236
|
{
|
|
901
|
-
|
|
1237
|
+
ref: railRef,
|
|
1238
|
+
"data-print": "hide",
|
|
1239
|
+
onBlur: (event) => {
|
|
1240
|
+
if (!event.currentTarget.contains(event.relatedTarget)) flyout.dismiss();
|
|
1241
|
+
},
|
|
902
1242
|
className: cn(
|
|
903
|
-
|
|
904
|
-
|
|
1243
|
+
// `relative` anchors the flyout, which hangs off the rail's right edge
|
|
1244
|
+
// and has to escape the nav's own scroll box to do it.
|
|
1245
|
+
"relative flex h-full flex-col bg-sidebar text-sidebar-foreground transition-all duration-200",
|
|
1246
|
+
!borderless && "border-r border-sidebar-border",
|
|
1247
|
+
// 14rem expanded. It was 16rem, which is the width a rail needs when
|
|
1248
|
+
// its entries are sentences ("GlossGenius Financing", "Configure your
|
|
1249
|
+
// payment methods"); these are one or two words, so the extra 2rem was
|
|
1250
|
+
// paid for by the content column on every screen in every app. The
|
|
1251
|
+
// labels still truncate, and a long one now truncates 32px earlier —
|
|
1252
|
+
// which is the trade, taken deliberately.
|
|
1253
|
+
collapsed ? "w-16" : "w-56"
|
|
905
1254
|
),
|
|
906
1255
|
children: [
|
|
907
|
-
/* @__PURE__ */
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1256
|
+
/* @__PURE__ */ jsxs(
|
|
1257
|
+
"div",
|
|
1258
|
+
{
|
|
1259
|
+
className: cn(
|
|
1260
|
+
"flex h-14 items-center justify-between px-4",
|
|
1261
|
+
!borderless && "border-b border-sidebar-border",
|
|
1262
|
+
collapsed && "justify-center px-2"
|
|
1263
|
+
),
|
|
1264
|
+
children: [
|
|
1265
|
+
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1266
|
+
logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }),
|
|
1267
|
+
/* @__PURE__ */ jsx(
|
|
1268
|
+
"button",
|
|
1269
|
+
{
|
|
1270
|
+
onClick: onToggle,
|
|
1271
|
+
className: "ml-auto rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
1272
|
+
"aria-label": "Collapse sidebar",
|
|
1273
|
+
children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
|
|
1274
|
+
}
|
|
1275
|
+
)
|
|
1276
|
+
] }),
|
|
1277
|
+
collapsed && /* @__PURE__ */ jsx(
|
|
1278
|
+
"button",
|
|
1279
|
+
{
|
|
1280
|
+
onClick: onToggle,
|
|
1281
|
+
className: "rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
1282
|
+
"aria-label": "Expand sidebar",
|
|
1283
|
+
children: /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" })
|
|
1284
|
+
}
|
|
1285
|
+
)
|
|
1286
|
+
]
|
|
1287
|
+
}
|
|
1288
|
+
),
|
|
1289
|
+
topContent && /* @__PURE__ */ jsx("div", { className: "px-2 pt-2", children: withCollapsed(topContent, collapsed) }),
|
|
1290
|
+
/* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: /* @__PURE__ */ jsxs(
|
|
923
1291
|
"button",
|
|
924
1292
|
{
|
|
925
|
-
onClick: () =>
|
|
926
|
-
|
|
927
|
-
setOpen(false);
|
|
928
|
-
},
|
|
1293
|
+
onClick: () => setCommandPaletteOpen(true),
|
|
1294
|
+
"aria-keyshortcuts": "Meta+K Control+K",
|
|
929
1295
|
className: cn(
|
|
930
|
-
|
|
931
|
-
|
|
1296
|
+
// Tonal surface, no outline. On a dark rail a 1px border is a hard
|
|
1297
|
+
// edge competing with every other hard edge; a lighter fill of the
|
|
1298
|
+
// same hue reads as "a field" without drawing a box around it — and
|
|
1299
|
+
// leaves the outline treatment free to mean focus, which it must.
|
|
1300
|
+
"fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
|
|
1301
|
+
"hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
|
|
1302
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
|
|
1303
|
+
collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
|
|
932
1304
|
),
|
|
1305
|
+
"aria-label": searchLabel,
|
|
1306
|
+
title: collapsed ? searchLabel : void 0,
|
|
933
1307
|
children: [
|
|
934
|
-
/* @__PURE__ */ jsx(
|
|
935
|
-
/* @__PURE__ */
|
|
1308
|
+
/* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
|
|
1309
|
+
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1310
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
|
|
1311
|
+
/* @__PURE__ */ jsxs("span", { className: "shrink-0 text-[11px] font-medium tabular-nums text-sidebar-muted/60 transition-opacity group-hover:opacity-100 md:opacity-70", children: [
|
|
1312
|
+
shortcutKey(),
|
|
1313
|
+
"K"
|
|
1314
|
+
] })
|
|
1315
|
+
] })
|
|
1316
|
+
]
|
|
1317
|
+
}
|
|
1318
|
+
) }),
|
|
1319
|
+
/* @__PURE__ */ jsxs(
|
|
1320
|
+
"nav",
|
|
1321
|
+
{
|
|
1322
|
+
className: cn(
|
|
1323
|
+
"relative flex-1 p-2",
|
|
1324
|
+
// scrollbar-none: the rail still scrolls, but without the native
|
|
1325
|
+
// scrollbar gutter eating into the nav items.
|
|
1326
|
+
outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
|
|
1327
|
+
),
|
|
1328
|
+
children: [
|
|
1329
|
+
outgoing && /* @__PURE__ */ jsx(
|
|
1330
|
+
"div",
|
|
1331
|
+
{
|
|
1332
|
+
"aria-hidden": true,
|
|
1333
|
+
className: cn(
|
|
1334
|
+
"pointer-events-none absolute inset-x-2 top-2 space-y-1",
|
|
1335
|
+
outgoing.direction === "back" ? "saas-rail-out-back" : "saas-rail-out-forward"
|
|
1336
|
+
),
|
|
1337
|
+
children: outgoing.body
|
|
1338
|
+
}
|
|
1339
|
+
),
|
|
1340
|
+
/* @__PURE__ */ jsx(
|
|
1341
|
+
"div",
|
|
1342
|
+
{
|
|
1343
|
+
className: cn(
|
|
1344
|
+
"space-y-1",
|
|
1345
|
+
outgoing && (outgoing.direction === "back" ? "saas-rail-in-back" : "saas-rail-in-forward")
|
|
1346
|
+
),
|
|
1347
|
+
children: navBody
|
|
1348
|
+
},
|
|
1349
|
+
navKey
|
|
1350
|
+
)
|
|
936
1351
|
]
|
|
1352
|
+
}
|
|
1353
|
+
),
|
|
1354
|
+
settingsItems.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("p-2 space-y-1", !borderless && "border-t border-sidebar-border"), children: settingsItems.map((item) => /* @__PURE__ */ jsx(
|
|
1355
|
+
NavItem,
|
|
1356
|
+
{
|
|
1357
|
+
item,
|
|
1358
|
+
collapsed,
|
|
1359
|
+
isActive: item.route === activeRoute,
|
|
1360
|
+
onNavigate: handleNavigate,
|
|
1361
|
+
flyout
|
|
937
1362
|
},
|
|
938
|
-
|
|
939
|
-
)
|
|
940
|
-
|
|
1363
|
+
item.id
|
|
1364
|
+
)) }),
|
|
1365
|
+
footerContent && // `empty:hidden` — slot children may all render null (e.g. the
|
|
1366
|
+
// SeuSiteWidget self-hides when the tenant has no project); without
|
|
1367
|
+
// it the padded/bordered wrapper shows as a blank block.
|
|
1368
|
+
/* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
|
|
1369
|
+
withCollapsed(orgSwitcher, collapsed),
|
|
1370
|
+
/* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
1371
|
+
"flex items-center rounded-button",
|
|
1372
|
+
collapsed ? "flex-col gap-1" : "gap-1 px-1"
|
|
1373
|
+
), children: [
|
|
1374
|
+
userMenuSlot,
|
|
1375
|
+
!collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
1376
|
+
notificationSlot ?? /* @__PURE__ */ jsxs(
|
|
1377
|
+
"button",
|
|
1378
|
+
{
|
|
1379
|
+
className: "fz-tap relative inline-flex shrink-0 items-center justify-center rounded-button p-2 text-sidebar-muted transition-colors hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
|
|
1380
|
+
"aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
|
|
1381
|
+
children: [
|
|
1382
|
+
/* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
|
|
1383
|
+
unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
|
|
1384
|
+
]
|
|
1385
|
+
}
|
|
1386
|
+
)
|
|
1387
|
+
] }) }),
|
|
1388
|
+
flyout.panel
|
|
1389
|
+
]
|
|
941
1390
|
}
|
|
942
1391
|
) })
|
|
943
|
-
|
|
944
|
-
}
|
|
945
|
-
function Topbar({
|
|
946
|
-
navigation,
|
|
947
|
-
logo,
|
|
948
|
-
user,
|
|
949
|
-
onMenuClick,
|
|
950
|
-
orgSwitcher,
|
|
951
|
-
leftContent,
|
|
952
|
-
rightContent,
|
|
953
|
-
currentPath = "/",
|
|
954
|
-
onNavigate,
|
|
955
|
-
searchPlaceholder = "Search...",
|
|
956
|
-
onSignOut,
|
|
957
|
-
onProfile,
|
|
958
|
-
onSettings,
|
|
959
|
-
onBilling,
|
|
960
|
-
userPlan,
|
|
961
|
-
billingLabel,
|
|
962
|
-
userMenuExtras
|
|
963
|
-
}) {
|
|
964
|
-
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
965
|
-
const mainNav = navigation.filter((item) => item.section === "main");
|
|
966
|
-
const secondaryNav = navigation.filter((item) => item.section === "secondary");
|
|
967
|
-
const allNav = [...mainNav, ...secondaryNav];
|
|
968
|
-
const handleNavigate = (route) => {
|
|
969
|
-
onNavigate?.(route);
|
|
970
|
-
};
|
|
971
|
-
return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
|
|
972
|
-
/* @__PURE__ */ jsx("div", { className: "border-b border-sidebar-border bg-sidebar text-sidebar-foreground", children: /* @__PURE__ */ jsxs("div", { className: "flex h-14 w-full items-center justify-between px-4 md:px-6", children: [
|
|
973
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
974
|
-
onMenuClick && /* @__PURE__ */ jsx(
|
|
975
|
-
"button",
|
|
976
|
-
{
|
|
977
|
-
onClick: onMenuClick,
|
|
978
|
-
className: "inline-flex items-center justify-center rounded-md p-2 text-sidebar-foreground/70 hover:bg-sidebar/30 hover:text-sidebar-foreground md:hidden",
|
|
979
|
-
"aria-label": "Open menu",
|
|
980
|
-
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
981
|
-
}
|
|
982
|
-
),
|
|
983
|
-
/* @__PURE__ */ jsx("div", { className: "hidden shrink-0 items-center md:flex", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
|
|
984
|
-
] }),
|
|
985
|
-
/* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
|
|
986
|
-
"button",
|
|
987
|
-
{
|
|
988
|
-
onClick: () => setCommandPaletteOpen(true),
|
|
989
|
-
className: "relative flex h-8 w-full items-center rounded-md border border-sidebar-border bg-sidebar-accent px-3 pl-8 text-sm text-sidebar-muted shadow-sm transition-colors hover:border-sidebar-foreground/25 hover:bg-sidebar-foreground/10 hover:text-sidebar-foreground",
|
|
990
|
-
children: [
|
|
991
|
-
/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-sidebar-muted" }),
|
|
992
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs", children: searchPlaceholder }),
|
|
993
|
-
/* @__PURE__ */ jsx("kbd", { className: "ml-auto hidden rounded border border-sidebar-border bg-sidebar-accent px-1.5 py-0.5 text-[10px] font-medium text-sidebar-muted sm:inline-block", children: "\u2318K" })
|
|
994
|
-
]
|
|
995
|
-
}
|
|
996
|
-
) }),
|
|
997
|
-
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [
|
|
998
|
-
orgSwitcher && /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 items-center md:flex", children: orgSwitcher }),
|
|
999
|
-
leftContent,
|
|
1000
|
-
rightContent,
|
|
1001
|
-
/* @__PURE__ */ jsx(
|
|
1002
|
-
TopbarUserMenu,
|
|
1003
|
-
{
|
|
1004
|
-
user,
|
|
1005
|
-
userPlan,
|
|
1006
|
-
billingLabel,
|
|
1007
|
-
onSignOut,
|
|
1008
|
-
onProfile,
|
|
1009
|
-
onSettings,
|
|
1010
|
-
onBilling,
|
|
1011
|
-
userMenuExtras
|
|
1012
|
-
}
|
|
1013
|
-
)
|
|
1014
|
-
] })
|
|
1015
|
-
] }) }),
|
|
1016
|
-
/* @__PURE__ */ jsx("div", { className: "hidden md:block bg-sidebar", children: /* @__PURE__ */ jsx("div", { className: "flex h-11 w-full items-center px-4 md:px-6", children: /* @__PURE__ */ jsx("nav", { ref: (el) => {
|
|
1017
|
-
if (!el) return;
|
|
1018
|
-
const active = el.querySelector('[data-nav-active="true"]');
|
|
1019
|
-
if (active) {
|
|
1020
|
-
const left = active.offsetLeft - el.offsetWidth / 2 + active.offsetWidth / 2;
|
|
1021
|
-
el.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
|
|
1022
|
-
}
|
|
1023
|
-
}, className: "flex items-center gap-0.5 overflow-x-auto scrollbar-none", children: allNav.map((item) => {
|
|
1024
|
-
if (item.children && item.children.length > 0) {
|
|
1025
|
-
return /* @__PURE__ */ jsx(
|
|
1026
|
-
NavDropdown,
|
|
1027
|
-
{
|
|
1028
|
-
item,
|
|
1029
|
-
currentPath,
|
|
1030
|
-
onNavigate: handleNavigate
|
|
1031
|
-
},
|
|
1032
|
-
item.id
|
|
1033
|
-
);
|
|
1034
|
-
}
|
|
1035
|
-
const Icon = getIcon2(item.icon);
|
|
1036
|
-
const isActive = item.route === "/" ? currentPath === "/" : currentPath === item.route || currentPath.startsWith(item.route + "/");
|
|
1037
|
-
return /* @__PURE__ */ jsxs(
|
|
1038
|
-
"button",
|
|
1039
|
-
{
|
|
1040
|
-
"data-nav-active": isActive || void 0,
|
|
1041
|
-
onClick: () => handleNavigate(item.route),
|
|
1042
|
-
className: cn(
|
|
1043
|
-
"flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
|
|
1044
|
-
isActive ? "border-primary font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
|
|
1045
|
-
),
|
|
1046
|
-
children: [
|
|
1047
|
-
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
1048
|
-
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
1049
|
-
item.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 text-amber-500", "aria-hidden": true }),
|
|
1050
|
-
item.badge !== void 0 && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-primary px-1.5 py-0.5 text-[10px] font-semibold text-primary-foreground", children: item.badge })
|
|
1051
|
-
]
|
|
1052
|
-
},
|
|
1053
|
-
item.id
|
|
1054
|
-
);
|
|
1055
|
-
}) }) }) })
|
|
1056
|
-
] });
|
|
1392
|
+
);
|
|
1057
1393
|
}
|
|
1058
|
-
var ModuleHeaderSlotContext =
|
|
1394
|
+
var ModuleHeaderSlotContext = React7.createContext(null);
|
|
1059
1395
|
function useModuleHeaderSlot() {
|
|
1060
|
-
return
|
|
1396
|
+
return React7.useContext(ModuleHeaderSlotContext);
|
|
1061
1397
|
}
|
|
1062
|
-
var ModuleHeaderActionsSlotContext =
|
|
1398
|
+
var ModuleHeaderActionsSlotContext = React7.createContext(null);
|
|
1063
1399
|
function useModuleHeaderActionsSlot() {
|
|
1064
|
-
return
|
|
1400
|
+
return React7.useContext(ModuleHeaderActionsSlotContext);
|
|
1401
|
+
}
|
|
1402
|
+
var PageRailSlotContext = React7.createContext(null);
|
|
1403
|
+
function usePageRailSlot() {
|
|
1404
|
+
return React7.useContext(PageRailSlotContext);
|
|
1065
1405
|
}
|
|
1066
1406
|
function ShellPageTitle({
|
|
1067
1407
|
title,
|
|
@@ -1106,11 +1446,13 @@ function SidebarLayout({
|
|
|
1106
1446
|
const pageHeaderSlot = usePageHeaderSlot();
|
|
1107
1447
|
usePageHeaderHost();
|
|
1108
1448
|
const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
|
|
1109
|
-
const [mobileMenuOpen, setMobileMenuOpen] =
|
|
1110
|
-
const [headerSlot, setHeaderSlot] =
|
|
1111
|
-
const [actionsSlot, setActionsSlot] =
|
|
1112
|
-
|
|
1113
|
-
|
|
1449
|
+
const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
|
|
1450
|
+
const [headerSlot, setHeaderSlot] = React7.useState(null);
|
|
1451
|
+
const [actionsSlot, setActionsSlot] = React7.useState(null);
|
|
1452
|
+
const [pageRailSlot, setPageRailSlot] = React7.useState(null);
|
|
1453
|
+
const hasPageActions = useHasPageActions();
|
|
1454
|
+
return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsx(PageRailSlotContext.Provider, { value: pageRailSlot, children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
1455
|
+
"shell-enter flex h-screen overflow-hidden bg-background",
|
|
1114
1456
|
// Frame mode: the inset gap around the framed card takes the sidebar (menu)
|
|
1115
1457
|
// color, so the white card reads as a panel floating on the same tone as the
|
|
1116
1458
|
// left menu. The card's own md:m-2 margin reveals this behind it.
|
|
@@ -1134,7 +1476,7 @@ function SidebarLayout({
|
|
|
1134
1476
|
topContent: sidebarTopContent,
|
|
1135
1477
|
navTop: sidebarNavTop,
|
|
1136
1478
|
footerContent: sidebarFooterContent,
|
|
1137
|
-
borderless:
|
|
1479
|
+
borderless: frame,
|
|
1138
1480
|
userMenuSlot,
|
|
1139
1481
|
notificationSlot,
|
|
1140
1482
|
unreadCount,
|
|
@@ -1146,34 +1488,42 @@ function SidebarLayout({
|
|
|
1146
1488
|
}
|
|
1147
1489
|
) }),
|
|
1148
1490
|
/* @__PURE__ */ jsxs("div", { className: cn(
|
|
1149
|
-
"relative flex flex-1
|
|
1150
|
-
|
|
1491
|
+
"relative flex flex-1 overflow-hidden",
|
|
1492
|
+
// `content`, not `card`: this is the page's own surface, and cards have
|
|
1493
|
+
// to sit ON it. Painted card, the frame was the same tone as everything
|
|
1494
|
+
// inside it and the page read as one flat sheet.
|
|
1495
|
+
frame && "md:m-2 md:rounded-xl md:border md:border-border md:bg-content"
|
|
1151
1496
|
), children: [
|
|
1152
|
-
/* @__PURE__ */
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
"
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1497
|
+
/* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
|
|
1498
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 flex-col overflow-hidden", children: [
|
|
1499
|
+
/* @__PURE__ */ jsxs("div", { className: cn(
|
|
1500
|
+
"flex min-h-14 shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-2",
|
|
1501
|
+
// …unless the page put its actions in it. Hiding the bar on mobile
|
|
1502
|
+
// used to hide the only "Novo" the screen had.
|
|
1503
|
+
mobileHeader !== "minimal" && !hasPageActions && "hidden md:flex"
|
|
1504
|
+
), children: [
|
|
1505
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
|
|
1506
|
+
!hasBottomNav && /* @__PURE__ */ jsx(
|
|
1507
|
+
"button",
|
|
1508
|
+
{
|
|
1509
|
+
onClick: () => setMobileMenuOpen(true),
|
|
1510
|
+
className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent md:hidden",
|
|
1511
|
+
"aria-label": "Open menu",
|
|
1512
|
+
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
1513
|
+
}
|
|
1514
|
+
),
|
|
1515
|
+
pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
|
|
1516
|
+
topbarStart,
|
|
1517
|
+
/* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
|
|
1518
|
+
] }),
|
|
1519
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1520
|
+
/* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex items-center gap-2" }),
|
|
1521
|
+
topbarEnd
|
|
1522
|
+
] })
|
|
1169
1523
|
] }),
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
] })
|
|
1174
|
-
] }),
|
|
1175
|
-
/* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children }),
|
|
1176
|
-
/* @__PURE__ */ jsx(SaveBar, {})
|
|
1524
|
+
/* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children }),
|
|
1525
|
+
/* @__PURE__ */ jsx(SaveBar, {})
|
|
1526
|
+
] })
|
|
1177
1527
|
] }),
|
|
1178
1528
|
mobileMenuOpen && /* @__PURE__ */ jsx(
|
|
1179
1529
|
MobileOverlay,
|
|
@@ -1198,7 +1548,7 @@ function SidebarLayout({
|
|
|
1198
1548
|
onClose: () => setMobileMenuOpen(false)
|
|
1199
1549
|
}
|
|
1200
1550
|
)
|
|
1201
|
-
] }) }) }) });
|
|
1551
|
+
] }) }) }) }) });
|
|
1202
1552
|
}
|
|
1203
1553
|
function TopbarLayout({
|
|
1204
1554
|
navigation = [],
|
|
@@ -1223,13 +1573,17 @@ function TopbarLayout({
|
|
|
1223
1573
|
notificationSlot,
|
|
1224
1574
|
hasBottomNav = false
|
|
1225
1575
|
}) {
|
|
1226
|
-
const [mobileMenuOpen, setMobileMenuOpen] =
|
|
1576
|
+
const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
|
|
1577
|
+
const [headerSlot, setHeaderSlot] = React7.useState(null);
|
|
1578
|
+
const [actionsSlot, setActionsSlot] = React7.useState(null);
|
|
1579
|
+
const [pageRailSlot, setPageRailSlot] = React7.useState(null);
|
|
1227
1580
|
const rawSlot = usePageHeaderSlot();
|
|
1228
1581
|
const hasOutlet = useHasPageHeaderOutlet();
|
|
1582
|
+
const hasPageActions = useHasPageActions();
|
|
1229
1583
|
usePageHeaderHost();
|
|
1230
1584
|
const pageHeaderSlot = hasOutlet ? null : rawSlot;
|
|
1231
1585
|
const showTitle = !hasOutlet && !!pageTitle;
|
|
1232
|
-
return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsxs("div", { className: "relative flex h-screen flex-col overflow-hidden bg-background", children: [
|
|
1586
|
+
return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsx(PageRailSlotContext.Provider, { value: pageRailSlot, children: /* @__PURE__ */ jsxs("div", { className: "shell-enter relative flex h-screen flex-col overflow-hidden bg-background", children: [
|
|
1233
1587
|
/* @__PURE__ */ jsx(
|
|
1234
1588
|
Topbar,
|
|
1235
1589
|
{
|
|
@@ -1254,9 +1608,18 @@ function TopbarLayout({
|
|
|
1254
1608
|
onMenuClick: hasBottomNav ? void 0 : () => setMobileMenuOpen(true)
|
|
1255
1609
|
}
|
|
1256
1610
|
),
|
|
1257
|
-
/* @__PURE__ */ jsx("main", { className: cn("flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", frame && "md:bg-sidebar md:p-2"), children: /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 min-h-0 flex-1
|
|
1258
|
-
|
|
1259
|
-
/* @__PURE__ */
|
|
1611
|
+
/* @__PURE__ */ jsx("main", { className: cn("flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", frame && "md:bg-sidebar md:p-2"), children: /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 min-h-0 flex-1 overflow-hidden", frame && "md:rounded-xl md:border md:border-border md:bg-content"), children: [
|
|
1612
|
+
/* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
|
|
1613
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", children: [
|
|
1614
|
+
(pageHeaderSlot || pageBack || hasPageActions) && /* @__PURE__ */ jsxs("div", { className: "flex min-h-12 shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-2", children: [
|
|
1615
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
|
|
1616
|
+
pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: showTitle ? pageTitle : void 0, back: pageBack }),
|
|
1617
|
+
/* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
|
|
1618
|
+
] }),
|
|
1619
|
+
/* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex shrink-0 items-center gap-2" })
|
|
1620
|
+
] }),
|
|
1621
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", children })
|
|
1622
|
+
] })
|
|
1260
1623
|
] }) }),
|
|
1261
1624
|
/* @__PURE__ */ jsx(SaveBar, {}),
|
|
1262
1625
|
mobileMenuOpen && /* @__PURE__ */ jsx(
|
|
@@ -1276,7 +1639,7 @@ function TopbarLayout({
|
|
|
1276
1639
|
onClose: () => setMobileMenuOpen(false)
|
|
1277
1640
|
}
|
|
1278
1641
|
)
|
|
1279
|
-
] }) });
|
|
1642
|
+
] }) }) }) }) });
|
|
1280
1643
|
}
|
|
1281
1644
|
function DrawerUserRow({ user }) {
|
|
1282
1645
|
const label = user.fullName || user.email;
|
|
@@ -1291,7 +1654,7 @@ function MinimalLayout({
|
|
|
1291
1654
|
children,
|
|
1292
1655
|
headerEnd
|
|
1293
1656
|
}) {
|
|
1294
|
-
return /* @__PURE__ */ jsxs("div", { className: "flex h-screen flex-col", children: [
|
|
1657
|
+
return /* @__PURE__ */ jsxs("div", { className: "shell-enter flex h-screen flex-col", children: [
|
|
1295
1658
|
/* @__PURE__ */ jsxs("header", { className: "flex h-14 shrink-0 items-center justify-between border-b border-border bg-card px-4 md:px-6", children: [
|
|
1296
1659
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
|
|
1297
1660
|
headerEnd && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: headerEnd })
|
|
@@ -1300,23 +1663,23 @@ function MinimalLayout({
|
|
|
1300
1663
|
] });
|
|
1301
1664
|
}
|
|
1302
1665
|
function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
|
|
1303
|
-
const [visible, setVisible] =
|
|
1304
|
-
|
|
1666
|
+
const [visible, setVisible] = React7.useState(false);
|
|
1667
|
+
React7.useEffect(() => {
|
|
1305
1668
|
const id = requestAnimationFrame(() => setVisible(true));
|
|
1306
1669
|
return () => cancelAnimationFrame(id);
|
|
1307
1670
|
}, []);
|
|
1308
|
-
const close =
|
|
1671
|
+
const close = React7.useCallback(() => {
|
|
1309
1672
|
setVisible(false);
|
|
1310
1673
|
window.setTimeout(onClose, 250);
|
|
1311
1674
|
}, [onClose]);
|
|
1312
|
-
|
|
1675
|
+
React7.useEffect(() => {
|
|
1313
1676
|
const previous = document.body.style.overflow;
|
|
1314
1677
|
document.body.style.overflow = "hidden";
|
|
1315
1678
|
return () => {
|
|
1316
1679
|
document.body.style.overflow = previous;
|
|
1317
1680
|
};
|
|
1318
1681
|
}, []);
|
|
1319
|
-
|
|
1682
|
+
React7.useEffect(() => {
|
|
1320
1683
|
const onKey = (e) => {
|
|
1321
1684
|
if (e.key === "Escape") close();
|
|
1322
1685
|
};
|
|
@@ -1592,13 +1955,13 @@ function SplitWithRightRail({
|
|
|
1592
1955
|
maxWidth = 720,
|
|
1593
1956
|
resizeLabel = "Resize panel"
|
|
1594
1957
|
}) {
|
|
1595
|
-
const [width, setWidth] =
|
|
1596
|
-
const [dragging, setDragging] =
|
|
1597
|
-
const clamp =
|
|
1958
|
+
const [width, setWidth] = React7.useState(defaultWidth);
|
|
1959
|
+
const [dragging, setDragging] = React7.useState(false);
|
|
1960
|
+
const clamp = React7.useCallback(
|
|
1598
1961
|
(value) => Math.min(maxWidth, Math.max(minWidth, value)),
|
|
1599
1962
|
[minWidth, maxWidth]
|
|
1600
1963
|
);
|
|
1601
|
-
|
|
1964
|
+
React7.useEffect(() => {
|
|
1602
1965
|
if (!dragging) return;
|
|
1603
1966
|
const onMove = (event) => {
|
|
1604
1967
|
event.preventDefault();
|
|
@@ -1620,7 +1983,7 @@ function SplitWithRightRail({
|
|
|
1620
1983
|
document.body.style.cursor = previousCursor;
|
|
1621
1984
|
};
|
|
1622
1985
|
}, [dragging, clamp]);
|
|
1623
|
-
|
|
1986
|
+
React7.useEffect(() => {
|
|
1624
1987
|
if (dragging) return;
|
|
1625
1988
|
onWidthChange?.(width);
|
|
1626
1989
|
}, [dragging]);
|
|
@@ -1699,40 +2062,74 @@ function SplitWithRightRail({
|
|
|
1699
2062
|
] })
|
|
1700
2063
|
);
|
|
1701
2064
|
}
|
|
1702
|
-
function
|
|
2065
|
+
function HeaderTabs({
|
|
2066
|
+
tabs,
|
|
2067
|
+
value,
|
|
2068
|
+
onChange,
|
|
2069
|
+
className,
|
|
2070
|
+
...rest
|
|
2071
|
+
}) {
|
|
1703
2072
|
return /* @__PURE__ */ jsx(
|
|
1704
|
-
"
|
|
2073
|
+
"div",
|
|
1705
2074
|
{
|
|
1706
|
-
"
|
|
2075
|
+
role: "tablist",
|
|
2076
|
+
"aria-label": rest["aria-label"],
|
|
1707
2077
|
className: cn(
|
|
1708
|
-
"
|
|
1709
|
-
"motion-reduce:animate-none motion-reduce:border-t-sidebar-foreground/20 motion-reduce:animate-pulse",
|
|
2078
|
+
"inline-flex max-w-full items-center gap-5 overflow-x-auto scrollbar-none",
|
|
1710
2079
|
className
|
|
1711
|
-
)
|
|
2080
|
+
),
|
|
2081
|
+
children: tabs.map((tab) => {
|
|
2082
|
+
const active = tab.value === value;
|
|
2083
|
+
const Icon = tab.icon;
|
|
2084
|
+
return /* @__PURE__ */ jsxs(
|
|
2085
|
+
"button",
|
|
2086
|
+
{
|
|
2087
|
+
type: "button",
|
|
2088
|
+
role: "tab",
|
|
2089
|
+
"aria-selected": active,
|
|
2090
|
+
onClick: () => onChange(tab.value),
|
|
2091
|
+
className: cn(
|
|
2092
|
+
"relative flex shrink-0 items-center gap-1.5 whitespace-nowrap pb-1.5 text-sm transition-colors",
|
|
2093
|
+
active ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
2094
|
+
),
|
|
2095
|
+
children: [
|
|
2096
|
+
Icon ? /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0" }) : null,
|
|
2097
|
+
/* @__PURE__ */ jsx("span", { children: tab.label }),
|
|
2098
|
+
tab.count != null && /* @__PURE__ */ jsx("span", { className: cn(
|
|
2099
|
+
"ml-0.5 rounded-full px-1.5 text-[11px] tabular-nums",
|
|
2100
|
+
active ? "bg-muted text-foreground" : "bg-muted/70 text-muted-foreground"
|
|
2101
|
+
), children: tab.count }),
|
|
2102
|
+
tab.badge,
|
|
2103
|
+
active && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 rounded-full bg-foreground" })
|
|
2104
|
+
]
|
|
2105
|
+
},
|
|
2106
|
+
tab.value
|
|
2107
|
+
);
|
|
2108
|
+
})
|
|
1712
2109
|
}
|
|
1713
2110
|
);
|
|
1714
2111
|
}
|
|
1715
|
-
function
|
|
2112
|
+
function PageTabs({ actions, className, ...props }) {
|
|
1716
2113
|
return /* @__PURE__ */ jsxs(
|
|
1717
2114
|
"div",
|
|
1718
2115
|
{
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
2116
|
+
className: cn(
|
|
2117
|
+
"-mx-3 -mt-3 mb-4 flex flex-wrap items-end justify-between gap-3 border-b border-border px-3 pt-2 md:-mx-6 md:-mt-4 md:px-6",
|
|
2118
|
+
className
|
|
2119
|
+
),
|
|
1722
2120
|
children: [
|
|
1723
|
-
/* @__PURE__ */ jsx(
|
|
1724
|
-
/* @__PURE__ */ jsx(
|
|
2121
|
+
/* @__PURE__ */ jsx(HeaderTabs, { ...props, className: "-mb-px" }),
|
|
2122
|
+
actions && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2 pb-1.5", children: actions })
|
|
1725
2123
|
]
|
|
1726
2124
|
}
|
|
1727
2125
|
);
|
|
1728
2126
|
}
|
|
1729
|
-
|
|
1730
|
-
var NavTransitionContext = React.createContext("slide");
|
|
2127
|
+
var NavTransitionContext = React7.createContext("slide");
|
|
1731
2128
|
function NavTransitionProvider({ value, children }) {
|
|
1732
2129
|
return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
|
|
1733
2130
|
}
|
|
1734
2131
|
function useNavTransition() {
|
|
1735
|
-
return
|
|
2132
|
+
return React7.useContext(NavTransitionContext);
|
|
1736
2133
|
}
|
|
1737
2134
|
function resolveTransitionClass(transition, direction) {
|
|
1738
2135
|
if (transition === "none") return "";
|
|
@@ -1751,8 +2148,14 @@ function PageTransition({
|
|
|
1751
2148
|
const animClass = resolveTransitionClass(transition, direction);
|
|
1752
2149
|
return /* @__PURE__ */ jsx("div", { className: cn(animClass, className), children }, transitionKey);
|
|
1753
2150
|
}
|
|
2151
|
+
function PageRail({ children }) {
|
|
2152
|
+
const slot = usePageRailSlot();
|
|
2153
|
+
if (slot) return createPortal(children, slot);
|
|
2154
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
2155
|
+
}
|
|
1754
2156
|
function PageHeaderActions({ children }) {
|
|
1755
2157
|
const slot = useModuleHeaderActionsSlot();
|
|
2158
|
+
useRegisterPageActions();
|
|
1756
2159
|
if (slot) return createPortal(children, slot);
|
|
1757
2160
|
return /* @__PURE__ */ jsx("div", { className: "mb-4 flex items-center justify-end gap-2", children });
|
|
1758
2161
|
}
|
|
@@ -1761,15 +2164,15 @@ function PremiumBadge({ className }) {
|
|
|
1761
2164
|
const tip = t("upgrade.badgeTooltip");
|
|
1762
2165
|
return /* @__PURE__ */ jsx("span", { title: tip, "aria-label": tip, className: cn("inline-flex shrink-0", className), children: /* @__PURE__ */ jsx(Crown, { className: "h-3.5 w-3.5 text-amber-500", "aria-hidden": true }) });
|
|
1763
2166
|
}
|
|
1764
|
-
var ModuleLayoutContext =
|
|
2167
|
+
var ModuleLayoutContext = React7.createContext("rail");
|
|
1765
2168
|
function ModuleLayoutProvider({ variant, children }) {
|
|
1766
2169
|
return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
|
|
1767
2170
|
}
|
|
1768
2171
|
function useModuleLayout() {
|
|
1769
|
-
return
|
|
2172
|
+
return React7.useContext(ModuleLayoutContext);
|
|
1770
2173
|
}
|
|
1771
|
-
function
|
|
1772
|
-
const [expanded, setExpanded] =
|
|
2174
|
+
function RailNavItem({ item }) {
|
|
2175
|
+
const [expanded, setExpanded] = React7.useState(true);
|
|
1773
2176
|
const Icon = item.icon ? ICON_MAP2[item.icon] ?? null : null;
|
|
1774
2177
|
const hasChildren = item.children && item.children.length > 0;
|
|
1775
2178
|
const hasActiveChild = hasChildren && item.children.some((c) => c.active);
|
|
@@ -1811,9 +2214,9 @@ function NavItem2({ item }) {
|
|
|
1811
2214
|
] });
|
|
1812
2215
|
}
|
|
1813
2216
|
function MobileTabs({ nav }) {
|
|
1814
|
-
const scrollRef =
|
|
1815
|
-
const [expandedId, setExpandedId] =
|
|
1816
|
-
|
|
2217
|
+
const scrollRef = React7.useRef(null);
|
|
2218
|
+
const [expandedId, setExpandedId] = React7.useState(null);
|
|
2219
|
+
React7.useEffect(() => {
|
|
1817
2220
|
const container = scrollRef.current;
|
|
1818
2221
|
if (!container) return;
|
|
1819
2222
|
const activeEl = container.querySelector('[data-active="true"]');
|
|
@@ -1880,12 +2283,12 @@ function MobileTabs({ nav }) {
|
|
|
1880
2283
|
})()
|
|
1881
2284
|
] });
|
|
1882
2285
|
}
|
|
1883
|
-
var BackStyleContext =
|
|
2286
|
+
var BackStyleContext = React7.createContext("link");
|
|
1884
2287
|
function BackStyleProvider({ style, children }) {
|
|
1885
2288
|
return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
|
|
1886
2289
|
}
|
|
1887
2290
|
function useBackStyle() {
|
|
1888
|
-
return
|
|
2291
|
+
return React7.useContext(BackStyleContext);
|
|
1889
2292
|
}
|
|
1890
2293
|
function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle, inline }) {
|
|
1891
2294
|
const t = useTranslation();
|
|
@@ -1981,13 +2384,11 @@ function ShellSubpageHeader({
|
|
|
1981
2384
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1982
2385
|
Icon && !onBack && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
1983
2386
|
/* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel, onBack })
|
|
1984
|
-
] })
|
|
2387
|
+
] }),
|
|
2388
|
+
{ back: !!onBack }
|
|
1985
2389
|
);
|
|
1986
|
-
if (!
|
|
1987
|
-
return /* @__PURE__ */
|
|
1988
|
-
subtitle ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle }) : /* @__PURE__ */ jsx("span", {}),
|
|
1989
|
-
actions && /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions })
|
|
1990
|
-
] });
|
|
2390
|
+
if (!actions) return null;
|
|
2391
|
+
return /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions }) });
|
|
1991
2392
|
}
|
|
1992
2393
|
function ModuleHeader({ nav }) {
|
|
1993
2394
|
function defaultChild(item) {
|
|
@@ -2010,12 +2411,13 @@ function ModuleHeader({ nav }) {
|
|
|
2010
2411
|
if (hasChildren) defaultChild(item)?.onClick?.();
|
|
2011
2412
|
else item.onClick?.();
|
|
2012
2413
|
};
|
|
2013
|
-
return /* @__PURE__ */ jsx(
|
|
2414
|
+
return /* @__PURE__ */ jsx(PageTabs, { tabs, value: activeItem?.id ?? nav[0]?.id ?? "", onChange });
|
|
2014
2415
|
}
|
|
2015
|
-
function ModulePage({ title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
|
|
2416
|
+
function ModulePage({ title: _title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
|
|
2016
2417
|
const ctxVariant = useModuleLayout();
|
|
2017
2418
|
const variant = navVariant ?? ctxVariant;
|
|
2018
2419
|
const transitionKey = viewKey ?? "view";
|
|
2420
|
+
const onSubpage = useHasPageHeaderBack();
|
|
2019
2421
|
if (variant === "sidebar") {
|
|
2020
2422
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2021
2423
|
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
@@ -2024,148 +2426,24 @@ function ModulePage({ title, subtitle, nav, children, className, headerAction, s
|
|
|
2024
2426
|
}
|
|
2025
2427
|
if (variant === "tabs") {
|
|
2026
2428
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2027
|
-
/* @__PURE__ */ jsx(ModuleHeader, { nav }),
|
|
2429
|
+
!onSubpage && /* @__PURE__ */ jsx(ModuleHeader, { nav }),
|
|
2028
2430
|
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
2029
2431
|
/* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, className: cn("min-w-0", className), children })
|
|
2030
2432
|
] });
|
|
2031
2433
|
}
|
|
2032
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("
|
|
2033
|
-
/* @__PURE__ */ jsx(
|
|
2034
|
-
/* @__PURE__ */
|
|
2035
|
-
|
|
2036
|
-
/* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(NavItem2, { item }, item.id)) })
|
|
2037
|
-
] }),
|
|
2038
|
-
sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto", children: sidebarFooter })
|
|
2434
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("min-w-0", className), children: [
|
|
2435
|
+
/* @__PURE__ */ jsx(PageRail, { children: /* @__PURE__ */ jsxs("div", { className: "hidden w-48 shrink-0 flex-col overflow-y-auto border-r border-border p-3 no-print md:flex", children: [
|
|
2436
|
+
/* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(RailNavItem, { item }, item.id)) }),
|
|
2437
|
+
sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto pt-3", children: sidebarFooter })
|
|
2039
2438
|
] }) }),
|
|
2040
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
2041
|
-
/* @__PURE__ */ jsx(
|
|
2042
|
-
showHeader && /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-start justify-between gap-3", children: [
|
|
2043
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
2044
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: title }),
|
|
2045
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle })
|
|
2046
|
-
] }),
|
|
2047
|
-
headerAction
|
|
2048
|
-
] }),
|
|
2439
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2440
|
+
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
2049
2441
|
/* @__PURE__ */ jsx(MobileTabs, { nav }),
|
|
2050
2442
|
/* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
|
|
2051
2443
|
] })
|
|
2052
2444
|
] });
|
|
2053
2445
|
}
|
|
2054
|
-
var EMPTY = [];
|
|
2055
|
-
var PageTrailContext = React.createContext(EMPTY);
|
|
2056
|
-
function PageTrail({
|
|
2057
|
-
label,
|
|
2058
|
-
href,
|
|
2059
|
-
onNavigate,
|
|
2060
|
-
children
|
|
2061
|
-
}) {
|
|
2062
|
-
const parent = React.useContext(PageTrailContext);
|
|
2063
|
-
const trail = React.useMemo(
|
|
2064
|
-
() => [...parent, { label, href, onNavigate }],
|
|
2065
|
-
[parent, label, href, onNavigate]
|
|
2066
|
-
);
|
|
2067
|
-
return /* @__PURE__ */ jsx(PageTrailContext.Provider, { value: trail, children });
|
|
2068
|
-
}
|
|
2069
|
-
function usePageTrail() {
|
|
2070
|
-
return React.useContext(PageTrailContext);
|
|
2071
|
-
}
|
|
2072
|
-
function usePageTrailParent(href) {
|
|
2073
|
-
const trail = React.useContext(PageTrailContext);
|
|
2074
|
-
return React.useMemo(() => {
|
|
2075
|
-
const above = href ? trail.filter((c) => c.href !== href) : trail;
|
|
2076
|
-
return above[above.length - 1];
|
|
2077
|
-
}, [trail, href]);
|
|
2078
|
-
}
|
|
2079
|
-
function navigateToCrumb(crumb) {
|
|
2080
|
-
if (crumb.onNavigate) crumb.onNavigate();
|
|
2081
|
-
else window.location.hash = crumb.href;
|
|
2082
|
-
}
|
|
2083
|
-
var ContentSplitContext = React.createContext(null);
|
|
2084
|
-
function ContentSplit({
|
|
2085
|
-
children,
|
|
2086
|
-
aside,
|
|
2087
|
-
asideTitle,
|
|
2088
|
-
asideDescription,
|
|
2089
|
-
asideWidth = "20rem",
|
|
2090
|
-
asideOpen,
|
|
2091
|
-
onAsideOpenChange,
|
|
2092
|
-
collapsible = false,
|
|
2093
|
-
defaultCollapsed = false,
|
|
2094
|
-
className
|
|
2095
|
-
}) {
|
|
2096
|
-
const [internalOpen, setInternalOpen] = React.useState(false);
|
|
2097
|
-
const open = asideOpen ?? internalOpen;
|
|
2098
|
-
const setOpen = onAsideOpenChange ?? setInternalOpen;
|
|
2099
|
-
const [collapsed, setCollapsed] = React.useState(collapsible && defaultCollapsed);
|
|
2100
|
-
return /* @__PURE__ */ jsxs(ContentSplitContext.Provider, { value: { open, setOpen }, children: [
|
|
2101
|
-
/* @__PURE__ */ jsxs(
|
|
2102
|
-
"div",
|
|
2103
|
-
{
|
|
2104
|
-
className: cn("lg:grid lg:items-start lg:gap-6", className),
|
|
2105
|
-
style: { gridTemplateColumns: `minmax(0, 1fr) ${collapsed ? "auto" : asideWidth}` },
|
|
2106
|
-
children: [
|
|
2107
|
-
/* @__PURE__ */ jsx("div", { className: "min-w-0", children }),
|
|
2108
|
-
/* @__PURE__ */ jsx("aside", { className: "hidden lg:block lg:sticky lg:top-4", children: collapsed ? /* @__PURE__ */ jsx(
|
|
2109
|
-
Button,
|
|
2110
|
-
{
|
|
2111
|
-
variant: "outline",
|
|
2112
|
-
size: "sm",
|
|
2113
|
-
onClick: () => setCollapsed(false),
|
|
2114
|
-
className: "h-9 w-9 p-0",
|
|
2115
|
-
"aria-label": typeof asideTitle === "string" ? asideTitle : void 0,
|
|
2116
|
-
title: typeof asideTitle === "string" ? asideTitle : void 0,
|
|
2117
|
-
children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-4 w-4" })
|
|
2118
|
-
}
|
|
2119
|
-
) : /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-border bg-card", children: [
|
|
2120
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 border-b border-border px-4 py-3", children: [
|
|
2121
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2122
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: asideTitle }),
|
|
2123
|
-
asideDescription && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: asideDescription })
|
|
2124
|
-
] }),
|
|
2125
|
-
collapsible && /* @__PURE__ */ jsx(
|
|
2126
|
-
"button",
|
|
2127
|
-
{
|
|
2128
|
-
type: "button",
|
|
2129
|
-
onClick: () => setCollapsed(true),
|
|
2130
|
-
className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted/60 hover:text-foreground",
|
|
2131
|
-
"aria-label": "collapse",
|
|
2132
|
-
children: /* @__PURE__ */ jsx(PanelRightClose, { className: "h-4 w-4" })
|
|
2133
|
-
}
|
|
2134
|
-
)
|
|
2135
|
-
] }),
|
|
2136
|
-
/* @__PURE__ */ jsx("div", { className: "p-4", children: aside })
|
|
2137
|
-
] }) })
|
|
2138
|
-
]
|
|
2139
|
-
}
|
|
2140
|
-
),
|
|
2141
|
-
/* @__PURE__ */ jsx("div", { className: "lg:hidden", children: /* @__PURE__ */ jsx(Sheet, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(SheetContent, { width: "max-w-sm", children: [
|
|
2142
|
-
/* @__PURE__ */ jsxs(SheetHeader, { children: [
|
|
2143
|
-
/* @__PURE__ */ jsx(SheetTitle, { children: asideTitle }),
|
|
2144
|
-
asideDescription && /* @__PURE__ */ jsx(SheetDescription, { children: asideDescription })
|
|
2145
|
-
] }),
|
|
2146
|
-
/* @__PURE__ */ jsx(SheetBody, { children: aside })
|
|
2147
|
-
] }) }) })
|
|
2148
|
-
] });
|
|
2149
|
-
}
|
|
2150
|
-
function ContentSplitTrigger({ label, iconOnly, className }) {
|
|
2151
|
-
const ctx = React.useContext(ContentSplitContext);
|
|
2152
|
-
if (!ctx) return null;
|
|
2153
|
-
return /* @__PURE__ */ jsxs(
|
|
2154
|
-
Button,
|
|
2155
|
-
{
|
|
2156
|
-
variant: "outline",
|
|
2157
|
-
size: "sm",
|
|
2158
|
-
onClick: () => ctx.setOpen(true),
|
|
2159
|
-
className: cn("lg:hidden", className),
|
|
2160
|
-
"aria-label": typeof label === "string" ? label : void 0,
|
|
2161
|
-
children: [
|
|
2162
|
-
/* @__PURE__ */ jsx(PanelRight, { className: cn("h-4 w-4", !iconOnly && "mr-1.5") }),
|
|
2163
|
-
!iconOnly && label
|
|
2164
|
-
]
|
|
2165
|
-
}
|
|
2166
|
-
);
|
|
2167
|
-
}
|
|
2168
2446
|
|
|
2169
|
-
export { AppShell,
|
|
2170
|
-
//# sourceMappingURL=chunk-
|
|
2171
|
-
//# sourceMappingURL=chunk-
|
|
2447
|
+
export { AppShell, BackStyleProvider, HeaderTabs, ModuleLayoutProvider, ModulePage, NavTransitionProvider, PageHeader, PageHeaderActions, PageHeaderCrumb, PageRail, PageTabs, PageTransition, RailNavItem, SaveBar, SaveBarProvider, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SubpageHeader, Topbar, useBackHandler, useBackStyle, useCrossModuleBack, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useNavTransition, useRailRow, useSaveBar, useSaveBarVisible };
|
|
2448
|
+
//# sourceMappingURL=chunk-MVH64JFG.js.map
|
|
2449
|
+
//# sourceMappingURL=chunk-MVH64JFG.js.map
|