@fayz-ai/ui 0.12.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/AnalyticsControls.d.ts +5 -1
- package/dist/analytics/AnalyticsControls.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-7BHG3XGJ.js → chunk-77CUIMOI.js} +201 -119
- 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-ZDRHCKRK.js → chunk-MVH64JFG.js} +1029 -767
- 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 +69 -96
- 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/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 +2 -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/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 +54 -17
- package/package.json +5 -5
- package/dist/chunk-22EVFMS3.js.map +0 -1
- package/dist/chunk-7BHG3XGJ.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-VAKWUUVC.js +0 -425
- package/dist/chunk-VAKWUUVC.js.map +0 -1
- package/dist/chunk-ZBQ772DU.js +0 -17
- package/dist/chunk-ZBQ772DU.js.map +0 -1
- package/dist/chunk-ZDRHCKRK.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,7 +928,7 @@ 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(
|
|
@@ -314,8 +994,8 @@ function NavGroup({
|
|
|
314
994
|
flyout
|
|
315
995
|
}) {
|
|
316
996
|
const isChildActive = item.children?.some((c) => c.route === activeRoute);
|
|
317
|
-
const [open, setOpen] =
|
|
318
|
-
const Icon =
|
|
997
|
+
const [open, setOpen] = React7.useState(() => isChildActive ?? false);
|
|
998
|
+
const Icon = getIcon2(item.icon);
|
|
319
999
|
if (collapsed) {
|
|
320
1000
|
const hoverProps = flyout?.rowProps(flyoutContentOf(item));
|
|
321
1001
|
return /* @__PURE__ */ jsx(
|
|
@@ -370,7 +1050,7 @@ function NavGroup({
|
|
|
370
1050
|
// along with the labels and land the arrowhead on top of the text.
|
|
371
1051
|
childConnector ? "relative" : "ml-4 space-y-0.5 border-l border-sidebar-border/50 pl-3"
|
|
372
1052
|
), children: item.children?.map((child, childIndex) => {
|
|
373
|
-
const ChildIcon =
|
|
1053
|
+
const ChildIcon = getIcon2(child.icon);
|
|
374
1054
|
const isActive = child.route === activeRoute;
|
|
375
1055
|
const childExternal = !!child.href;
|
|
376
1056
|
const childClass = cn(
|
|
@@ -388,7 +1068,7 @@ function NavGroup({
|
|
|
388
1068
|
child.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 shrink-0 text-amber-500", "aria-hidden": true })
|
|
389
1069
|
] });
|
|
390
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 });
|
|
391
|
-
if (!childConnector) return /* @__PURE__ */ jsx(
|
|
1071
|
+
if (!childConnector) return /* @__PURE__ */ jsx(React7.Fragment, { children: row }, child.id);
|
|
392
1072
|
const activeIndex = item.children.findIndex((c) => c.route === activeRoute);
|
|
393
1073
|
const onPath = activeIndex >= 0 && childIndex < activeIndex;
|
|
394
1074
|
return (
|
|
@@ -450,9 +1130,9 @@ function NavGroup({
|
|
|
450
1130
|
] });
|
|
451
1131
|
}
|
|
452
1132
|
function withCollapsed(slot, collapsed) {
|
|
453
|
-
if (!
|
|
1133
|
+
if (!React7.isValidElement(slot)) return slot;
|
|
454
1134
|
if (typeof slot.type === "string") return slot;
|
|
455
|
-
return
|
|
1135
|
+
return React7.cloneElement(slot, { collapsed });
|
|
456
1136
|
}
|
|
457
1137
|
function Sidebar({
|
|
458
1138
|
navigation,
|
|
@@ -479,7 +1159,7 @@ function Sidebar({
|
|
|
479
1159
|
const currentPath = currentPathProp;
|
|
480
1160
|
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
481
1161
|
const activeRoute = resolveActiveRoute(navigation, currentPath);
|
|
482
|
-
const railRef =
|
|
1162
|
+
const railRef = React7.useRef(null);
|
|
483
1163
|
const flyout = useRailFlyout({
|
|
484
1164
|
enabled: collapsed,
|
|
485
1165
|
railRef,
|
|
@@ -529,10 +1209,10 @@ function Sidebar({
|
|
|
529
1209
|
mainItems.length > 0 && renderSection(mainItems, sectionLabels?.main),
|
|
530
1210
|
secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6", children: renderSection(secondaryItems, sectionLabels?.secondary) })
|
|
531
1211
|
] });
|
|
532
|
-
const [outgoing, setOutgoing] =
|
|
533
|
-
const lastBodyRef =
|
|
534
|
-
const lastKeyRef =
|
|
535
|
-
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);
|
|
536
1216
|
useIsomorphicLayoutEffect2(() => {
|
|
537
1217
|
if (lastKeyRef.current !== navKey) {
|
|
538
1218
|
const previousBody = lastBodyRef.current;
|
|
@@ -545,539 +1225,183 @@ function Sidebar({
|
|
|
545
1225
|
}
|
|
546
1226
|
lastBodyRef.current = navBody;
|
|
547
1227
|
});
|
|
548
|
-
|
|
1228
|
+
React7.useEffect(() => () => {
|
|
549
1229
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
550
1230
|
}, []);
|
|
551
|
-
return
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
"
|
|
556
|
-
onBlur: (event) => {
|
|
557
|
-
if (!event.currentTarget.contains(event.relatedTarget)) flyout.dismiss();
|
|
558
|
-
},
|
|
559
|
-
className: cn(
|
|
560
|
-
// `relative` anchors the flyout, which hangs off the rail's right edge
|
|
561
|
-
// and has to escape the nav's own scroll box to do it.
|
|
562
|
-
"relative flex h-full flex-col bg-sidebar text-sidebar-foreground transition-all duration-200",
|
|
563
|
-
!borderless && "border-r border-sidebar-border",
|
|
564
|
-
// 14rem expanded. It was 16rem, which is the width a rail needs when
|
|
565
|
-
// its entries are sentences ("GlossGenius Financing", "Configure your
|
|
566
|
-
// payment methods"); these are one or two words, so the extra 2rem was
|
|
567
|
-
// paid for by the content column on every screen in every app. The
|
|
568
|
-
// labels still truncate, and a long one now truncates 32px earlier —
|
|
569
|
-
// which is the trade, taken deliberately.
|
|
570
|
-
collapsed ? "w-16" : "w-56"
|
|
571
|
-
),
|
|
572
|
-
children: [
|
|
573
|
-
/* @__PURE__ */ jsxs(
|
|
574
|
-
"div",
|
|
575
|
-
{
|
|
576
|
-
className: cn(
|
|
577
|
-
"flex h-14 items-center justify-between px-4",
|
|
578
|
-
!borderless && "border-b border-sidebar-border",
|
|
579
|
-
collapsed && "justify-center px-2"
|
|
580
|
-
),
|
|
581
|
-
children: [
|
|
582
|
-
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
583
|
-
logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }),
|
|
584
|
-
/* @__PURE__ */ jsx(
|
|
585
|
-
"button",
|
|
586
|
-
{
|
|
587
|
-
onClick: onToggle,
|
|
588
|
-
className: "ml-auto rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
589
|
-
"aria-label": "Collapse sidebar",
|
|
590
|
-
children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
|
|
591
|
-
}
|
|
592
|
-
)
|
|
593
|
-
] }),
|
|
594
|
-
collapsed && /* @__PURE__ */ jsx(
|
|
595
|
-
"button",
|
|
596
|
-
{
|
|
597
|
-
onClick: onToggle,
|
|
598
|
-
className: "rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
599
|
-
"aria-label": "Expand sidebar",
|
|
600
|
-
children: /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" })
|
|
601
|
-
}
|
|
602
|
-
)
|
|
603
|
-
]
|
|
604
|
-
}
|
|
605
|
-
),
|
|
606
|
-
topContent && /* @__PURE__ */ jsx("div", { className: "px-2 pt-2", children: withCollapsed(topContent, collapsed) }),
|
|
607
|
-
/* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: /* @__PURE__ */ jsxs(
|
|
608
|
-
"button",
|
|
609
|
-
{
|
|
610
|
-
onClick: () => setCommandPaletteOpen(true),
|
|
611
|
-
"aria-keyshortcuts": "Meta+K Control+K",
|
|
612
|
-
className: cn(
|
|
613
|
-
// Tonal surface, no outline. On a dark rail a 1px border is a hard
|
|
614
|
-
// edge competing with every other hard edge; a lighter fill of the
|
|
615
|
-
// same hue reads as "a field" without drawing a box around it — and
|
|
616
|
-
// leaves the outline treatment free to mean focus, which it must.
|
|
617
|
-
"fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
|
|
618
|
-
"hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
|
|
619
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
|
|
620
|
-
collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
|
|
621
|
-
),
|
|
622
|
-
"aria-label": searchLabel,
|
|
623
|
-
title: collapsed ? searchLabel : void 0,
|
|
624
|
-
children: [
|
|
625
|
-
/* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
|
|
626
|
-
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
627
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
|
|
628
|
-
/* @__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: [
|
|
629
|
-
shortcutKey(),
|
|
630
|
-
"K"
|
|
631
|
-
] })
|
|
632
|
-
] })
|
|
633
|
-
]
|
|
634
|
-
}
|
|
635
|
-
) }),
|
|
636
|
-
/* @__PURE__ */ jsxs(
|
|
637
|
-
"nav",
|
|
638
|
-
{
|
|
639
|
-
className: cn(
|
|
640
|
-
"relative flex-1 p-2",
|
|
641
|
-
// scrollbar-none: the rail still scrolls, but without the native
|
|
642
|
-
// scrollbar gutter eating into the nav items.
|
|
643
|
-
outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
|
|
644
|
-
),
|
|
645
|
-
children: [
|
|
646
|
-
outgoing && /* @__PURE__ */ jsx(
|
|
647
|
-
"div",
|
|
648
|
-
{
|
|
649
|
-
"aria-hidden": true,
|
|
650
|
-
className: cn(
|
|
651
|
-
"pointer-events-none absolute inset-x-2 top-2 space-y-1",
|
|
652
|
-
outgoing.direction === "back" ? "saas-rail-out-back" : "saas-rail-out-forward"
|
|
653
|
-
),
|
|
654
|
-
children: outgoing.body
|
|
655
|
-
}
|
|
656
|
-
),
|
|
657
|
-
/* @__PURE__ */ jsx(
|
|
658
|
-
"div",
|
|
659
|
-
{
|
|
660
|
-
className: cn(
|
|
661
|
-
"space-y-1",
|
|
662
|
-
outgoing && (outgoing.direction === "back" ? "saas-rail-in-back" : "saas-rail-in-forward")
|
|
663
|
-
),
|
|
664
|
-
children: navBody
|
|
665
|
-
},
|
|
666
|
-
navKey
|
|
667
|
-
)
|
|
668
|
-
]
|
|
669
|
-
}
|
|
670
|
-
),
|
|
671
|
-
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(
|
|
672
|
-
NavItem,
|
|
673
|
-
{
|
|
674
|
-
item,
|
|
675
|
-
collapsed,
|
|
676
|
-
isActive: item.route === activeRoute,
|
|
677
|
-
onNavigate: handleNavigate,
|
|
678
|
-
flyout
|
|
679
|
-
},
|
|
680
|
-
item.id
|
|
681
|
-
)) }),
|
|
682
|
-
footerContent && // `empty:hidden` — slot children may all render null (e.g. the
|
|
683
|
-
// SeuSiteWidget self-hides when the tenant has no project); without
|
|
684
|
-
// it the padded/bordered wrapper shows as a blank block.
|
|
685
|
-
/* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
|
|
686
|
-
withCollapsed(orgSwitcher, collapsed),
|
|
687
|
-
/* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
688
|
-
"flex items-center rounded-button",
|
|
689
|
-
collapsed ? "flex-col gap-1" : "gap-1 px-1"
|
|
690
|
-
), children: [
|
|
691
|
-
userMenuSlot,
|
|
692
|
-
!collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
693
|
-
notificationSlot ?? /* @__PURE__ */ jsxs(
|
|
694
|
-
"button",
|
|
695
|
-
{
|
|
696
|
-
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",
|
|
697
|
-
"aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
|
|
698
|
-
children: [
|
|
699
|
-
/* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
|
|
700
|
-
unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
|
|
701
|
-
]
|
|
702
|
-
}
|
|
703
|
-
)
|
|
704
|
-
] }) }),
|
|
705
|
-
flyout.panel
|
|
706
|
-
]
|
|
707
|
-
}
|
|
708
|
-
);
|
|
709
|
-
}
|
|
710
|
-
function TopbarUserMenu({
|
|
711
|
-
user,
|
|
712
|
-
userPlan,
|
|
713
|
-
billingLabel = "Subscription",
|
|
714
|
-
onSignOut,
|
|
715
|
-
onProfile,
|
|
716
|
-
onSettings,
|
|
717
|
-
onBilling,
|
|
718
|
-
userMenuExtras
|
|
719
|
-
}) {
|
|
720
|
-
const [open, setOpen] = React.useState(false);
|
|
721
|
-
if (!user) return null;
|
|
722
|
-
const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
|
|
723
|
-
const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
|
|
724
|
-
"button",
|
|
725
|
-
{
|
|
726
|
-
onClick: () => {
|
|
727
|
-
setOpen(false);
|
|
728
|
-
onClick();
|
|
729
|
-
},
|
|
730
|
-
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",
|
|
731
|
-
children: [
|
|
732
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground" }),
|
|
733
|
-
/* @__PURE__ */ jsx("span", { children: label })
|
|
734
|
-
]
|
|
735
|
-
},
|
|
736
|
-
label
|
|
737
|
-
) : null;
|
|
738
|
-
return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
|
|
739
|
-
/* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
740
|
-
"button",
|
|
741
|
-
{
|
|
742
|
-
"aria-label": "User menu",
|
|
743
|
-
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",
|
|
744
|
-
children: user.avatarUrl ? (
|
|
745
|
-
// eslint-disable-next-line @next/next/no-img-element
|
|
746
|
-
/* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" })
|
|
747
|
-
) : initial
|
|
748
|
-
}
|
|
749
|
-
) }),
|
|
750
|
-
/* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(
|
|
751
|
-
Popover.Content,
|
|
752
|
-
{
|
|
753
|
-
align: "end",
|
|
754
|
-
sideOffset: 8,
|
|
755
|
-
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",
|
|
756
|
-
children: [
|
|
757
|
-
/* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
|
|
758
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: user.fullName || user.email }),
|
|
759
|
-
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
|
|
760
|
-
] }),
|
|
761
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5", children: [
|
|
762
|
-
item("Perfil", onProfile, UserCog),
|
|
763
|
-
item("Configura\xE7\xF5es", onSettings, Settings),
|
|
764
|
-
onBilling && /* @__PURE__ */ jsxs(
|
|
765
|
-
"button",
|
|
766
|
-
{
|
|
767
|
-
onClick: () => {
|
|
768
|
-
setOpen(false);
|
|
769
|
-
onBilling();
|
|
770
|
-
},
|
|
771
|
-
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",
|
|
772
|
-
children: [
|
|
773
|
-
/* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" }),
|
|
774
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: billingLabel }),
|
|
775
|
-
userPlan && /* @__PURE__ */ jsxs(
|
|
776
|
-
"span",
|
|
777
|
-
{
|
|
778
|
-
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",
|
|
779
|
-
children: [
|
|
780
|
-
userPlan.paid ? /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
|
|
781
|
-
userPlan.label
|
|
782
|
-
]
|
|
783
|
-
}
|
|
784
|
-
)
|
|
785
|
-
]
|
|
786
|
-
},
|
|
787
|
-
"billing"
|
|
788
|
-
),
|
|
789
|
-
userMenuExtras?.map((e) => item(e.label, e.onClick)),
|
|
790
|
-
onSignOut && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
791
|
-
/* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
|
|
792
|
-
item("Sair", onSignOut, LogOut)
|
|
793
|
-
] })
|
|
794
|
-
] })
|
|
795
|
-
]
|
|
796
|
-
}
|
|
797
|
-
) })
|
|
798
|
-
] });
|
|
799
|
-
}
|
|
800
|
-
var ICON_MAP2 = {
|
|
801
|
-
Home: Home,
|
|
802
|
-
Users,
|
|
803
|
-
Settings: Settings,
|
|
804
|
-
CreditCard,
|
|
805
|
-
Bell: Bell,
|
|
806
|
-
Calendar,
|
|
807
|
-
CalendarClock,
|
|
808
|
-
Package,
|
|
809
|
-
Activity,
|
|
810
|
-
BarChart3,
|
|
811
|
-
FileText,
|
|
812
|
-
Mail,
|
|
813
|
-
DollarSign,
|
|
814
|
-
Megaphone,
|
|
815
|
-
ShoppingCart,
|
|
816
|
-
ShoppingBag,
|
|
817
|
-
Target,
|
|
818
|
-
Wrench,
|
|
819
|
-
ClipboardList,
|
|
820
|
-
ClipboardCheck,
|
|
821
|
-
Briefcase,
|
|
822
|
-
UserCog,
|
|
823
|
-
BookOpen,
|
|
824
|
-
MessageCircle,
|
|
825
|
-
Globe,
|
|
826
|
-
BookOpenCheck,
|
|
827
|
-
Percent,
|
|
828
|
-
Tag,
|
|
829
|
-
Tags,
|
|
830
|
-
Camera,
|
|
831
|
-
UtensilsCrossed,
|
|
832
|
-
Search: Search,
|
|
833
|
-
MapPin,
|
|
834
|
-
Handshake,
|
|
835
|
-
Contact,
|
|
836
|
-
Building2,
|
|
837
|
-
ChevronDown: ChevronDown,
|
|
838
|
-
Filter,
|
|
839
|
-
Plus,
|
|
840
|
-
Shield,
|
|
841
|
-
List,
|
|
842
|
-
ListChecks,
|
|
843
|
-
FolderOpen,
|
|
844
|
-
Dog,
|
|
845
|
-
Cat,
|
|
846
|
-
PawPrint,
|
|
847
|
-
Heart,
|
|
848
|
-
LayoutTemplate,
|
|
849
|
-
LeafyGreen,
|
|
850
|
-
Apple,
|
|
851
|
-
Egg,
|
|
852
|
-
Wheat,
|
|
853
|
-
ArrowDownCircle,
|
|
854
|
-
ArrowUpCircle,
|
|
855
|
-
ArrowLeftRight,
|
|
856
|
-
Plug,
|
|
857
|
-
SlidersHorizontal,
|
|
858
|
-
TableProperties,
|
|
859
|
-
Landmark,
|
|
860
|
-
Receipt,
|
|
861
|
-
TrendingUp,
|
|
862
|
-
CircleDollarSign,
|
|
863
|
-
Clock,
|
|
864
|
-
AlertTriangle,
|
|
865
|
-
Sparkles,
|
|
866
|
-
Wallet,
|
|
867
|
-
Warehouse,
|
|
868
|
-
Ruler,
|
|
869
|
-
ArrowUpRight,
|
|
870
|
-
ArrowDownRight,
|
|
871
|
-
UserPlus,
|
|
872
|
-
UserX,
|
|
873
|
-
TreePalm,
|
|
874
|
-
Boxes,
|
|
875
|
-
Box,
|
|
876
|
-
FileCheck2,
|
|
877
|
-
PartyPopper,
|
|
878
|
-
Radio,
|
|
879
|
-
CalendarDays,
|
|
880
|
-
BadgeDollarSign,
|
|
881
|
-
Banknote,
|
|
882
|
-
Layers,
|
|
883
|
-
Music,
|
|
884
|
-
Eye,
|
|
885
|
-
ListMusic,
|
|
886
|
-
Disc3,
|
|
887
|
-
UsersRound,
|
|
888
|
-
Mic,
|
|
889
|
-
CalendarCheck2,
|
|
890
|
-
CalendarX,
|
|
891
|
-
ListPlus,
|
|
892
|
-
Map,
|
|
893
|
-
Ban,
|
|
894
|
-
Clock3,
|
|
895
|
-
Inbox,
|
|
896
|
-
UserCheck,
|
|
897
|
-
Star,
|
|
898
|
-
Zap,
|
|
899
|
-
Clapperboard,
|
|
900
|
-
Newspaper,
|
|
901
|
-
GraduationCap,
|
|
902
|
-
Repeat
|
|
903
|
-
};
|
|
904
|
-
function getIcon2(name) {
|
|
905
|
-
return ICON_MAP2[name] ?? Home;
|
|
906
|
-
}
|
|
907
|
-
function NavDropdown({ item, currentPath, onNavigate }) {
|
|
908
|
-
const [open, setOpen] = React.useState(false);
|
|
909
|
-
const Icon = getIcon2(item.icon);
|
|
910
|
-
const isChildActive = item.children?.some(
|
|
911
|
-
(child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
|
|
912
|
-
);
|
|
913
|
-
return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
|
|
914
|
-
/* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
915
|
-
"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",
|
|
916
1236
|
{
|
|
917
|
-
|
|
1237
|
+
ref: railRef,
|
|
1238
|
+
"data-print": "hide",
|
|
1239
|
+
onBlur: (event) => {
|
|
1240
|
+
if (!event.currentTarget.contains(event.relatedTarget)) flyout.dismiss();
|
|
1241
|
+
},
|
|
918
1242
|
className: cn(
|
|
919
|
-
|
|
920
|
-
|
|
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"
|
|
921
1254
|
),
|
|
922
1255
|
children: [
|
|
923
|
-
/* @__PURE__ */
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
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(
|
|
939
1291
|
"button",
|
|
940
1292
|
{
|
|
941
|
-
onClick: () =>
|
|
942
|
-
|
|
943
|
-
setOpen(false);
|
|
944
|
-
},
|
|
1293
|
+
onClick: () => setCommandPaletteOpen(true),
|
|
1294
|
+
"aria-keyshortcuts": "Meta+K Control+K",
|
|
945
1295
|
className: cn(
|
|
946
|
-
|
|
947
|
-
|
|
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"
|
|
948
1304
|
),
|
|
1305
|
+
"aria-label": searchLabel,
|
|
1306
|
+
title: collapsed ? searchLabel : void 0,
|
|
949
1307
|
children: [
|
|
950
|
-
/* @__PURE__ */ jsx(
|
|
951
|
-
/* @__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
|
+
)
|
|
952
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
|
|
953
1362
|
},
|
|
954
|
-
|
|
955
|
-
)
|
|
956
|
-
|
|
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
|
+
]
|
|
957
1390
|
}
|
|
958
1391
|
) })
|
|
959
|
-
|
|
960
|
-
}
|
|
961
|
-
function Topbar({
|
|
962
|
-
navigation,
|
|
963
|
-
logo,
|
|
964
|
-
user,
|
|
965
|
-
onMenuClick,
|
|
966
|
-
orgSwitcher,
|
|
967
|
-
leftContent,
|
|
968
|
-
rightContent,
|
|
969
|
-
currentPath = "/",
|
|
970
|
-
onNavigate,
|
|
971
|
-
searchPlaceholder = "Search...",
|
|
972
|
-
onSignOut,
|
|
973
|
-
onProfile,
|
|
974
|
-
onSettings,
|
|
975
|
-
onBilling,
|
|
976
|
-
userPlan,
|
|
977
|
-
billingLabel,
|
|
978
|
-
userMenuExtras
|
|
979
|
-
}) {
|
|
980
|
-
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
981
|
-
const mainNav = navigation.filter((item) => item.section === "main");
|
|
982
|
-
const secondaryNav = navigation.filter((item) => item.section === "secondary");
|
|
983
|
-
const allNav = [...mainNav, ...secondaryNav];
|
|
984
|
-
const handleNavigate = (route) => {
|
|
985
|
-
onNavigate?.(route);
|
|
986
|
-
};
|
|
987
|
-
return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
|
|
988
|
-
/* @__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: [
|
|
989
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
990
|
-
onMenuClick && /* @__PURE__ */ jsx(
|
|
991
|
-
"button",
|
|
992
|
-
{
|
|
993
|
-
onClick: onMenuClick,
|
|
994
|
-
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",
|
|
995
|
-
"aria-label": "Open menu",
|
|
996
|
-
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
997
|
-
}
|
|
998
|
-
),
|
|
999
|
-
/* @__PURE__ */ jsx("div", { className: "hidden shrink-0 items-center md:flex", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
|
|
1000
|
-
] }),
|
|
1001
|
-
/* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
|
|
1002
|
-
"button",
|
|
1003
|
-
{
|
|
1004
|
-
onClick: () => setCommandPaletteOpen(true),
|
|
1005
|
-
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",
|
|
1006
|
-
children: [
|
|
1007
|
-
/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-sidebar-muted" }),
|
|
1008
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs", children: searchPlaceholder }),
|
|
1009
|
-
/* @__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" })
|
|
1010
|
-
]
|
|
1011
|
-
}
|
|
1012
|
-
) }),
|
|
1013
|
-
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [
|
|
1014
|
-
orgSwitcher && /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 items-center md:flex", children: orgSwitcher }),
|
|
1015
|
-
leftContent,
|
|
1016
|
-
rightContent,
|
|
1017
|
-
/* @__PURE__ */ jsx(
|
|
1018
|
-
TopbarUserMenu,
|
|
1019
|
-
{
|
|
1020
|
-
user,
|
|
1021
|
-
userPlan,
|
|
1022
|
-
billingLabel,
|
|
1023
|
-
onSignOut,
|
|
1024
|
-
onProfile,
|
|
1025
|
-
onSettings,
|
|
1026
|
-
onBilling,
|
|
1027
|
-
userMenuExtras
|
|
1028
|
-
}
|
|
1029
|
-
)
|
|
1030
|
-
] })
|
|
1031
|
-
] }) }),
|
|
1032
|
-
/* @__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) => {
|
|
1033
|
-
if (!el) return;
|
|
1034
|
-
const active = el.querySelector('[data-nav-active="true"]');
|
|
1035
|
-
if (active) {
|
|
1036
|
-
const left = active.offsetLeft - el.offsetWidth / 2 + active.offsetWidth / 2;
|
|
1037
|
-
el.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
|
|
1038
|
-
}
|
|
1039
|
-
}, className: "flex items-center gap-0.5 overflow-x-auto scrollbar-none", children: allNav.map((item) => {
|
|
1040
|
-
if (item.children && item.children.length > 0) {
|
|
1041
|
-
return /* @__PURE__ */ jsx(
|
|
1042
|
-
NavDropdown,
|
|
1043
|
-
{
|
|
1044
|
-
item,
|
|
1045
|
-
currentPath,
|
|
1046
|
-
onNavigate: handleNavigate
|
|
1047
|
-
},
|
|
1048
|
-
item.id
|
|
1049
|
-
);
|
|
1050
|
-
}
|
|
1051
|
-
const Icon = getIcon2(item.icon);
|
|
1052
|
-
const isActive = item.route === "/" ? currentPath === "/" : currentPath === item.route || currentPath.startsWith(item.route + "/");
|
|
1053
|
-
return /* @__PURE__ */ jsxs(
|
|
1054
|
-
"button",
|
|
1055
|
-
{
|
|
1056
|
-
"data-nav-active": isActive || void 0,
|
|
1057
|
-
onClick: () => handleNavigate(item.route),
|
|
1058
|
-
className: cn(
|
|
1059
|
-
"flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
|
|
1060
|
-
isActive ? "border-primary font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
|
|
1061
|
-
),
|
|
1062
|
-
children: [
|
|
1063
|
-
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
1064
|
-
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
1065
|
-
item.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 text-amber-500", "aria-hidden": true }),
|
|
1066
|
-
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 })
|
|
1067
|
-
]
|
|
1068
|
-
},
|
|
1069
|
-
item.id
|
|
1070
|
-
);
|
|
1071
|
-
}) }) }) })
|
|
1072
|
-
] });
|
|
1392
|
+
);
|
|
1073
1393
|
}
|
|
1074
|
-
var ModuleHeaderSlotContext =
|
|
1394
|
+
var ModuleHeaderSlotContext = React7.createContext(null);
|
|
1075
1395
|
function useModuleHeaderSlot() {
|
|
1076
|
-
return
|
|
1396
|
+
return React7.useContext(ModuleHeaderSlotContext);
|
|
1077
1397
|
}
|
|
1078
|
-
var ModuleHeaderActionsSlotContext =
|
|
1398
|
+
var ModuleHeaderActionsSlotContext = React7.createContext(null);
|
|
1079
1399
|
function useModuleHeaderActionsSlot() {
|
|
1080
|
-
return
|
|
1400
|
+
return React7.useContext(ModuleHeaderActionsSlotContext);
|
|
1401
|
+
}
|
|
1402
|
+
var PageRailSlotContext = React7.createContext(null);
|
|
1403
|
+
function usePageRailSlot() {
|
|
1404
|
+
return React7.useContext(PageRailSlotContext);
|
|
1081
1405
|
}
|
|
1082
1406
|
function ShellPageTitle({
|
|
1083
1407
|
title,
|
|
@@ -1122,10 +1446,12 @@ function SidebarLayout({
|
|
|
1122
1446
|
const pageHeaderSlot = usePageHeaderSlot();
|
|
1123
1447
|
usePageHeaderHost();
|
|
1124
1448
|
const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
|
|
1125
|
-
const [mobileMenuOpen, setMobileMenuOpen] =
|
|
1126
|
-
const [headerSlot, setHeaderSlot] =
|
|
1127
|
-
const [actionsSlot, setActionsSlot] =
|
|
1128
|
-
|
|
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(
|
|
1129
1455
|
"shell-enter flex h-screen overflow-hidden bg-background",
|
|
1130
1456
|
// Frame mode: the inset gap around the framed card takes the sidebar (menu)
|
|
1131
1457
|
// color, so the white card reads as a panel floating on the same tone as the
|
|
@@ -1162,34 +1488,42 @@ function SidebarLayout({
|
|
|
1162
1488
|
}
|
|
1163
1489
|
) }),
|
|
1164
1490
|
/* @__PURE__ */ jsxs("div", { className: cn(
|
|
1165
|
-
"relative flex flex-1
|
|
1166
|
-
|
|
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"
|
|
1167
1496
|
), children: [
|
|
1168
|
-
/* @__PURE__ */
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
"
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
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
|
+
] })
|
|
1185
1523
|
] }),
|
|
1186
|
-
/* @__PURE__ */
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
] })
|
|
1190
|
-
] }),
|
|
1191
|
-
/* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children }),
|
|
1192
|
-
/* @__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
|
+
] })
|
|
1193
1527
|
] }),
|
|
1194
1528
|
mobileMenuOpen && /* @__PURE__ */ jsx(
|
|
1195
1529
|
MobileOverlay,
|
|
@@ -1214,7 +1548,7 @@ function SidebarLayout({
|
|
|
1214
1548
|
onClose: () => setMobileMenuOpen(false)
|
|
1215
1549
|
}
|
|
1216
1550
|
)
|
|
1217
|
-
] }) }) }) });
|
|
1551
|
+
] }) }) }) }) });
|
|
1218
1552
|
}
|
|
1219
1553
|
function TopbarLayout({
|
|
1220
1554
|
navigation = [],
|
|
@@ -1239,13 +1573,17 @@ function TopbarLayout({
|
|
|
1239
1573
|
notificationSlot,
|
|
1240
1574
|
hasBottomNav = false
|
|
1241
1575
|
}) {
|
|
1242
|
-
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);
|
|
1243
1580
|
const rawSlot = usePageHeaderSlot();
|
|
1244
1581
|
const hasOutlet = useHasPageHeaderOutlet();
|
|
1582
|
+
const hasPageActions = useHasPageActions();
|
|
1245
1583
|
usePageHeaderHost();
|
|
1246
1584
|
const pageHeaderSlot = hasOutlet ? null : rawSlot;
|
|
1247
1585
|
const showTitle = !hasOutlet && !!pageTitle;
|
|
1248
|
-
return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsxs("div", { className: "shell-enter 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: [
|
|
1249
1587
|
/* @__PURE__ */ jsx(
|
|
1250
1588
|
Topbar,
|
|
1251
1589
|
{
|
|
@@ -1270,9 +1608,18 @@ function TopbarLayout({
|
|
|
1270
1608
|
onMenuClick: hasBottomNav ? void 0 : () => setMobileMenuOpen(true)
|
|
1271
1609
|
}
|
|
1272
1610
|
),
|
|
1273
|
-
/* @__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
|
|
1274
|
-
|
|
1275
|
-
/* @__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
|
+
] })
|
|
1276
1623
|
] }) }),
|
|
1277
1624
|
/* @__PURE__ */ jsx(SaveBar, {}),
|
|
1278
1625
|
mobileMenuOpen && /* @__PURE__ */ jsx(
|
|
@@ -1292,7 +1639,7 @@ function TopbarLayout({
|
|
|
1292
1639
|
onClose: () => setMobileMenuOpen(false)
|
|
1293
1640
|
}
|
|
1294
1641
|
)
|
|
1295
|
-
] }) });
|
|
1642
|
+
] }) }) }) }) });
|
|
1296
1643
|
}
|
|
1297
1644
|
function DrawerUserRow({ user }) {
|
|
1298
1645
|
const label = user.fullName || user.email;
|
|
@@ -1316,23 +1663,23 @@ function MinimalLayout({
|
|
|
1316
1663
|
] });
|
|
1317
1664
|
}
|
|
1318
1665
|
function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
|
|
1319
|
-
const [visible, setVisible] =
|
|
1320
|
-
|
|
1666
|
+
const [visible, setVisible] = React7.useState(false);
|
|
1667
|
+
React7.useEffect(() => {
|
|
1321
1668
|
const id = requestAnimationFrame(() => setVisible(true));
|
|
1322
1669
|
return () => cancelAnimationFrame(id);
|
|
1323
1670
|
}, []);
|
|
1324
|
-
const close =
|
|
1671
|
+
const close = React7.useCallback(() => {
|
|
1325
1672
|
setVisible(false);
|
|
1326
1673
|
window.setTimeout(onClose, 250);
|
|
1327
1674
|
}, [onClose]);
|
|
1328
|
-
|
|
1675
|
+
React7.useEffect(() => {
|
|
1329
1676
|
const previous = document.body.style.overflow;
|
|
1330
1677
|
document.body.style.overflow = "hidden";
|
|
1331
1678
|
return () => {
|
|
1332
1679
|
document.body.style.overflow = previous;
|
|
1333
1680
|
};
|
|
1334
1681
|
}, []);
|
|
1335
|
-
|
|
1682
|
+
React7.useEffect(() => {
|
|
1336
1683
|
const onKey = (e) => {
|
|
1337
1684
|
if (e.key === "Escape") close();
|
|
1338
1685
|
};
|
|
@@ -1608,13 +1955,13 @@ function SplitWithRightRail({
|
|
|
1608
1955
|
maxWidth = 720,
|
|
1609
1956
|
resizeLabel = "Resize panel"
|
|
1610
1957
|
}) {
|
|
1611
|
-
const [width, setWidth] =
|
|
1612
|
-
const [dragging, setDragging] =
|
|
1613
|
-
const clamp =
|
|
1958
|
+
const [width, setWidth] = React7.useState(defaultWidth);
|
|
1959
|
+
const [dragging, setDragging] = React7.useState(false);
|
|
1960
|
+
const clamp = React7.useCallback(
|
|
1614
1961
|
(value) => Math.min(maxWidth, Math.max(minWidth, value)),
|
|
1615
1962
|
[minWidth, maxWidth]
|
|
1616
1963
|
);
|
|
1617
|
-
|
|
1964
|
+
React7.useEffect(() => {
|
|
1618
1965
|
if (!dragging) return;
|
|
1619
1966
|
const onMove = (event) => {
|
|
1620
1967
|
event.preventDefault();
|
|
@@ -1636,7 +1983,7 @@ function SplitWithRightRail({
|
|
|
1636
1983
|
document.body.style.cursor = previousCursor;
|
|
1637
1984
|
};
|
|
1638
1985
|
}, [dragging, clamp]);
|
|
1639
|
-
|
|
1986
|
+
React7.useEffect(() => {
|
|
1640
1987
|
if (dragging) return;
|
|
1641
1988
|
onWidthChange?.(width);
|
|
1642
1989
|
}, [dragging]);
|
|
@@ -1715,40 +2062,74 @@ function SplitWithRightRail({
|
|
|
1715
2062
|
] })
|
|
1716
2063
|
);
|
|
1717
2064
|
}
|
|
1718
|
-
function
|
|
2065
|
+
function HeaderTabs({
|
|
2066
|
+
tabs,
|
|
2067
|
+
value,
|
|
2068
|
+
onChange,
|
|
2069
|
+
className,
|
|
2070
|
+
...rest
|
|
2071
|
+
}) {
|
|
1719
2072
|
return /* @__PURE__ */ jsx(
|
|
1720
|
-
"
|
|
2073
|
+
"div",
|
|
1721
2074
|
{
|
|
1722
|
-
"
|
|
2075
|
+
role: "tablist",
|
|
2076
|
+
"aria-label": rest["aria-label"],
|
|
1723
2077
|
className: cn(
|
|
1724
|
-
"
|
|
1725
|
-
"motion-reduce:animate-none motion-reduce:border-t-foreground/20 motion-reduce:animate-pulse",
|
|
2078
|
+
"inline-flex max-w-full items-center gap-5 overflow-x-auto scrollbar-none",
|
|
1726
2079
|
className
|
|
1727
|
-
)
|
|
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
|
+
})
|
|
1728
2109
|
}
|
|
1729
2110
|
);
|
|
1730
2111
|
}
|
|
1731
|
-
function
|
|
2112
|
+
function PageTabs({ actions, className, ...props }) {
|
|
1732
2113
|
return /* @__PURE__ */ jsxs(
|
|
1733
2114
|
"div",
|
|
1734
2115
|
{
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
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
|
+
),
|
|
1738
2120
|
children: [
|
|
1739
|
-
/* @__PURE__ */ jsx(
|
|
1740
|
-
/* @__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 })
|
|
1741
2123
|
]
|
|
1742
2124
|
}
|
|
1743
2125
|
);
|
|
1744
2126
|
}
|
|
1745
|
-
|
|
1746
|
-
var NavTransitionContext = React.createContext("slide");
|
|
2127
|
+
var NavTransitionContext = React7.createContext("slide");
|
|
1747
2128
|
function NavTransitionProvider({ value, children }) {
|
|
1748
2129
|
return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
|
|
1749
2130
|
}
|
|
1750
2131
|
function useNavTransition() {
|
|
1751
|
-
return
|
|
2132
|
+
return React7.useContext(NavTransitionContext);
|
|
1752
2133
|
}
|
|
1753
2134
|
function resolveTransitionClass(transition, direction) {
|
|
1754
2135
|
if (transition === "none") return "";
|
|
@@ -1767,8 +2148,14 @@ function PageTransition({
|
|
|
1767
2148
|
const animClass = resolveTransitionClass(transition, direction);
|
|
1768
2149
|
return /* @__PURE__ */ jsx("div", { className: cn(animClass, className), children }, transitionKey);
|
|
1769
2150
|
}
|
|
2151
|
+
function PageRail({ children }) {
|
|
2152
|
+
const slot = usePageRailSlot();
|
|
2153
|
+
if (slot) return createPortal(children, slot);
|
|
2154
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
2155
|
+
}
|
|
1770
2156
|
function PageHeaderActions({ children }) {
|
|
1771
2157
|
const slot = useModuleHeaderActionsSlot();
|
|
2158
|
+
useRegisterPageActions();
|
|
1772
2159
|
if (slot) return createPortal(children, slot);
|
|
1773
2160
|
return /* @__PURE__ */ jsx("div", { className: "mb-4 flex items-center justify-end gap-2", children });
|
|
1774
2161
|
}
|
|
@@ -1777,15 +2164,15 @@ function PremiumBadge({ className }) {
|
|
|
1777
2164
|
const tip = t("upgrade.badgeTooltip");
|
|
1778
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 }) });
|
|
1779
2166
|
}
|
|
1780
|
-
var ModuleLayoutContext =
|
|
2167
|
+
var ModuleLayoutContext = React7.createContext("rail");
|
|
1781
2168
|
function ModuleLayoutProvider({ variant, children }) {
|
|
1782
2169
|
return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
|
|
1783
2170
|
}
|
|
1784
2171
|
function useModuleLayout() {
|
|
1785
|
-
return
|
|
2172
|
+
return React7.useContext(ModuleLayoutContext);
|
|
1786
2173
|
}
|
|
1787
|
-
function
|
|
1788
|
-
const [expanded, setExpanded] =
|
|
2174
|
+
function RailNavItem({ item }) {
|
|
2175
|
+
const [expanded, setExpanded] = React7.useState(true);
|
|
1789
2176
|
const Icon = item.icon ? ICON_MAP2[item.icon] ?? null : null;
|
|
1790
2177
|
const hasChildren = item.children && item.children.length > 0;
|
|
1791
2178
|
const hasActiveChild = hasChildren && item.children.some((c) => c.active);
|
|
@@ -1827,9 +2214,9 @@ function NavItem2({ item }) {
|
|
|
1827
2214
|
] });
|
|
1828
2215
|
}
|
|
1829
2216
|
function MobileTabs({ nav }) {
|
|
1830
|
-
const scrollRef =
|
|
1831
|
-
const [expandedId, setExpandedId] =
|
|
1832
|
-
|
|
2217
|
+
const scrollRef = React7.useRef(null);
|
|
2218
|
+
const [expandedId, setExpandedId] = React7.useState(null);
|
|
2219
|
+
React7.useEffect(() => {
|
|
1833
2220
|
const container = scrollRef.current;
|
|
1834
2221
|
if (!container) return;
|
|
1835
2222
|
const activeEl = container.querySelector('[data-active="true"]');
|
|
@@ -1896,12 +2283,12 @@ function MobileTabs({ nav }) {
|
|
|
1896
2283
|
})()
|
|
1897
2284
|
] });
|
|
1898
2285
|
}
|
|
1899
|
-
var BackStyleContext =
|
|
2286
|
+
var BackStyleContext = React7.createContext("link");
|
|
1900
2287
|
function BackStyleProvider({ style, children }) {
|
|
1901
2288
|
return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
|
|
1902
2289
|
}
|
|
1903
2290
|
function useBackStyle() {
|
|
1904
|
-
return
|
|
2291
|
+
return React7.useContext(BackStyleContext);
|
|
1905
2292
|
}
|
|
1906
2293
|
function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle, inline }) {
|
|
1907
2294
|
const t = useTranslation();
|
|
@@ -1997,13 +2384,11 @@ function ShellSubpageHeader({
|
|
|
1997
2384
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1998
2385
|
Icon && !onBack && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
1999
2386
|
/* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel, onBack })
|
|
2000
|
-
] })
|
|
2387
|
+
] }),
|
|
2388
|
+
{ back: !!onBack }
|
|
2001
2389
|
);
|
|
2002
|
-
if (!
|
|
2003
|
-
return /* @__PURE__ */
|
|
2004
|
-
subtitle ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle }) : /* @__PURE__ */ jsx("span", {}),
|
|
2005
|
-
actions && /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions })
|
|
2006
|
-
] });
|
|
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 }) });
|
|
2007
2392
|
}
|
|
2008
2393
|
function ModuleHeader({ nav }) {
|
|
2009
2394
|
function defaultChild(item) {
|
|
@@ -2026,12 +2411,13 @@ function ModuleHeader({ nav }) {
|
|
|
2026
2411
|
if (hasChildren) defaultChild(item)?.onClick?.();
|
|
2027
2412
|
else item.onClick?.();
|
|
2028
2413
|
};
|
|
2029
|
-
return /* @__PURE__ */ jsx(
|
|
2414
|
+
return /* @__PURE__ */ jsx(PageTabs, { tabs, value: activeItem?.id ?? nav[0]?.id ?? "", onChange });
|
|
2030
2415
|
}
|
|
2031
|
-
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 }) {
|
|
2032
2417
|
const ctxVariant = useModuleLayout();
|
|
2033
2418
|
const variant = navVariant ?? ctxVariant;
|
|
2034
2419
|
const transitionKey = viewKey ?? "view";
|
|
2420
|
+
const onSubpage = useHasPageHeaderBack();
|
|
2035
2421
|
if (variant === "sidebar") {
|
|
2036
2422
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2037
2423
|
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
@@ -2040,148 +2426,24 @@ function ModulePage({ title, subtitle, nav, children, className, headerAction, s
|
|
|
2040
2426
|
}
|
|
2041
2427
|
if (variant === "tabs") {
|
|
2042
2428
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2043
|
-
/* @__PURE__ */ jsx(ModuleHeader, { nav }),
|
|
2429
|
+
!onSubpage && /* @__PURE__ */ jsx(ModuleHeader, { nav }),
|
|
2044
2430
|
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
2045
2431
|
/* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, className: cn("min-w-0", className), children })
|
|
2046
2432
|
] });
|
|
2047
2433
|
}
|
|
2048
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("
|
|
2049
|
-
/* @__PURE__ */ jsx(
|
|
2050
|
-
/* @__PURE__ */
|
|
2051
|
-
|
|
2052
|
-
/* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(NavItem2, { item }, item.id)) })
|
|
2053
|
-
] }),
|
|
2054
|
-
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 })
|
|
2055
2438
|
] }) }),
|
|
2056
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
2057
|
-
/* @__PURE__ */ jsx(
|
|
2058
|
-
showHeader && /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-start justify-between gap-3", children: [
|
|
2059
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
2060
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: title }),
|
|
2061
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle })
|
|
2062
|
-
] }),
|
|
2063
|
-
headerAction
|
|
2064
|
-
] }),
|
|
2439
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2440
|
+
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
2065
2441
|
/* @__PURE__ */ jsx(MobileTabs, { nav }),
|
|
2066
2442
|
/* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
|
|
2067
2443
|
] })
|
|
2068
2444
|
] });
|
|
2069
2445
|
}
|
|
2070
|
-
var EMPTY = [];
|
|
2071
|
-
var PageTrailContext = React.createContext(EMPTY);
|
|
2072
|
-
function PageTrail({
|
|
2073
|
-
label,
|
|
2074
|
-
href,
|
|
2075
|
-
onNavigate,
|
|
2076
|
-
children
|
|
2077
|
-
}) {
|
|
2078
|
-
const parent = React.useContext(PageTrailContext);
|
|
2079
|
-
const trail = React.useMemo(
|
|
2080
|
-
() => [...parent, { label, href, onNavigate }],
|
|
2081
|
-
[parent, label, href, onNavigate]
|
|
2082
|
-
);
|
|
2083
|
-
return /* @__PURE__ */ jsx(PageTrailContext.Provider, { value: trail, children });
|
|
2084
|
-
}
|
|
2085
|
-
function usePageTrail() {
|
|
2086
|
-
return React.useContext(PageTrailContext);
|
|
2087
|
-
}
|
|
2088
|
-
function usePageTrailParent(href) {
|
|
2089
|
-
const trail = React.useContext(PageTrailContext);
|
|
2090
|
-
return React.useMemo(() => {
|
|
2091
|
-
const above = href ? trail.filter((c) => c.href !== href) : trail;
|
|
2092
|
-
return above[above.length - 1];
|
|
2093
|
-
}, [trail, href]);
|
|
2094
|
-
}
|
|
2095
|
-
function navigateToCrumb(crumb) {
|
|
2096
|
-
if (crumb.onNavigate) crumb.onNavigate();
|
|
2097
|
-
else window.location.hash = crumb.href;
|
|
2098
|
-
}
|
|
2099
|
-
var ContentSplitContext = React.createContext(null);
|
|
2100
|
-
function ContentSplit({
|
|
2101
|
-
children,
|
|
2102
|
-
aside,
|
|
2103
|
-
asideTitle,
|
|
2104
|
-
asideDescription,
|
|
2105
|
-
asideWidth = "20rem",
|
|
2106
|
-
asideOpen,
|
|
2107
|
-
onAsideOpenChange,
|
|
2108
|
-
collapsible = false,
|
|
2109
|
-
defaultCollapsed = false,
|
|
2110
|
-
className
|
|
2111
|
-
}) {
|
|
2112
|
-
const [internalOpen, setInternalOpen] = React.useState(false);
|
|
2113
|
-
const open = asideOpen ?? internalOpen;
|
|
2114
|
-
const setOpen = onAsideOpenChange ?? setInternalOpen;
|
|
2115
|
-
const [collapsed, setCollapsed] = React.useState(collapsible && defaultCollapsed);
|
|
2116
|
-
return /* @__PURE__ */ jsxs(ContentSplitContext.Provider, { value: { open, setOpen }, children: [
|
|
2117
|
-
/* @__PURE__ */ jsxs(
|
|
2118
|
-
"div",
|
|
2119
|
-
{
|
|
2120
|
-
className: cn("lg:grid lg:items-start lg:gap-6", className),
|
|
2121
|
-
style: { gridTemplateColumns: `minmax(0, 1fr) ${collapsed ? "auto" : asideWidth}` },
|
|
2122
|
-
children: [
|
|
2123
|
-
/* @__PURE__ */ jsx("div", { className: "min-w-0", children }),
|
|
2124
|
-
/* @__PURE__ */ jsx("aside", { className: "hidden lg:block lg:sticky lg:top-4", children: collapsed ? /* @__PURE__ */ jsx(
|
|
2125
|
-
Button,
|
|
2126
|
-
{
|
|
2127
|
-
variant: "outline",
|
|
2128
|
-
size: "sm",
|
|
2129
|
-
onClick: () => setCollapsed(false),
|
|
2130
|
-
className: "h-9 w-9 p-0",
|
|
2131
|
-
"aria-label": typeof asideTitle === "string" ? asideTitle : void 0,
|
|
2132
|
-
title: typeof asideTitle === "string" ? asideTitle : void 0,
|
|
2133
|
-
children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-4 w-4" })
|
|
2134
|
-
}
|
|
2135
|
-
) : /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-border bg-card", children: [
|
|
2136
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 border-b border-border px-4 py-3", children: [
|
|
2137
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2138
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: asideTitle }),
|
|
2139
|
-
asideDescription && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: asideDescription })
|
|
2140
|
-
] }),
|
|
2141
|
-
collapsible && /* @__PURE__ */ jsx(
|
|
2142
|
-
"button",
|
|
2143
|
-
{
|
|
2144
|
-
type: "button",
|
|
2145
|
-
onClick: () => setCollapsed(true),
|
|
2146
|
-
className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted/60 hover:text-foreground",
|
|
2147
|
-
"aria-label": "collapse",
|
|
2148
|
-
children: /* @__PURE__ */ jsx(PanelRightClose, { className: "h-4 w-4" })
|
|
2149
|
-
}
|
|
2150
|
-
)
|
|
2151
|
-
] }),
|
|
2152
|
-
/* @__PURE__ */ jsx("div", { className: "p-4", children: aside })
|
|
2153
|
-
] }) })
|
|
2154
|
-
]
|
|
2155
|
-
}
|
|
2156
|
-
),
|
|
2157
|
-
/* @__PURE__ */ jsx("div", { className: "lg:hidden", children: /* @__PURE__ */ jsx(Sheet, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(SheetContent, { width: "max-w-sm", children: [
|
|
2158
|
-
/* @__PURE__ */ jsxs(SheetHeader, { children: [
|
|
2159
|
-
/* @__PURE__ */ jsx(SheetTitle, { children: asideTitle }),
|
|
2160
|
-
asideDescription && /* @__PURE__ */ jsx(SheetDescription, { children: asideDescription })
|
|
2161
|
-
] }),
|
|
2162
|
-
/* @__PURE__ */ jsx(SheetBody, { children: aside })
|
|
2163
|
-
] }) }) })
|
|
2164
|
-
] });
|
|
2165
|
-
}
|
|
2166
|
-
function ContentSplitTrigger({ label, iconOnly, className }) {
|
|
2167
|
-
const ctx = React.useContext(ContentSplitContext);
|
|
2168
|
-
if (!ctx) return null;
|
|
2169
|
-
return /* @__PURE__ */ jsxs(
|
|
2170
|
-
Button,
|
|
2171
|
-
{
|
|
2172
|
-
variant: "outline",
|
|
2173
|
-
size: "sm",
|
|
2174
|
-
onClick: () => ctx.setOpen(true),
|
|
2175
|
-
className: cn("lg:hidden", className),
|
|
2176
|
-
"aria-label": typeof label === "string" ? label : void 0,
|
|
2177
|
-
children: [
|
|
2178
|
-
/* @__PURE__ */ jsx(PanelRight, { className: cn("h-4 w-4", !iconOnly && "mr-1.5") }),
|
|
2179
|
-
!iconOnly && label
|
|
2180
|
-
]
|
|
2181
|
-
}
|
|
2182
|
-
);
|
|
2183
|
-
}
|
|
2184
2446
|
|
|
2185
|
-
export { AppShell,
|
|
2186
|
-
//# sourceMappingURL=chunk-
|
|
2187
|
-
//# 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
|