@fayz-ai/ui 0.14.0 → 0.16.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/{ChartWidgetInner-GDE5PBTR.js → ChartWidgetInner-IOLIUYTB.js} +21 -8
- package/dist/ChartWidgetInner-IOLIUYTB.js.map +1 -0
- package/dist/analytics/AnalyticsCard.d.ts.map +1 -1
- package/dist/analytics/AnalyticsControls.d.ts.map +1 -1
- package/dist/analytics/context.d.ts +15 -1
- package/dist/analytics/context.d.ts.map +1 -1
- package/dist/analytics/index.d.ts +1 -1
- package/dist/analytics/index.d.ts.map +1 -1
- package/dist/chunk-3OVZ5JJQ.js +540 -0
- package/dist/chunk-3OVZ5JJQ.js.map +1 -0
- package/dist/{chunk-UZYXFEQS.js → chunk-4GK6IHOI.js} +16 -3
- package/dist/chunk-4GK6IHOI.js.map +1 -0
- package/dist/{chunk-46UZAKFJ.js → chunk-5VF45NLE.js} +7 -6
- package/dist/chunk-5VF45NLE.js.map +1 -0
- package/dist/{chunk-5KAGNW45.js → chunk-6BJTH3L6.js} +5 -5
- package/dist/chunk-6BJTH3L6.js.map +1 -0
- package/dist/chunk-AASANYXN.js +601 -0
- package/dist/chunk-AASANYXN.js.map +1 -0
- package/dist/{chunk-2EFRFYGF.js → chunk-E44WOPCR.js} +198 -39
- package/dist/chunk-E44WOPCR.js.map +1 -0
- package/dist/chunk-HKGTF2YA.js +117 -0
- package/dist/chunk-HKGTF2YA.js.map +1 -0
- package/dist/chunk-OKZ6WQB3.js +57 -0
- package/dist/chunk-OKZ6WQB3.js.map +1 -0
- package/dist/{chunk-MVH64JFG.js → chunk-PZU7XC2M.js} +780 -719
- package/dist/chunk-PZU7XC2M.js.map +1 -0
- package/dist/{chunk-77CUIMOI.js → chunk-SDPYDWWV.js} +459 -226
- package/dist/chunk-SDPYDWWV.js.map +1 -0
- package/dist/dashboard/ChartWidgetInner.d.ts.map +1 -1
- package/dist/dashboard/index.js +5 -4
- package/dist/hooks/useIsWide.d.ts +16 -0
- package/dist/hooks/useIsWide.d.ts.map +1 -0
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -17
- package/dist/index.js.map +1 -1
- package/dist/layout/AppShell.d.ts +31 -3
- package/dist/layout/AppShell.d.ts.map +1 -1
- package/dist/layout/ModulePage.d.ts +4 -0
- package/dist/layout/ModulePage.d.ts.map +1 -1
- package/dist/layout/SaveBar.d.ts.map +1 -1
- package/dist/layout/Sidebar.d.ts +27 -4
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Topbar.d.ts +14 -3
- package/dist/layout/Topbar.d.ts.map +1 -1
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +5 -4
- package/dist/primitives/SearchCombobox.d.ts +9 -1
- package/dist/primitives/SearchCombobox.d.ts.map +1 -1
- package/dist/primitives/checkbox.d.ts +4 -0
- package/dist/primitives/checkbox.d.ts.map +1 -1
- package/dist/primitives/color-palette.d.ts +24 -0
- package/dist/primitives/color-palette.d.ts.map +1 -0
- package/dist/primitives/header-tabs.d.ts +11 -1
- package/dist/primitives/header-tabs.d.ts.map +1 -1
- package/dist/primitives/image-drop.d.ts +28 -0
- package/dist/primitives/image-drop.d.ts.map +1 -0
- package/dist/primitives/index.d.ts +11 -3
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +7 -5
- package/dist/primitives/list-view.d.ts +11 -1
- package/dist/primitives/list-view.d.ts.map +1 -1
- package/dist/primitives/modal.d.ts.map +1 -1
- package/dist/primitives/option-select.d.ts +46 -0
- package/dist/primitives/option-select.d.ts.map +1 -0
- package/dist/primitives/page-actions.d.ts +55 -0
- package/dist/primitives/page-actions.d.ts.map +1 -0
- package/dist/primitives/page-header.d.ts +5 -2
- package/dist/primitives/page-header.d.ts.map +1 -1
- package/dist/primitives/page-toolbar.d.ts +20 -1
- package/dist/primitives/page-toolbar.d.ts.map +1 -1
- package/dist/primitives/refresh-button.d.ts.map +1 -1
- package/dist/primitives/row-actions.d.ts +11 -0
- package/dist/primitives/row-actions.d.ts.map +1 -0
- package/dist/primitives/sheet.d.ts.map +1 -1
- package/dist/primitives/step-progress.d.ts +20 -0
- package/dist/primitives/step-progress.d.ts.map +1 -0
- package/dist/primitives/use-row-selection.d.ts +50 -0
- package/dist/primitives/use-row-selection.d.ts.map +1 -0
- package/dist/primitives/whatsapp-icon.d.ts +7 -0
- package/dist/primitives/whatsapp-icon.d.ts.map +1 -0
- package/dist/stores/index.d.ts +1 -1
- package/dist/stores/index.d.ts.map +1 -1
- package/dist/stores/layout.store.d.ts +1 -0
- package/dist/stores/layout.store.d.ts.map +1 -1
- package/dist/stores/page-header.store.d.ts +39 -5
- package/dist/stores/page-header.store.d.ts.map +1 -1
- package/dist/styles.css +186 -3
- package/dist/wizard/Wizard.d.ts +38 -0
- package/dist/wizard/Wizard.d.ts.map +1 -0
- package/dist/wizard/WizardField.d.ts +22 -0
- package/dist/wizard/WizardField.d.ts.map +1 -0
- package/dist/wizard/index.d.ts +6 -0
- package/dist/wizard/index.d.ts.map +1 -0
- package/dist/wizard/index.js +5 -0
- package/dist/wizard/index.js.map +1 -0
- package/dist/wizard/logic.d.ts +39 -0
- package/dist/wizard/logic.d.ts.map +1 -0
- package/dist/wizard/types.d.ts +95 -0
- package/dist/wizard/types.d.ts.map +1 -0
- package/dist/wizard/useWizard.d.ts +47 -0
- package/dist/wizard/useWizard.d.ts.map +1 -0
- package/package.json +12 -6
- package/dist/ChartWidgetInner-GDE5PBTR.js.map +0 -1
- package/dist/chunk-2EFRFYGF.js.map +0 -1
- package/dist/chunk-46UZAKFJ.js.map +0 -1
- package/dist/chunk-5KAGNW45.js.map +0 -1
- package/dist/chunk-77CUIMOI.js.map +0 -1
- package/dist/chunk-LFZFBN3P.js +0 -142
- package/dist/chunk-LFZFBN3P.js.map +0 -1
- package/dist/chunk-MVH64JFG.js.map +0 -1
- package/dist/chunk-UZYXFEQS.js.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import { useHasPageHeaderHost, usePageHeader, PageTitlePortal, useLayoutStore, useHasPageHeaderBack, useRegisterPageActions, usePageHeaderClaimed, usePageTitleSlotHost, useHasPageHeaderOutlet, useHasPageActions, usePageHeaderHost } from './chunk-HKGTF2YA.js';
|
|
2
|
+
import { cn } from './chunk-OKZ6WQB3.js';
|
|
3
|
+
import { ICON_MAP } from './chunk-4GK6IHOI.js';
|
|
4
|
+
import * as React2 from 'react';
|
|
4
5
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
5
|
-
import { X, Loader2, ArrowLeft,
|
|
6
|
+
import { X, Loader2, ArrowLeft, Search, Bell, PanelLeft, PanelLeftClose, Menu, Crown, ChevronDown, ChevronRight, ChevronLeft, ExternalLink, CreditCard, Sparkles, Home, Plus, UserCog, Settings, LogOut } from 'lucide-react';
|
|
6
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
8
|
import { useTranslation, useNavReferrer, routeModule, navHistoryBack } from '@fayz-ai/core';
|
|
8
9
|
import * as Popover from '@radix-ui/react-popover';
|
|
@@ -28,26 +29,40 @@ var Sheet = DialogPrimitive.Root;
|
|
|
28
29
|
var SheetTrigger = DialogPrimitive.Trigger;
|
|
29
30
|
var SheetClose = DialogPrimitive.Close;
|
|
30
31
|
var SheetPortal = DialogPrimitive.Portal;
|
|
31
|
-
var SheetOverlay =
|
|
32
|
+
var SheetOverlay = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
32
33
|
DialogPrimitive.Overlay,
|
|
33
34
|
{
|
|
34
35
|
ref,
|
|
35
36
|
className: cn(
|
|
36
|
-
"saas-sheet-overlay fixed inset-0 z-
|
|
37
|
+
"saas-sheet-overlay fixed inset-0 z-[100] bg-black/40 backdrop-blur-[2px]",
|
|
37
38
|
className
|
|
38
39
|
),
|
|
39
40
|
...props
|
|
40
41
|
}
|
|
41
42
|
));
|
|
42
43
|
SheetOverlay.displayName = "SheetOverlay";
|
|
43
|
-
var SheetContent =
|
|
44
|
+
var SheetContent = React2.forwardRef(({ className, children, width, hideClose, overlay = "blur", onPointerDownOutside, onInteractOutside, ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [
|
|
44
45
|
/* @__PURE__ */ jsx(SheetOverlay, { className: overlay === "none" ? "bg-transparent backdrop-blur-0" : overlay === "dim" ? "backdrop-blur-0" : void 0 }),
|
|
45
46
|
/* @__PURE__ */ jsxs(
|
|
46
47
|
DialogPrimitive.Content,
|
|
47
48
|
{
|
|
48
49
|
ref,
|
|
50
|
+
onPointerDownOutside: (e) => {
|
|
51
|
+
const target = e.target;
|
|
52
|
+
if (target.closest("[data-modal-passthrough]") || target.closest("[data-radix-popper-content-wrapper]")) {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
}
|
|
55
|
+
onPointerDownOutside?.(e);
|
|
56
|
+
},
|
|
57
|
+
onInteractOutside: (e) => {
|
|
58
|
+
const target = e.target;
|
|
59
|
+
if (target.closest("[data-modal-passthrough]") || target.closest("[data-radix-popper-content-wrapper]")) {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
}
|
|
62
|
+
onInteractOutside?.(e);
|
|
63
|
+
},
|
|
49
64
|
className: cn(
|
|
50
|
-
"fayz-glass-surface saas-sheet-content fixed inset-y-0 right-0 z-
|
|
65
|
+
"fayz-glass-surface saas-sheet-content fixed inset-y-0 right-0 z-[100] flex flex-col border-l bg-card shadow-2xl outline-none",
|
|
51
66
|
"w-full",
|
|
52
67
|
width ?? "max-w-md",
|
|
53
68
|
className
|
|
@@ -84,7 +99,7 @@ function SheetFooter({ className, ...props }) {
|
|
|
84
99
|
);
|
|
85
100
|
}
|
|
86
101
|
SheetFooter.displayName = "SheetFooter";
|
|
87
|
-
var SheetTitle =
|
|
102
|
+
var SheetTitle = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
88
103
|
DialogPrimitive.Title,
|
|
89
104
|
{
|
|
90
105
|
ref,
|
|
@@ -93,7 +108,7 @@ var SheetTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE_
|
|
|
93
108
|
}
|
|
94
109
|
));
|
|
95
110
|
SheetTitle.displayName = "SheetTitle";
|
|
96
|
-
var SheetDescription =
|
|
111
|
+
var SheetDescription = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
97
112
|
DialogPrimitive.Description,
|
|
98
113
|
{
|
|
99
114
|
ref,
|
|
@@ -112,8 +127,8 @@ function SheetBody({ className, ...props }) {
|
|
|
112
127
|
);
|
|
113
128
|
}
|
|
114
129
|
SheetBody.displayName = "SheetBody";
|
|
115
|
-
var SaveBarApiContext =
|
|
116
|
-
var SaveBarStateContext =
|
|
130
|
+
var SaveBarApiContext = React2.createContext(null);
|
|
131
|
+
var SaveBarStateContext = React2.createContext({ visible: false, saving: false });
|
|
117
132
|
var _saveBarVisible = false;
|
|
118
133
|
var _saveBarSubs = /* @__PURE__ */ new Set();
|
|
119
134
|
function setSaveBarVisibleSignal(v) {
|
|
@@ -122,7 +137,7 @@ function setSaveBarVisibleSignal(v) {
|
|
|
122
137
|
_saveBarSubs.forEach((fn) => fn());
|
|
123
138
|
}
|
|
124
139
|
function useSaveBarVisible() {
|
|
125
|
-
return
|
|
140
|
+
return React2.useSyncExternalStore(
|
|
126
141
|
(cb) => {
|
|
127
142
|
_saveBarSubs.add(cb);
|
|
128
143
|
return () => {
|
|
@@ -134,15 +149,15 @@ function useSaveBarVisible() {
|
|
|
134
149
|
);
|
|
135
150
|
}
|
|
136
151
|
function SaveBarProvider({ children }) {
|
|
137
|
-
const [state, setState] =
|
|
138
|
-
const handlers =
|
|
139
|
-
const api =
|
|
140
|
-
const backStack =
|
|
141
|
-
const backApi =
|
|
142
|
-
|
|
152
|
+
const [state, setState] = React2.useState({ visible: false, saving: false });
|
|
153
|
+
const handlers = React2.useRef({});
|
|
154
|
+
const api = React2.useMemo(() => ({ setState, handlers }), []);
|
|
155
|
+
const backStack = React2.useRef([]);
|
|
156
|
+
const backApi = React2.useMemo(() => ({ stack: backStack }), []);
|
|
157
|
+
React2.useEffect(() => {
|
|
143
158
|
setSaveBarVisibleSignal(state.visible);
|
|
144
159
|
}, [state.visible]);
|
|
145
|
-
|
|
160
|
+
React2.useEffect(() => {
|
|
146
161
|
function onKey(e) {
|
|
147
162
|
if (state.visible && !state.saving) {
|
|
148
163
|
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
@@ -170,7 +185,7 @@ function SaveBarProvider({ children }) {
|
|
|
170
185
|
}, [state.visible, state.saving]);
|
|
171
186
|
return /* @__PURE__ */ jsx(SaveBarApiContext.Provider, { value: api, children: /* @__PURE__ */ jsx(BackNavApiContext.Provider, { value: backApi, children: /* @__PURE__ */ jsx(SaveBarStateContext.Provider, { value: state, children }) }) });
|
|
172
187
|
}
|
|
173
|
-
var BackNavApiContext =
|
|
188
|
+
var BackNavApiContext = React2.createContext(null);
|
|
174
189
|
function isEditableTarget(target) {
|
|
175
190
|
const el = target;
|
|
176
191
|
if (!el || !el.tagName) return false;
|
|
@@ -178,12 +193,12 @@ function isEditableTarget(target) {
|
|
|
178
193
|
return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
|
|
179
194
|
}
|
|
180
195
|
function useBackHandler(onBack) {
|
|
181
|
-
const api =
|
|
182
|
-
const ref =
|
|
183
|
-
|
|
196
|
+
const api = React2.useContext(BackNavApiContext);
|
|
197
|
+
const ref = React2.useRef(onBack);
|
|
198
|
+
React2.useEffect(() => {
|
|
184
199
|
ref.current = onBack;
|
|
185
200
|
});
|
|
186
|
-
|
|
201
|
+
React2.useEffect(() => {
|
|
187
202
|
if (!api) return;
|
|
188
203
|
const stack = api.stack.current;
|
|
189
204
|
stack.push(ref);
|
|
@@ -194,23 +209,23 @@ function useBackHandler(onBack) {
|
|
|
194
209
|
}, [api]);
|
|
195
210
|
}
|
|
196
211
|
function useSaveBar({ dirty, saving = false, onSave, onDiscard, message, saveLabel, discardLabel }) {
|
|
197
|
-
const api =
|
|
198
|
-
|
|
212
|
+
const api = React2.useContext(SaveBarApiContext);
|
|
213
|
+
React2.useEffect(() => {
|
|
199
214
|
if (api) api.handlers.current = { onSave, onDiscard };
|
|
200
215
|
});
|
|
201
|
-
|
|
216
|
+
React2.useEffect(() => {
|
|
202
217
|
if (!api) return;
|
|
203
218
|
api.setState({ visible: dirty, saving, message, saveLabel, discardLabel });
|
|
204
219
|
return () => api.setState((s) => ({ ...s, visible: false }));
|
|
205
220
|
}, [api, dirty, saving, message, saveLabel, discardLabel]);
|
|
206
221
|
}
|
|
207
222
|
function SaveBar({ className }) {
|
|
208
|
-
const state =
|
|
209
|
-
const api =
|
|
223
|
+
const state = React2.useContext(SaveBarStateContext);
|
|
224
|
+
const api = React2.useContext(SaveBarApiContext);
|
|
210
225
|
const t = useTranslation();
|
|
211
|
-
const [rendered, setRendered] =
|
|
212
|
-
const [closing, setClosing] =
|
|
213
|
-
|
|
226
|
+
const [rendered, setRendered] = React2.useState(state.visible);
|
|
227
|
+
const [closing, setClosing] = React2.useState(false);
|
|
228
|
+
React2.useEffect(() => {
|
|
214
229
|
if (state.visible) {
|
|
215
230
|
setRendered(true);
|
|
216
231
|
setClosing(false);
|
|
@@ -226,40 +241,52 @@ function SaveBar({ className }) {
|
|
|
226
241
|
}, [state.visible, rendered]);
|
|
227
242
|
if (!api || !rendered) return null;
|
|
228
243
|
const { saving, message, saveLabel, discardLabel } = state;
|
|
229
|
-
return /* @__PURE__ */ jsx(
|
|
230
|
-
"
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
}
|
|
244
|
+
return /* @__PURE__ */ jsx(
|
|
245
|
+
"div",
|
|
246
|
+
{
|
|
247
|
+
className: cn(
|
|
248
|
+
// Above the tab bar (z-50), below a full-screen sheet (z-60), and lifted
|
|
249
|
+
// clear of the bar's height so its actions are not sitting under it.
|
|
250
|
+
"pointer-events-none fixed inset-x-0 z-[55] flex justify-center px-4",
|
|
251
|
+
"bottom-[calc(var(--fz-bottom-bar,0px)+1.25rem)]",
|
|
252
|
+
className
|
|
247
253
|
),
|
|
248
|
-
/* @__PURE__ */ jsxs(
|
|
249
|
-
"
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
254
|
+
children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
255
|
+
"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",
|
|
256
|
+
closing ? "animate-out fade-out slide-out-to-bottom-2" : "animate-in fade-in slide-in-from-bottom-2"
|
|
257
|
+
), children: [
|
|
258
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
|
|
259
|
+
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400" }),
|
|
260
|
+
message ?? t("saveBar.unsaved")
|
|
261
|
+
] }),
|
|
262
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
263
|
+
/* @__PURE__ */ jsx(
|
|
264
|
+
"button",
|
|
265
|
+
{
|
|
266
|
+
type: "button",
|
|
267
|
+
disabled: saving,
|
|
268
|
+
onClick: () => api.handlers.current.onDiscard?.(),
|
|
269
|
+
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",
|
|
270
|
+
children: discardLabel ?? t("common.discard")
|
|
271
|
+
}
|
|
272
|
+
),
|
|
273
|
+
/* @__PURE__ */ jsxs(
|
|
274
|
+
"button",
|
|
275
|
+
{
|
|
276
|
+
type: "button",
|
|
277
|
+
disabled: saving,
|
|
278
|
+
onClick: () => api.handlers.current.onSave?.(),
|
|
279
|
+
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",
|
|
280
|
+
children: [
|
|
281
|
+
saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
|
|
282
|
+
saveLabel ?? t("common.save")
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
)
|
|
286
|
+
] })
|
|
287
|
+
] })
|
|
288
|
+
}
|
|
289
|
+
);
|
|
263
290
|
}
|
|
264
291
|
function useCrossModuleBack(onBack, parentLabel) {
|
|
265
292
|
const currentPath = typeof window !== "undefined" ? (window.location.hash ? window.location.hash.slice(1) : window.location.pathname) || "/" : "/";
|
|
@@ -274,10 +301,14 @@ function useCrossModuleBack(onBack, parentLabel) {
|
|
|
274
301
|
function PageHeaderCrumb({
|
|
275
302
|
title,
|
|
276
303
|
parentLabel,
|
|
277
|
-
onBack
|
|
304
|
+
onBack,
|
|
305
|
+
headingLevel = "h2"
|
|
278
306
|
}) {
|
|
307
|
+
const Heading = headingLevel;
|
|
308
|
+
const asHeading = typeof title === "string" && title !== "";
|
|
279
309
|
if (!onBack) {
|
|
280
|
-
|
|
310
|
+
if (!title) return null;
|
|
311
|
+
return asHeading ? /* @__PURE__ */ jsx(Heading, { className: "min-w-0 truncate text-lg font-semibold", children: title }) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-lg font-semibold", children: title });
|
|
281
312
|
}
|
|
282
313
|
return /* @__PURE__ */ jsxs("nav", { className: "flex min-w-0 items-center gap-1.5 text-sm text-muted-foreground", children: [
|
|
283
314
|
/* @__PURE__ */ jsxs(
|
|
@@ -298,6 +329,11 @@ function PageHeaderCrumb({
|
|
|
298
329
|
// Title still waiting on data: a pulsing bar, otherwise the line
|
|
299
330
|
// appears from nowhere and shoves the rest when the record lands.
|
|
300
331
|
/* @__PURE__ */ jsx("span", { className: "inline-block h-4 w-24 animate-pulse rounded bg-muted align-middle" })
|
|
332
|
+
) : asHeading ? (
|
|
333
|
+
// A heading, not a span, when it IS the screen's name: a subpage
|
|
334
|
+
// that only ever renders a breadcrumb was leaving the page with no
|
|
335
|
+
// heading for a screen reader to land on.
|
|
336
|
+
/* @__PURE__ */ jsx(Heading, { className: "min-w-0 truncate font-medium text-foreground", children: title })
|
|
301
337
|
) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-foreground", children: title })
|
|
302
338
|
] })
|
|
303
339
|
] });
|
|
@@ -306,7 +342,7 @@ function PageHeader({ title, onBack, parentLabel, backLabel }) {
|
|
|
306
342
|
const hasHost = useHasPageHeaderHost();
|
|
307
343
|
const { onBack: effectiveOnBack, parentLabel: effectiveParent } = useCrossModuleBack(onBack, parentLabel);
|
|
308
344
|
useBackHandler(effectiveOnBack);
|
|
309
|
-
const node =
|
|
345
|
+
const node = React2.useMemo(() => /* @__PURE__ */ jsx(
|
|
310
346
|
PageHeaderCrumb,
|
|
311
347
|
{
|
|
312
348
|
title,
|
|
@@ -314,385 +350,21 @@ function PageHeader({ title, onBack, parentLabel, backLabel }) {
|
|
|
314
350
|
onBack: effectiveOnBack
|
|
315
351
|
}
|
|
316
352
|
), [effectiveOnBack, effectiveParent, backLabel, title]);
|
|
317
|
-
usePageHeader(hasHost
|
|
318
|
-
if (hasHost) return
|
|
353
|
+
usePageHeader(hasHost, { back: !!effectiveOnBack });
|
|
354
|
+
if (hasHost) return /* @__PURE__ */ jsx(PageTitlePortal, { children: node });
|
|
319
355
|
return /* @__PURE__ */ jsx("div", { className: "mb-4", children: node });
|
|
320
356
|
}
|
|
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
|
-
}
|
|
685
357
|
var GUTTER_PX = 8;
|
|
686
358
|
var CLOSE_DELAY_MS = 140;
|
|
687
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ?
|
|
359
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React2.useLayoutEffect : React2.useEffect;
|
|
688
360
|
function iconOf(name) {
|
|
689
361
|
if (!name) return null;
|
|
690
362
|
return ICON_MAP[name] ?? null;
|
|
691
363
|
}
|
|
692
|
-
var RailFlyoutContext =
|
|
364
|
+
var RailFlyoutContext = React2.createContext(null);
|
|
693
365
|
var RailFlyoutProvider = RailFlyoutContext.Provider;
|
|
694
366
|
function useRailRow(content) {
|
|
695
|
-
const flyout =
|
|
367
|
+
const flyout = React2.useContext(RailFlyoutContext);
|
|
696
368
|
if (!flyout || !content) return {};
|
|
697
369
|
return flyout.rowProps(content);
|
|
698
370
|
}
|
|
@@ -702,19 +374,19 @@ function useRailFlyout({
|
|
|
702
374
|
activeRoute,
|
|
703
375
|
onNavigate
|
|
704
376
|
}) {
|
|
705
|
-
const [state, setState] =
|
|
706
|
-
const [pos, setPos] =
|
|
707
|
-
const [animate, setAnimate] =
|
|
708
|
-
const panelRef =
|
|
709
|
-
const closeTimer =
|
|
710
|
-
const lastContent =
|
|
711
|
-
const cancelClose =
|
|
377
|
+
const [state, setState] = React2.useState(null);
|
|
378
|
+
const [pos, setPos] = React2.useState({ top: 0, arrow: 20 });
|
|
379
|
+
const [animate, setAnimate] = React2.useState(false);
|
|
380
|
+
const panelRef = React2.useRef(null);
|
|
381
|
+
const closeTimer = React2.useRef(null);
|
|
382
|
+
const lastContent = React2.useRef(null);
|
|
383
|
+
const cancelClose = React2.useCallback(() => {
|
|
712
384
|
if (closeTimer.current) {
|
|
713
385
|
clearTimeout(closeTimer.current);
|
|
714
386
|
closeTimer.current = null;
|
|
715
387
|
}
|
|
716
388
|
}, []);
|
|
717
|
-
const show =
|
|
389
|
+
const show = React2.useCallback(
|
|
718
390
|
(anchor, content2) => {
|
|
719
391
|
if (!enabled) return;
|
|
720
392
|
cancelClose();
|
|
@@ -722,19 +394,19 @@ function useRailFlyout({
|
|
|
722
394
|
},
|
|
723
395
|
[enabled, cancelClose]
|
|
724
396
|
);
|
|
725
|
-
const hide =
|
|
397
|
+
const hide = React2.useCallback(() => {
|
|
726
398
|
cancelClose();
|
|
727
399
|
closeTimer.current = setTimeout(() => setState(null), CLOSE_DELAY_MS);
|
|
728
400
|
}, [cancelClose]);
|
|
729
|
-
const dismiss =
|
|
401
|
+
const dismiss = React2.useCallback(() => {
|
|
730
402
|
cancelClose();
|
|
731
403
|
setState(null);
|
|
732
404
|
}, [cancelClose]);
|
|
733
|
-
|
|
734
|
-
|
|
405
|
+
React2.useEffect(() => () => cancelClose(), [cancelClose]);
|
|
406
|
+
React2.useEffect(() => {
|
|
735
407
|
if (!enabled) dismiss();
|
|
736
408
|
}, [enabled, dismiss]);
|
|
737
|
-
const reposition =
|
|
409
|
+
const reposition = React2.useCallback(() => {
|
|
738
410
|
const rail = railRef.current;
|
|
739
411
|
const anchor = state?.anchor;
|
|
740
412
|
if (!rail || !anchor) return;
|
|
@@ -749,7 +421,7 @@ function useRailFlyout({
|
|
|
749
421
|
setPos({ top, arrow });
|
|
750
422
|
}, [railRef, state]);
|
|
751
423
|
useIsomorphicLayoutEffect(reposition, [reposition, state?.content.key]);
|
|
752
|
-
|
|
424
|
+
React2.useEffect(() => {
|
|
753
425
|
if (!state) return;
|
|
754
426
|
const rail = railRef.current;
|
|
755
427
|
const onScroll = () => reposition();
|
|
@@ -761,7 +433,7 @@ function useRailFlyout({
|
|
|
761
433
|
};
|
|
762
434
|
}, [state, railRef, reposition]);
|
|
763
435
|
const visible = !!state;
|
|
764
|
-
|
|
436
|
+
React2.useEffect(() => {
|
|
765
437
|
if (!visible) {
|
|
766
438
|
setAnimate(false);
|
|
767
439
|
return;
|
|
@@ -769,7 +441,7 @@ function useRailFlyout({
|
|
|
769
441
|
const id = requestAnimationFrame(() => setAnimate(true));
|
|
770
442
|
return () => cancelAnimationFrame(id);
|
|
771
443
|
}, [visible]);
|
|
772
|
-
|
|
444
|
+
React2.useEffect(() => {
|
|
773
445
|
if (!visible) return;
|
|
774
446
|
const onKey = (event) => {
|
|
775
447
|
if (event.key === "Escape") dismiss();
|
|
@@ -777,7 +449,7 @@ function useRailFlyout({
|
|
|
777
449
|
window.addEventListener("keydown", onKey);
|
|
778
450
|
return () => window.removeEventListener("keydown", onKey);
|
|
779
451
|
}, [visible, dismiss]);
|
|
780
|
-
const rowProps =
|
|
452
|
+
const rowProps = React2.useCallback(
|
|
781
453
|
(content2) => ({
|
|
782
454
|
onMouseEnter: (event) => show(event.currentTarget, content2),
|
|
783
455
|
onFocus: (event) => show(event.currentTarget, content2),
|
|
@@ -894,8 +566,8 @@ function FlyoutBody({
|
|
|
894
566
|
] });
|
|
895
567
|
}
|
|
896
568
|
var RAIL_PUSH_MS = 260;
|
|
897
|
-
var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ?
|
|
898
|
-
function
|
|
569
|
+
var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React2.useLayoutEffect : React2.useEffect;
|
|
570
|
+
function getIcon(name) {
|
|
899
571
|
return ICON_MAP[name] ?? Home;
|
|
900
572
|
}
|
|
901
573
|
function shortcutKey() {
|
|
@@ -928,7 +600,7 @@ function NavItem({
|
|
|
928
600
|
onNavigate,
|
|
929
601
|
flyout
|
|
930
602
|
}) {
|
|
931
|
-
const Icon =
|
|
603
|
+
const Icon = getIcon(item.icon);
|
|
932
604
|
const isExternal = !!item.href;
|
|
933
605
|
const hoverProps = collapsed && flyout ? flyout.rowProps(flyoutContentOf(item)) : void 0;
|
|
934
606
|
const className = cn(
|
|
@@ -938,7 +610,7 @@ function NavItem({
|
|
|
938
610
|
// as cramped rather than as dense.
|
|
939
611
|
"flex w-full items-center gap-3 rounded-button px-2.5 py-2 text-[13px] font-medium transition-colors",
|
|
940
612
|
"hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground",
|
|
941
|
-
isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground
|
|
613
|
+
isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground" : "text-sidebar-foreground/80",
|
|
942
614
|
collapsed && "justify-center px-2"
|
|
943
615
|
);
|
|
944
616
|
const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -994,8 +666,8 @@ function NavGroup({
|
|
|
994
666
|
flyout
|
|
995
667
|
}) {
|
|
996
668
|
const isChildActive = item.children?.some((c) => c.route === activeRoute);
|
|
997
|
-
const [open, setOpen] =
|
|
998
|
-
const Icon =
|
|
669
|
+
const [open, setOpen] = React2.useState(() => isChildActive ?? false);
|
|
670
|
+
const Icon = getIcon(item.icon);
|
|
999
671
|
if (collapsed) {
|
|
1000
672
|
const hoverProps = flyout?.rowProps(flyoutContentOf(item));
|
|
1001
673
|
return /* @__PURE__ */ jsx(
|
|
@@ -1050,7 +722,7 @@ function NavGroup({
|
|
|
1050
722
|
// along with the labels and land the arrowhead on top of the text.
|
|
1051
723
|
childConnector ? "relative" : "ml-4 space-y-0.5 border-l border-sidebar-border/50 pl-3"
|
|
1052
724
|
), children: item.children?.map((child, childIndex) => {
|
|
1053
|
-
const ChildIcon =
|
|
725
|
+
const ChildIcon = getIcon(child.icon);
|
|
1054
726
|
const isActive = child.route === activeRoute;
|
|
1055
727
|
const childExternal = !!child.href;
|
|
1056
728
|
const childClass = cn(
|
|
@@ -1068,7 +740,7 @@ function NavGroup({
|
|
|
1068
740
|
child.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 shrink-0 text-amber-500", "aria-hidden": true })
|
|
1069
741
|
] });
|
|
1070
742
|
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 });
|
|
1071
|
-
if (!childConnector) return /* @__PURE__ */ jsx(
|
|
743
|
+
if (!childConnector) return /* @__PURE__ */ jsx(React2.Fragment, { children: row }, child.id);
|
|
1072
744
|
const activeIndex = item.children.findIndex((c) => c.route === activeRoute);
|
|
1073
745
|
const onPath = activeIndex >= 0 && childIndex < activeIndex;
|
|
1074
746
|
return (
|
|
@@ -1130,9 +802,9 @@ function NavGroup({
|
|
|
1130
802
|
] });
|
|
1131
803
|
}
|
|
1132
804
|
function withCollapsed(slot, collapsed) {
|
|
1133
|
-
if (!
|
|
805
|
+
if (!React2.isValidElement(slot)) return slot;
|
|
1134
806
|
if (typeof slot.type === "string") return slot;
|
|
1135
|
-
return
|
|
807
|
+
return React2.cloneElement(slot, { collapsed });
|
|
1136
808
|
}
|
|
1137
809
|
function Sidebar({
|
|
1138
810
|
navigation,
|
|
@@ -1144,6 +816,7 @@ function Sidebar({
|
|
|
1144
816
|
user: _user,
|
|
1145
817
|
orgSwitcher,
|
|
1146
818
|
topContent,
|
|
819
|
+
identity,
|
|
1147
820
|
navTop,
|
|
1148
821
|
footerContent,
|
|
1149
822
|
borderless,
|
|
@@ -1154,12 +827,13 @@ function Sidebar({
|
|
|
1154
827
|
sectionLabels,
|
|
1155
828
|
navKey,
|
|
1156
829
|
navDirection = "forward",
|
|
1157
|
-
navChildConnector
|
|
830
|
+
navChildConnector,
|
|
831
|
+
drawer
|
|
1158
832
|
}) {
|
|
1159
833
|
const currentPath = currentPathProp;
|
|
1160
834
|
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
1161
835
|
const activeRoute = resolveActiveRoute(navigation, currentPath);
|
|
1162
|
-
const railRef =
|
|
836
|
+
const railRef = React2.useRef(null);
|
|
1163
837
|
const flyout = useRailFlyout({
|
|
1164
838
|
enabled: collapsed,
|
|
1165
839
|
railRef,
|
|
@@ -1205,14 +879,13 @@ function Sidebar({
|
|
|
1205
879
|
item.id
|
|
1206
880
|
);
|
|
1207
881
|
const navBody = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1208
|
-
navTop && /* @__PURE__ */ jsx("div", { className: "mb-2", children: withCollapsed(navTop, collapsed) }),
|
|
1209
882
|
mainItems.length > 0 && renderSection(mainItems, sectionLabels?.main),
|
|
1210
883
|
secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6", children: renderSection(secondaryItems, sectionLabels?.secondary) })
|
|
1211
884
|
] });
|
|
1212
|
-
const [outgoing, setOutgoing] =
|
|
1213
|
-
const lastBodyRef =
|
|
1214
|
-
const lastKeyRef =
|
|
1215
|
-
const timerRef =
|
|
885
|
+
const [outgoing, setOutgoing] = React2.useState(null);
|
|
886
|
+
const lastBodyRef = React2.useRef(null);
|
|
887
|
+
const lastKeyRef = React2.useRef(navKey);
|
|
888
|
+
const timerRef = React2.useRef(null);
|
|
1216
889
|
useIsomorphicLayoutEffect2(() => {
|
|
1217
890
|
if (lastKeyRef.current !== navKey) {
|
|
1218
891
|
const previousBody = lastBodyRef.current;
|
|
@@ -1225,7 +898,7 @@ function Sidebar({
|
|
|
1225
898
|
}
|
|
1226
899
|
lastBodyRef.current = navBody;
|
|
1227
900
|
});
|
|
1228
|
-
|
|
901
|
+
React2.useEffect(() => () => {
|
|
1229
902
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
1230
903
|
}, []);
|
|
1231
904
|
return (
|
|
@@ -1253,162 +926,446 @@ function Sidebar({
|
|
|
1253
926
|
collapsed ? "w-16" : "w-56"
|
|
1254
927
|
),
|
|
1255
928
|
children: [
|
|
1256
|
-
/* @__PURE__ */ jsxs(
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
929
|
+
/* @__PURE__ */ jsxs("div", { className: cn("shrink-0", !borderless && "border-b border-sidebar-border"), children: [
|
|
930
|
+
/* @__PURE__ */ jsxs(
|
|
931
|
+
"div",
|
|
932
|
+
{
|
|
933
|
+
className: cn(
|
|
934
|
+
"flex items-center gap-1 px-2",
|
|
935
|
+
// h-12 com a conta embaixo (48 + 44 + 8 = 100px, na régua de 4/8),
|
|
936
|
+
// h-14 quando a marca é a única coisa aqui e a linha tem de bater
|
|
937
|
+
// com o cabeçalho da página.
|
|
938
|
+
identity ? "h-12" : "h-14",
|
|
939
|
+
collapsed && "justify-center"
|
|
940
|
+
),
|
|
941
|
+
children: [
|
|
942
|
+
topContent ? /* @__PURE__ */ jsx("div", { className: cn("min-w-0", collapsed ? "flex justify-center" : "flex-1"), children: withCollapsed(topContent, collapsed) }) : !collapsed && /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 px-1.5", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
|
|
943
|
+
drawer && /* @__PURE__ */ jsx(
|
|
1268
944
|
"button",
|
|
1269
945
|
{
|
|
1270
946
|
onClick: onToggle,
|
|
1271
|
-
className: "
|
|
1272
|
-
"aria-label": "
|
|
1273
|
-
children: /* @__PURE__ */ jsx(
|
|
947
|
+
className: "fz-tap shrink-0 rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
948
|
+
"aria-label": "Close menu",
|
|
949
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
1274
950
|
}
|
|
1275
951
|
)
|
|
1276
|
-
]
|
|
1277
|
-
|
|
952
|
+
]
|
|
953
|
+
}
|
|
954
|
+
),
|
|
955
|
+
identity && /* @__PURE__ */ jsx("div", { className: cn("px-2 pb-2", collapsed && "flex justify-center"), children: withCollapsed(identity, collapsed) })
|
|
956
|
+
] }),
|
|
957
|
+
navTop ? /* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: withCollapsed(navTop, collapsed) }) : (
|
|
958
|
+
/* Search — above the first destination. It used to be a 16px magnifier
|
|
959
|
+
tucked beside the avatar at the very bottom, which is where you put
|
|
960
|
+
something nobody is meant to find: the fastest way through an admin
|
|
961
|
+
with thirty screens was the one control shaped like an afterthought.
|
|
962
|
+
Here it reads as what it is — a field — and says which key opens it.
|
|
963
|
+
Clicking it opens the same palette ⌘K does; it is a button, not an
|
|
964
|
+
input, because typing happens there. */
|
|
965
|
+
/* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: /* @__PURE__ */ jsxs(
|
|
966
|
+
"button",
|
|
967
|
+
{
|
|
968
|
+
onClick: () => setCommandPaletteOpen(true),
|
|
969
|
+
"aria-keyshortcuts": "Meta+K Control+K",
|
|
970
|
+
className: cn(
|
|
971
|
+
// Tonal surface, no outline. On a dark rail a 1px border is a hard
|
|
972
|
+
// edge competing with every other hard edge; a lighter fill of the
|
|
973
|
+
// same hue reads as "a field" without drawing a box around it — and
|
|
974
|
+
// leaves the outline treatment free to mean focus, which it must.
|
|
975
|
+
"fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
|
|
976
|
+
"hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
|
|
977
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
|
|
978
|
+
collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
|
|
979
|
+
),
|
|
980
|
+
"aria-label": searchLabel,
|
|
981
|
+
title: collapsed ? searchLabel : void 0,
|
|
982
|
+
children: [
|
|
983
|
+
/* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
|
|
984
|
+
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
985
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
|
|
986
|
+
/* @__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: [
|
|
987
|
+
shortcutKey(),
|
|
988
|
+
"K"
|
|
989
|
+
] })
|
|
990
|
+
] })
|
|
991
|
+
]
|
|
992
|
+
}
|
|
993
|
+
) })
|
|
994
|
+
),
|
|
995
|
+
/* @__PURE__ */ jsxs(
|
|
996
|
+
"nav",
|
|
997
|
+
{
|
|
998
|
+
className: cn(
|
|
999
|
+
"relative flex-1 p-2",
|
|
1000
|
+
// scrollbar-none: the rail still scrolls, but without the native
|
|
1001
|
+
// scrollbar gutter eating into the nav items.
|
|
1002
|
+
outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
|
|
1003
|
+
),
|
|
1004
|
+
children: [
|
|
1005
|
+
outgoing && /* @__PURE__ */ jsx(
|
|
1006
|
+
"div",
|
|
1007
|
+
{
|
|
1008
|
+
"aria-hidden": true,
|
|
1009
|
+
className: cn(
|
|
1010
|
+
"pointer-events-none absolute inset-x-2 top-2 space-y-1",
|
|
1011
|
+
outgoing.direction === "back" ? "saas-rail-out-back" : "saas-rail-out-forward"
|
|
1012
|
+
),
|
|
1013
|
+
children: outgoing.body
|
|
1014
|
+
}
|
|
1015
|
+
),
|
|
1016
|
+
/* @__PURE__ */ jsx(
|
|
1017
|
+
"div",
|
|
1018
|
+
{
|
|
1019
|
+
className: cn(
|
|
1020
|
+
"space-y-1",
|
|
1021
|
+
outgoing && (outgoing.direction === "back" ? "saas-rail-in-back" : "saas-rail-in-forward")
|
|
1022
|
+
),
|
|
1023
|
+
children: navBody
|
|
1024
|
+
},
|
|
1025
|
+
navKey
|
|
1026
|
+
)
|
|
1027
|
+
]
|
|
1028
|
+
}
|
|
1029
|
+
),
|
|
1030
|
+
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(
|
|
1031
|
+
NavItem,
|
|
1032
|
+
{
|
|
1033
|
+
item,
|
|
1034
|
+
collapsed,
|
|
1035
|
+
isActive: item.route === activeRoute,
|
|
1036
|
+
onNavigate: handleNavigate,
|
|
1037
|
+
flyout
|
|
1038
|
+
},
|
|
1039
|
+
item.id
|
|
1040
|
+
)) }),
|
|
1041
|
+
footerContent && // `empty:hidden` — slot children may all render null (e.g. the
|
|
1042
|
+
// SeuSiteWidget self-hides when the tenant has no project); without
|
|
1043
|
+
// it the padded/bordered wrapper shows as a blank block.
|
|
1044
|
+
/* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
|
|
1045
|
+
withCollapsed(orgSwitcher, collapsed),
|
|
1046
|
+
/* @__PURE__ */ jsx(
|
|
1047
|
+
"div",
|
|
1048
|
+
{
|
|
1049
|
+
className: cn("py-2 pr-2", !borderless && "border-t border-sidebar-border"),
|
|
1050
|
+
style: drawer ? { paddingBottom: "calc(0.5rem + env(safe-area-inset-bottom))" } : void 0,
|
|
1051
|
+
children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
1052
|
+
"flex items-center rounded-button",
|
|
1053
|
+
collapsed ? "flex-col gap-1" : "gap-1"
|
|
1054
|
+
), children: [
|
|
1055
|
+
userMenuSlot,
|
|
1056
|
+
!collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
1057
|
+
notificationSlot ?? /* @__PURE__ */ jsxs(
|
|
1058
|
+
"button",
|
|
1059
|
+
{
|
|
1060
|
+
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",
|
|
1061
|
+
"aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
|
|
1062
|
+
children: [
|
|
1063
|
+
/* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
|
|
1064
|
+
unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
|
|
1065
|
+
]
|
|
1066
|
+
}
|
|
1067
|
+
),
|
|
1068
|
+
!drawer && /* @__PURE__ */ jsx(
|
|
1278
1069
|
"button",
|
|
1279
1070
|
{
|
|
1280
1071
|
onClick: onToggle,
|
|
1281
|
-
className: "rounded-button p-
|
|
1282
|
-
"aria-label": "Expand sidebar",
|
|
1283
|
-
|
|
1072
|
+
className: "fz-tap 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",
|
|
1073
|
+
"aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
1074
|
+
title: collapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
1075
|
+
children: collapsed ? /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
|
|
1284
1076
|
}
|
|
1285
1077
|
)
|
|
1286
|
-
]
|
|
1078
|
+
] })
|
|
1287
1079
|
}
|
|
1288
1080
|
),
|
|
1289
|
-
|
|
1290
|
-
|
|
1081
|
+
flyout.panel
|
|
1082
|
+
]
|
|
1083
|
+
}
|
|
1084
|
+
) })
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
function TopbarUserMenu({
|
|
1088
|
+
user,
|
|
1089
|
+
userPlan,
|
|
1090
|
+
billingLabel = "Subscription",
|
|
1091
|
+
onSignOut,
|
|
1092
|
+
onProfile,
|
|
1093
|
+
onSettings,
|
|
1094
|
+
onBilling,
|
|
1095
|
+
userMenuExtras
|
|
1096
|
+
}) {
|
|
1097
|
+
const [open, setOpen] = React2.useState(false);
|
|
1098
|
+
if (!user) return null;
|
|
1099
|
+
const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
|
|
1100
|
+
const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
|
|
1101
|
+
"button",
|
|
1102
|
+
{
|
|
1103
|
+
onClick: () => {
|
|
1104
|
+
setOpen(false);
|
|
1105
|
+
onClick();
|
|
1106
|
+
},
|
|
1107
|
+
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",
|
|
1108
|
+
children: [
|
|
1109
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-muted-foreground" }),
|
|
1110
|
+
/* @__PURE__ */ jsx("span", { children: label })
|
|
1111
|
+
]
|
|
1112
|
+
},
|
|
1113
|
+
label
|
|
1114
|
+
) : null;
|
|
1115
|
+
return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
|
|
1116
|
+
/* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1117
|
+
"button",
|
|
1118
|
+
{
|
|
1119
|
+
"aria-label": "User menu",
|
|
1120
|
+
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",
|
|
1121
|
+
children: user.avatarUrl ? (
|
|
1122
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
1123
|
+
/* @__PURE__ */ jsx("img", { src: user.avatarUrl, alt: "", className: "h-full w-full object-cover" })
|
|
1124
|
+
) : initial
|
|
1125
|
+
}
|
|
1126
|
+
) }),
|
|
1127
|
+
/* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(
|
|
1128
|
+
Popover.Content,
|
|
1129
|
+
{
|
|
1130
|
+
align: "end",
|
|
1131
|
+
sideOffset: 8,
|
|
1132
|
+
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",
|
|
1133
|
+
children: [
|
|
1134
|
+
/* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
|
|
1135
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: user.fullName || user.email }),
|
|
1136
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
|
|
1137
|
+
] }),
|
|
1138
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-0.5", children: [
|
|
1139
|
+
item("Perfil", onProfile, UserCog),
|
|
1140
|
+
item("Configura\xE7\xF5es", onSettings, Settings),
|
|
1141
|
+
onBilling && /* @__PURE__ */ jsxs(
|
|
1142
|
+
"button",
|
|
1143
|
+
{
|
|
1144
|
+
onClick: () => {
|
|
1145
|
+
setOpen(false);
|
|
1146
|
+
onBilling();
|
|
1147
|
+
},
|
|
1148
|
+
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",
|
|
1149
|
+
children: [
|
|
1150
|
+
/* @__PURE__ */ jsx(CreditCard, { className: "h-4 w-4 text-muted-foreground" }),
|
|
1151
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: billingLabel }),
|
|
1152
|
+
userPlan && /* @__PURE__ */ jsxs(
|
|
1153
|
+
"span",
|
|
1154
|
+
{
|
|
1155
|
+
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",
|
|
1156
|
+
children: [
|
|
1157
|
+
userPlan.paid ? /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Sparkles, { className: "h-3 w-3" }),
|
|
1158
|
+
userPlan.label
|
|
1159
|
+
]
|
|
1160
|
+
}
|
|
1161
|
+
)
|
|
1162
|
+
]
|
|
1163
|
+
},
|
|
1164
|
+
"billing"
|
|
1165
|
+
),
|
|
1166
|
+
userMenuExtras?.map((e) => item(e.label, e.onClick)),
|
|
1167
|
+
onSignOut && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1168
|
+
/* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
|
|
1169
|
+
item("Sair", onSignOut, LogOut)
|
|
1170
|
+
] })
|
|
1171
|
+
] })
|
|
1172
|
+
]
|
|
1173
|
+
}
|
|
1174
|
+
) })
|
|
1175
|
+
] });
|
|
1176
|
+
}
|
|
1177
|
+
function getIcon2(name) {
|
|
1178
|
+
return ICON_MAP[name] ?? Home;
|
|
1179
|
+
}
|
|
1180
|
+
function NavDropdown({ item, currentPath, onNavigate }) {
|
|
1181
|
+
const [open, setOpen] = React2.useState(false);
|
|
1182
|
+
const Icon = getIcon2(item.icon);
|
|
1183
|
+
const isChildActive = item.children?.some(
|
|
1184
|
+
(child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
|
|
1185
|
+
);
|
|
1186
|
+
return /* @__PURE__ */ jsxs(Popover.Root, { open, onOpenChange: setOpen, children: [
|
|
1187
|
+
/* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
1188
|
+
"button",
|
|
1189
|
+
{
|
|
1190
|
+
"data-nav-active": isChildActive || void 0,
|
|
1191
|
+
className: cn(
|
|
1192
|
+
"flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
|
|
1193
|
+
isChildActive ? "border-sidebar-accent-foreground font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
|
|
1194
|
+
),
|
|
1195
|
+
children: [
|
|
1196
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
1197
|
+
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
1198
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: cn("h-3 w-3 transition-transform", open && "rotate-180") })
|
|
1199
|
+
]
|
|
1200
|
+
}
|
|
1201
|
+
) }),
|
|
1202
|
+
/* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
|
|
1203
|
+
Popover.Content,
|
|
1204
|
+
{
|
|
1205
|
+
align: "start",
|
|
1206
|
+
sideOffset: 4,
|
|
1207
|
+
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",
|
|
1208
|
+
children: item.children?.map((child) => {
|
|
1209
|
+
const ChildIcon = getIcon2(child.icon);
|
|
1210
|
+
const isActive = currentPath === child.route || currentPath.startsWith(child.route + "/");
|
|
1211
|
+
return /* @__PURE__ */ jsxs(
|
|
1291
1212
|
"button",
|
|
1292
1213
|
{
|
|
1293
|
-
onClick: () =>
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
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"
|
|
1304
|
-
),
|
|
1305
|
-
"aria-label": searchLabel,
|
|
1306
|
-
title: collapsed ? searchLabel : void 0,
|
|
1307
|
-
children: [
|
|
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
|
-
{
|
|
1214
|
+
onClick: () => {
|
|
1215
|
+
onNavigate(child.route);
|
|
1216
|
+
setOpen(false);
|
|
1217
|
+
},
|
|
1322
1218
|
className: cn(
|
|
1323
|
-
"
|
|
1324
|
-
|
|
1325
|
-
// scrollbar gutter eating into the nav items.
|
|
1326
|
-
outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
|
|
1219
|
+
"flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors",
|
|
1220
|
+
isActive ? "bg-muted font-medium text-foreground" : "text-popover-foreground hover:bg-muted/70"
|
|
1327
1221
|
),
|
|
1328
1222
|
children: [
|
|
1329
|
-
|
|
1330
|
-
|
|
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
|
-
)
|
|
1223
|
+
/* @__PURE__ */ jsx(ChildIcon, { className: "h-4 w-4 text-muted-foreground" }),
|
|
1224
|
+
/* @__PURE__ */ jsx("span", { children: child.label })
|
|
1351
1225
|
]
|
|
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
|
|
1362
1226
|
},
|
|
1363
|
-
|
|
1364
|
-
)
|
|
1365
|
-
|
|
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
|
-
]
|
|
1227
|
+
child.id
|
|
1228
|
+
);
|
|
1229
|
+
})
|
|
1390
1230
|
}
|
|
1391
1231
|
) })
|
|
1392
|
-
);
|
|
1232
|
+
] });
|
|
1233
|
+
}
|
|
1234
|
+
function Topbar({
|
|
1235
|
+
navigation,
|
|
1236
|
+
logo,
|
|
1237
|
+
user,
|
|
1238
|
+
onMenuClick,
|
|
1239
|
+
hideMobileChrome,
|
|
1240
|
+
orgSwitcher,
|
|
1241
|
+
leftContent,
|
|
1242
|
+
rightContent,
|
|
1243
|
+
currentPath = "/",
|
|
1244
|
+
onNavigate,
|
|
1245
|
+
searchPlaceholder = "Search...",
|
|
1246
|
+
onSignOut,
|
|
1247
|
+
onProfile,
|
|
1248
|
+
onSettings,
|
|
1249
|
+
onBilling,
|
|
1250
|
+
userPlan,
|
|
1251
|
+
billingLabel,
|
|
1252
|
+
userMenuExtras,
|
|
1253
|
+
userMenuSlot
|
|
1254
|
+
}) {
|
|
1255
|
+
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
1256
|
+
const mainNav = navigation.filter((item) => item.section === "main");
|
|
1257
|
+
const secondaryNav = navigation.filter((item) => item.section === "secondary");
|
|
1258
|
+
const allNav = [...mainNav, ...secondaryNav];
|
|
1259
|
+
const handleNavigate = (route) => {
|
|
1260
|
+
onNavigate?.(route);
|
|
1261
|
+
};
|
|
1262
|
+
return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
|
|
1263
|
+
/* @__PURE__ */ jsx("div", { className: cn("border-b border-sidebar-border bg-sidebar text-sidebar-foreground", hideMobileChrome && "hidden md:block"), children: /* @__PURE__ */ jsxs("div", { className: "flex h-14 w-full items-center justify-between px-4 md:px-6", children: [
|
|
1264
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
|
|
1265
|
+
onMenuClick && /* @__PURE__ */ jsx(
|
|
1266
|
+
"button",
|
|
1267
|
+
{
|
|
1268
|
+
onClick: onMenuClick,
|
|
1269
|
+
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",
|
|
1270
|
+
"aria-label": "Open menu",
|
|
1271
|
+
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
1272
|
+
}
|
|
1273
|
+
),
|
|
1274
|
+
/* @__PURE__ */ jsx("div", { className: "hidden min-w-0 shrink-0 items-center md:flex", children: orgSwitcher ?? logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
|
|
1275
|
+
] }),
|
|
1276
|
+
/* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
|
|
1277
|
+
"button",
|
|
1278
|
+
{
|
|
1279
|
+
onClick: () => setCommandPaletteOpen(true),
|
|
1280
|
+
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",
|
|
1281
|
+
children: [
|
|
1282
|
+
/* @__PURE__ */ jsx(Search, { className: "absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-sidebar-muted" }),
|
|
1283
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs", children: searchPlaceholder }),
|
|
1284
|
+
/* @__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" })
|
|
1285
|
+
]
|
|
1286
|
+
}
|
|
1287
|
+
) }),
|
|
1288
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [
|
|
1289
|
+
leftContent,
|
|
1290
|
+
rightContent,
|
|
1291
|
+
userMenuSlot ?? /* @__PURE__ */ jsx(
|
|
1292
|
+
TopbarUserMenu,
|
|
1293
|
+
{
|
|
1294
|
+
user,
|
|
1295
|
+
userPlan,
|
|
1296
|
+
billingLabel,
|
|
1297
|
+
onSignOut,
|
|
1298
|
+
onProfile,
|
|
1299
|
+
onSettings,
|
|
1300
|
+
onBilling,
|
|
1301
|
+
userMenuExtras
|
|
1302
|
+
}
|
|
1303
|
+
)
|
|
1304
|
+
] })
|
|
1305
|
+
] }) }),
|
|
1306
|
+
/* @__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) => {
|
|
1307
|
+
if (!el) return;
|
|
1308
|
+
const active = el.querySelector('[data-nav-active="true"]');
|
|
1309
|
+
if (active) {
|
|
1310
|
+
const left = active.offsetLeft - el.offsetWidth / 2 + active.offsetWidth / 2;
|
|
1311
|
+
el.scrollTo({ left: Math.max(0, left), behavior: "smooth" });
|
|
1312
|
+
}
|
|
1313
|
+
}, className: "flex items-center gap-0.5 overflow-x-auto scrollbar-none", children: allNav.map((item) => {
|
|
1314
|
+
if (item.children && item.children.length > 0) {
|
|
1315
|
+
return /* @__PURE__ */ jsx(
|
|
1316
|
+
NavDropdown,
|
|
1317
|
+
{
|
|
1318
|
+
item,
|
|
1319
|
+
currentPath,
|
|
1320
|
+
onNavigate: handleNavigate
|
|
1321
|
+
},
|
|
1322
|
+
item.id
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
const Icon = getIcon2(item.icon);
|
|
1326
|
+
const isActive = item.route === "/" ? currentPath === "/" : currentPath === item.route || currentPath.startsWith(item.route + "/");
|
|
1327
|
+
return /* @__PURE__ */ jsxs(
|
|
1328
|
+
"button",
|
|
1329
|
+
{
|
|
1330
|
+
"data-nav-active": isActive || void 0,
|
|
1331
|
+
onClick: () => handleNavigate(item.route),
|
|
1332
|
+
className: cn(
|
|
1333
|
+
"flex items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm transition-colors",
|
|
1334
|
+
isActive ? "border-primary font-semibold text-sidebar-foreground" : "border-transparent text-sidebar-muted hover:text-sidebar-foreground"
|
|
1335
|
+
),
|
|
1336
|
+
children: [
|
|
1337
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" }),
|
|
1338
|
+
/* @__PURE__ */ jsx("span", { children: item.label }),
|
|
1339
|
+
item.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 text-amber-500", "aria-hidden": true }),
|
|
1340
|
+
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 })
|
|
1341
|
+
]
|
|
1342
|
+
},
|
|
1343
|
+
item.id
|
|
1344
|
+
);
|
|
1345
|
+
}) }) }) })
|
|
1346
|
+
] });
|
|
1393
1347
|
}
|
|
1394
|
-
var ModuleHeaderSlotContext =
|
|
1348
|
+
var ModuleHeaderSlotContext = React2.createContext(null);
|
|
1395
1349
|
function useModuleHeaderSlot() {
|
|
1396
|
-
return
|
|
1350
|
+
return React2.useContext(ModuleHeaderSlotContext);
|
|
1397
1351
|
}
|
|
1398
|
-
var ModuleHeaderActionsSlotContext =
|
|
1352
|
+
var ModuleHeaderActionsSlotContext = React2.createContext(null);
|
|
1399
1353
|
function useModuleHeaderActionsSlot() {
|
|
1400
|
-
return
|
|
1354
|
+
return React2.useContext(ModuleHeaderActionsSlotContext);
|
|
1401
1355
|
}
|
|
1402
|
-
var PageRailSlotContext =
|
|
1356
|
+
var PageRailSlotContext = React2.createContext(null);
|
|
1403
1357
|
function usePageRailSlot() {
|
|
1404
|
-
return
|
|
1358
|
+
return React2.useContext(PageRailSlotContext);
|
|
1359
|
+
}
|
|
1360
|
+
function bottomNavReachesAll(items) {
|
|
1361
|
+
return !!items?.some((i) => "kind" in i && i.kind === "more");
|
|
1405
1362
|
}
|
|
1406
1363
|
function ShellPageTitle({
|
|
1407
1364
|
title,
|
|
1408
1365
|
back
|
|
1409
1366
|
}) {
|
|
1410
1367
|
if (!title && !back) return null;
|
|
1411
|
-
return /* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel: back?.label, onBack: back?.onBack });
|
|
1368
|
+
return /* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel: back?.label, onBack: back?.onBack, headingLevel: "h1" });
|
|
1412
1369
|
}
|
|
1413
1370
|
function SidebarLayout({
|
|
1414
1371
|
navigation = [],
|
|
@@ -1423,6 +1380,7 @@ function SidebarLayout({
|
|
|
1423
1380
|
userMenuExtras: _userMenuExtras,
|
|
1424
1381
|
orgSwitcher,
|
|
1425
1382
|
sidebarTopContent,
|
|
1383
|
+
sidebarIdentity,
|
|
1426
1384
|
sidebarNavTop,
|
|
1427
1385
|
sidebarFooterContent,
|
|
1428
1386
|
frame,
|
|
@@ -1436,23 +1394,28 @@ function SidebarLayout({
|
|
|
1436
1394
|
unreadCount = 0,
|
|
1437
1395
|
searchLabel,
|
|
1438
1396
|
hasBottomNav = false,
|
|
1397
|
+
barOwnsNav = false,
|
|
1439
1398
|
mobileHeader = "minimal",
|
|
1440
1399
|
sidebarSectionLabels,
|
|
1441
1400
|
sidebarNavKey,
|
|
1442
1401
|
sidebarNavDirection,
|
|
1443
|
-
sidebarNavChildConnector
|
|
1402
|
+
sidebarNavChildConnector,
|
|
1403
|
+
railOpen
|
|
1444
1404
|
}) {
|
|
1445
1405
|
const sidebarCollapsed = useLayoutStore((s) => s.sidebarCollapsed);
|
|
1446
|
-
const
|
|
1406
|
+
const pageHeaderClaimed = usePageHeaderClaimed();
|
|
1407
|
+
const setTitleSlot = usePageTitleSlotHost();
|
|
1447
1408
|
usePageHeaderHost();
|
|
1448
1409
|
const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
|
|
1449
|
-
const
|
|
1450
|
-
const
|
|
1451
|
-
const [
|
|
1452
|
-
const [
|
|
1410
|
+
const mobileMenuOpen = useLayoutStore((st) => st.mobileDrawerOpen);
|
|
1411
|
+
const setMobileMenuOpen = useLayoutStore((st) => st.setMobileDrawerOpen);
|
|
1412
|
+
const [headerSlot, setHeaderSlot] = React2.useState(null);
|
|
1413
|
+
const [actionsSlot, setActionsSlot] = React2.useState(null);
|
|
1414
|
+
const [pageRailSlot, setPageRailSlot] = React2.useState(null);
|
|
1453
1415
|
const hasPageActions = useHasPageActions();
|
|
1454
1416
|
return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsx(PageRailSlotContext.Provider, { value: pageRailSlot, children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
1455
|
-
"shell-enter flex
|
|
1417
|
+
"shell-enter fz-viewport-h flex overflow-hidden bg-background",
|
|
1418
|
+
hasBottomNav && "fz-has-bottom-bar",
|
|
1456
1419
|
// Frame mode: the inset gap around the framed card takes the sidebar (menu)
|
|
1457
1420
|
// color, so the white card reads as a panel floating on the same tone as the
|
|
1458
1421
|
// left menu. The card's own md:m-2 margin reveals this behind it.
|
|
@@ -1474,6 +1437,7 @@ function SidebarLayout({
|
|
|
1474
1437
|
onBilling,
|
|
1475
1438
|
orgSwitcher,
|
|
1476
1439
|
topContent: sidebarTopContent,
|
|
1440
|
+
identity: sidebarIdentity,
|
|
1477
1441
|
navTop: sidebarNavTop,
|
|
1478
1442
|
footerContent: sidebarFooterContent,
|
|
1479
1443
|
borderless: frame,
|
|
@@ -1492,7 +1456,13 @@ function SidebarLayout({
|
|
|
1492
1456
|
// `content`, not `card`: this is the page's own surface, and cards have
|
|
1493
1457
|
// to sit ON it. Painted card, the frame was the same tone as everything
|
|
1494
1458
|
// inside it and the page read as one flat sheet.
|
|
1495
|
-
frame && "md:
|
|
1459
|
+
frame && "md:my-2 md:ml-2 md:rounded-xl md:border md:border-border md:bg-content",
|
|
1460
|
+
// With a companion rail open, the page and the rail are ONE card: the
|
|
1461
|
+
// right edge opens and the rail closes it. Two cards with a strip of
|
|
1462
|
+
// chrome between them made the assistant read as a separate window
|
|
1463
|
+
// parked next to the app rather than as a column of it — the rail's own
|
|
1464
|
+
// left border is then the single seam between the two.
|
|
1465
|
+
frame && (railOpen ? "md:rounded-r-none md:border-r-0" : "md:mr-2")
|
|
1496
1466
|
), children: [
|
|
1497
1467
|
/* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
|
|
1498
1468
|
/* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 flex-col overflow-hidden", children: [
|
|
@@ -1503,7 +1473,7 @@ function SidebarLayout({
|
|
|
1503
1473
|
mobileHeader !== "minimal" && !hasPageActions && "hidden md:flex"
|
|
1504
1474
|
), children: [
|
|
1505
1475
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
|
|
1506
|
-
!
|
|
1476
|
+
!barOwnsNav && /* @__PURE__ */ jsx(
|
|
1507
1477
|
"button",
|
|
1508
1478
|
{
|
|
1509
1479
|
onClick: () => setMobileMenuOpen(true),
|
|
@@ -1512,7 +1482,8 @@ function SidebarLayout({
|
|
|
1512
1482
|
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
1513
1483
|
}
|
|
1514
1484
|
),
|
|
1515
|
-
|
|
1485
|
+
!pageHeaderClaimed && /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
|
|
1486
|
+
/* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" }),
|
|
1516
1487
|
topbarStart,
|
|
1517
1488
|
/* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
|
|
1518
1489
|
] }),
|
|
@@ -1539,6 +1510,7 @@ function SidebarLayout({
|
|
|
1539
1510
|
onBilling,
|
|
1540
1511
|
orgSwitcher,
|
|
1541
1512
|
topContent: sidebarTopContent,
|
|
1513
|
+
identity: sidebarIdentity,
|
|
1542
1514
|
navTop: sidebarNavTop,
|
|
1543
1515
|
footerContent: sidebarFooterContent,
|
|
1544
1516
|
userMenuSlot,
|
|
@@ -1563,6 +1535,8 @@ function TopbarLayout({
|
|
|
1563
1535
|
userPlan,
|
|
1564
1536
|
billingLabel,
|
|
1565
1537
|
userMenuExtras,
|
|
1538
|
+
userMenuSlot,
|
|
1539
|
+
drawerUserMenuSlot,
|
|
1566
1540
|
orgSwitcher,
|
|
1567
1541
|
frame,
|
|
1568
1542
|
currentPath,
|
|
@@ -1571,19 +1545,23 @@ function TopbarLayout({
|
|
|
1571
1545
|
pageTitle,
|
|
1572
1546
|
pageBack,
|
|
1573
1547
|
notificationSlot,
|
|
1574
|
-
|
|
1548
|
+
unreadCount = 0,
|
|
1549
|
+
hasBottomNav = false,
|
|
1550
|
+
barOwnsNav = false
|
|
1575
1551
|
}) {
|
|
1576
|
-
const
|
|
1577
|
-
const
|
|
1578
|
-
const [
|
|
1579
|
-
const [
|
|
1580
|
-
const
|
|
1552
|
+
const mobileMenuOpen = useLayoutStore((st) => st.mobileDrawerOpen);
|
|
1553
|
+
const setMobileMenuOpen = useLayoutStore((st) => st.setMobileDrawerOpen);
|
|
1554
|
+
const [headerSlot, setHeaderSlot] = React2.useState(null);
|
|
1555
|
+
const [actionsSlot, setActionsSlot] = React2.useState(null);
|
|
1556
|
+
const [pageRailSlot, setPageRailSlot] = React2.useState(null);
|
|
1557
|
+
const rawClaimed = usePageHeaderClaimed();
|
|
1558
|
+
const setTitleSlot = usePageTitleSlotHost();
|
|
1581
1559
|
const hasOutlet = useHasPageHeaderOutlet();
|
|
1582
1560
|
const hasPageActions = useHasPageActions();
|
|
1583
1561
|
usePageHeaderHost();
|
|
1584
|
-
const
|
|
1562
|
+
const pageHeaderClaimed = hasOutlet ? false : rawClaimed;
|
|
1585
1563
|
const showTitle = !hasOutlet && !!pageTitle;
|
|
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
|
|
1564
|
+
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("shell-enter fz-viewport-h relative flex flex-col overflow-hidden bg-background", hasBottomNav && "fz-has-bottom-bar"), children: [
|
|
1587
1565
|
/* @__PURE__ */ jsx(
|
|
1588
1566
|
Topbar,
|
|
1589
1567
|
{
|
|
@@ -1599,25 +1577,37 @@ function TopbarLayout({
|
|
|
1599
1577
|
userPlan,
|
|
1600
1578
|
billingLabel,
|
|
1601
1579
|
userMenuExtras,
|
|
1580
|
+
userMenuSlot,
|
|
1602
1581
|
orgSwitcher,
|
|
1603
1582
|
leftContent: topbarStart,
|
|
1604
1583
|
rightContent: topbarEnd || notificationSlot ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1605
1584
|
topbarEnd,
|
|
1606
1585
|
notificationSlot
|
|
1607
1586
|
] }) : void 0,
|
|
1608
|
-
onMenuClick:
|
|
1587
|
+
onMenuClick: barOwnsNav ? void 0 : () => setMobileMenuOpen(true),
|
|
1588
|
+
hideMobileChrome: barOwnsNav
|
|
1609
1589
|
}
|
|
1610
1590
|
),
|
|
1611
1591
|
/* @__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
1592
|
/* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
|
|
1613
1593
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", children: [
|
|
1614
|
-
(
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1594
|
+
(pageHeaderClaimed || pageBack || hasPageActions || barOwnsNav) && /* @__PURE__ */ jsxs(
|
|
1595
|
+
"div",
|
|
1596
|
+
{
|
|
1597
|
+
className: cn(
|
|
1598
|
+
"flex min-h-12 shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-2",
|
|
1599
|
+
!(pageHeaderClaimed || pageBack || hasPageActions) && "md:hidden"
|
|
1600
|
+
),
|
|
1601
|
+
children: [
|
|
1602
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
|
|
1603
|
+
!pageHeaderClaimed && /* @__PURE__ */ jsx(ShellPageTitle, { title: showTitle ? pageTitle : void 0, back: pageBack }),
|
|
1604
|
+
/* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" }),
|
|
1605
|
+
/* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
|
|
1606
|
+
] }),
|
|
1607
|
+
/* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex shrink-0 items-center gap-2" })
|
|
1608
|
+
]
|
|
1609
|
+
}
|
|
1610
|
+
),
|
|
1621
1611
|
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", children })
|
|
1622
1612
|
] })
|
|
1623
1613
|
] }) }),
|
|
@@ -1635,7 +1625,9 @@ function TopbarLayout({
|
|
|
1635
1625
|
onSettings,
|
|
1636
1626
|
onBilling,
|
|
1637
1627
|
orgSwitcher,
|
|
1638
|
-
userMenuSlot: user ? /* @__PURE__ */ jsx(DrawerUserRow, { user }) : void 0,
|
|
1628
|
+
userMenuSlot: drawerUserMenuSlot ?? (user ? /* @__PURE__ */ jsx(DrawerUserRow, { user }) : void 0),
|
|
1629
|
+
notificationSlot,
|
|
1630
|
+
unreadCount,
|
|
1639
1631
|
onClose: () => setMobileMenuOpen(false)
|
|
1640
1632
|
}
|
|
1641
1633
|
)
|
|
@@ -1652,9 +1644,10 @@ function MinimalLayout({
|
|
|
1652
1644
|
logo,
|
|
1653
1645
|
user: _user,
|
|
1654
1646
|
children,
|
|
1655
|
-
headerEnd
|
|
1647
|
+
headerEnd,
|
|
1648
|
+
hasBottomNav = false
|
|
1656
1649
|
}) {
|
|
1657
|
-
return /* @__PURE__ */ jsxs("div", { className: "shell-enter
|
|
1650
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("shell-enter fz-viewport-h flex flex-col", hasBottomNav && "fz-has-bottom-bar"), children: [
|
|
1658
1651
|
/* @__PURE__ */ jsxs("header", { className: "flex h-14 shrink-0 items-center justify-between border-b border-border bg-card px-4 md:px-6", children: [
|
|
1659
1652
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
|
|
1660
1653
|
headerEnd && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: headerEnd })
|
|
@@ -1663,23 +1656,23 @@ function MinimalLayout({
|
|
|
1663
1656
|
] });
|
|
1664
1657
|
}
|
|
1665
1658
|
function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
|
|
1666
|
-
const [visible, setVisible] =
|
|
1667
|
-
|
|
1659
|
+
const [visible, setVisible] = React2.useState(false);
|
|
1660
|
+
React2.useEffect(() => {
|
|
1668
1661
|
const id = requestAnimationFrame(() => setVisible(true));
|
|
1669
1662
|
return () => cancelAnimationFrame(id);
|
|
1670
1663
|
}, []);
|
|
1671
|
-
const close =
|
|
1664
|
+
const close = React2.useCallback(() => {
|
|
1672
1665
|
setVisible(false);
|
|
1673
1666
|
window.setTimeout(onClose, 250);
|
|
1674
1667
|
}, [onClose]);
|
|
1675
|
-
|
|
1668
|
+
React2.useEffect(() => {
|
|
1676
1669
|
const previous = document.body.style.overflow;
|
|
1677
1670
|
document.body.style.overflow = "hidden";
|
|
1678
1671
|
return () => {
|
|
1679
1672
|
document.body.style.overflow = previous;
|
|
1680
1673
|
};
|
|
1681
1674
|
}, []);
|
|
1682
|
-
|
|
1675
|
+
React2.useEffect(() => {
|
|
1683
1676
|
const onKey = (e) => {
|
|
1684
1677
|
if (e.key === "Escape") close();
|
|
1685
1678
|
};
|
|
@@ -1710,6 +1703,7 @@ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
|
|
|
1710
1703
|
...sidebarProps,
|
|
1711
1704
|
collapsed: false,
|
|
1712
1705
|
onToggle: close,
|
|
1706
|
+
drawer: true,
|
|
1713
1707
|
borderless: true,
|
|
1714
1708
|
onNavigate: (route) => {
|
|
1715
1709
|
onNavigate?.(route);
|
|
@@ -1721,19 +1715,7 @@ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
|
|
|
1721
1715
|
)
|
|
1722
1716
|
] });
|
|
1723
1717
|
}
|
|
1724
|
-
var
|
|
1725
|
-
Home: Home,
|
|
1726
|
-
Receipt: Receipt,
|
|
1727
|
-
ArrowLeftRight: ArrowLeftRight,
|
|
1728
|
-
Calendar: Calendar,
|
|
1729
|
-
CreditCard: CreditCard,
|
|
1730
|
-
Settings: Settings,
|
|
1731
|
-
Menu: Menu,
|
|
1732
|
-
MessageCircle: MessageCircle,
|
|
1733
|
-
Wallet: Wallet,
|
|
1734
|
-
PiggyBank,
|
|
1735
|
-
Plus: Plus
|
|
1736
|
-
};
|
|
1718
|
+
var bottomNavIcon = (name, fallback = Home) => (name ? ICON_MAP[name] : void 0) ?? fallback;
|
|
1737
1719
|
function FloatingProfileAvatar({ user, onClick }) {
|
|
1738
1720
|
if (!user) return null;
|
|
1739
1721
|
const label = user.fullName || user.email;
|
|
@@ -1759,43 +1741,80 @@ function MobileBottomNav({
|
|
|
1759
1741
|
items,
|
|
1760
1742
|
currentPath,
|
|
1761
1743
|
onNavigate,
|
|
1762
|
-
onAction
|
|
1744
|
+
onAction,
|
|
1745
|
+
onMore,
|
|
1746
|
+
onAssistant,
|
|
1747
|
+
assistantOpen,
|
|
1748
|
+
moreOpen,
|
|
1749
|
+
retracted
|
|
1763
1750
|
}) {
|
|
1764
1751
|
const path = currentPath ?? "";
|
|
1765
|
-
return /* @__PURE__ */ jsx(
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1752
|
+
return /* @__PURE__ */ jsx(
|
|
1753
|
+
"nav",
|
|
1754
|
+
{
|
|
1755
|
+
"aria-label": "Navega\xE7\xE3o principal",
|
|
1756
|
+
"aria-hidden": retracted || void 0,
|
|
1757
|
+
className: cn(
|
|
1758
|
+
"fz-safe-b fixed inset-x-0 bottom-0 z-50 flex min-h-16 items-stretch justify-around border-t border-border bg-background md:hidden",
|
|
1759
|
+
"motion-safe:transition-transform motion-safe:duration-200 motion-safe:ease-out",
|
|
1760
|
+
retracted && "pointer-events-none translate-y-full"
|
|
1761
|
+
),
|
|
1762
|
+
children: items.map((item, i) => {
|
|
1763
|
+
if ("kind" in item && item.kind === "action") {
|
|
1764
|
+
const ActionIcon = bottomNavIcon(item.icon, Plus);
|
|
1765
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-start justify-center", children: /* @__PURE__ */ jsx(
|
|
1766
|
+
"button",
|
|
1767
|
+
{
|
|
1768
|
+
type: "button",
|
|
1769
|
+
onClick: () => onAction?.(item.id),
|
|
1770
|
+
"aria-label": item.label ?? "Add",
|
|
1771
|
+
className: "-mt-6 flex h-14 w-14 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg shadow-primary/30 ring-4 ring-background transition-transform active:scale-95",
|
|
1772
|
+
children: /* @__PURE__ */ jsx(ActionIcon, { className: "h-6 w-6" })
|
|
1773
|
+
}
|
|
1774
|
+
) }, item.id);
|
|
1776
1775
|
}
|
|
1777
|
-
|
|
1776
|
+
const isMore = "kind" in item && item.kind === "more";
|
|
1777
|
+
const isAssistant = "kind" in item && item.kind === "assistant";
|
|
1778
|
+
const label = isMore ? item.label ?? "Mais" : isAssistant ? item.label ?? "IA" : item.label;
|
|
1779
|
+
const Icon = bottomNavIcon(
|
|
1780
|
+
item.icon,
|
|
1781
|
+
isMore ? Menu : isAssistant ? Sparkles : Home
|
|
1782
|
+
);
|
|
1783
|
+
const route = isMore || isAssistant ? void 0 : item.route;
|
|
1784
|
+
const isActive = isMore ? !!moreOpen : isAssistant ? !!assistantOpen : route === "/" ? path === "/" || path === "" : !!route && (path === route || path.startsWith(route + "/"));
|
|
1785
|
+
return /* @__PURE__ */ jsxs(
|
|
1786
|
+
"button",
|
|
1787
|
+
{
|
|
1788
|
+
type: "button",
|
|
1789
|
+
onClick: () => isMore ? onMore?.() : isAssistant ? onAssistant?.() : onNavigate?.(route),
|
|
1790
|
+
"aria-current": isActive && route ? "page" : void 0,
|
|
1791
|
+
"aria-expanded": isMore || isAssistant ? isActive : void 0,
|
|
1792
|
+
className: cn(
|
|
1793
|
+
// min-h-[44px] is the floor a finger needs; the bar was letting
|
|
1794
|
+
// content decide and landing around 40.
|
|
1795
|
+
"relative flex min-h-[44px] flex-1 flex-col items-center justify-center gap-0.5 px-1 py-2 transition-colors",
|
|
1796
|
+
isActive ? "text-primary" : "text-muted-foreground"
|
|
1797
|
+
),
|
|
1798
|
+
children: [
|
|
1799
|
+
/* @__PURE__ */ jsx(
|
|
1800
|
+
"span",
|
|
1801
|
+
{
|
|
1802
|
+
"aria-hidden": true,
|
|
1803
|
+
className: cn(
|
|
1804
|
+
"absolute inset-x-3 top-0 h-0.5 rounded-full transition-opacity",
|
|
1805
|
+
isActive ? "bg-primary opacity-100" : "opacity-0"
|
|
1806
|
+
)
|
|
1807
|
+
}
|
|
1808
|
+
),
|
|
1809
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 shrink-0" }),
|
|
1810
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-full truncate text-[11px] leading-tight", children: label })
|
|
1811
|
+
]
|
|
1812
|
+
},
|
|
1813
|
+
isMore ? "more" : isAssistant ? "assistant" : `${route}-${i}`
|
|
1814
|
+
);
|
|
1815
|
+
})
|
|
1778
1816
|
}
|
|
1779
|
-
|
|
1780
|
-
const Icon = BOTTOM_NAV_ICONS[item.icon] ?? Home;
|
|
1781
|
-
return /* @__PURE__ */ jsxs(
|
|
1782
|
-
"button",
|
|
1783
|
-
{
|
|
1784
|
-
type: "button",
|
|
1785
|
-
onClick: () => onNavigate?.(item.route),
|
|
1786
|
-
className: cn(
|
|
1787
|
-
"flex flex-1 flex-col items-center justify-center gap-0.5 px-1 py-1 transition-colors",
|
|
1788
|
-
isActive ? "text-primary" : "text-muted-foreground"
|
|
1789
|
-
),
|
|
1790
|
-
"aria-current": isActive ? "page" : void 0,
|
|
1791
|
-
children: [
|
|
1792
|
-
/* @__PURE__ */ jsx(Icon, { className: "h-5 w-5" }),
|
|
1793
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs", children: item.label })
|
|
1794
|
-
]
|
|
1795
|
-
},
|
|
1796
|
-
item.route
|
|
1797
|
-
);
|
|
1798
|
-
}) });
|
|
1817
|
+
);
|
|
1799
1818
|
}
|
|
1800
1819
|
function AppShell({
|
|
1801
1820
|
variant,
|
|
@@ -1819,12 +1838,16 @@ function AppShell({
|
|
|
1819
1838
|
topbarStart,
|
|
1820
1839
|
topbarEnd,
|
|
1821
1840
|
sidebarTopContent,
|
|
1841
|
+
sidebarIdentity,
|
|
1822
1842
|
sidebarNavTop,
|
|
1823
1843
|
sidebarFooterContent,
|
|
1824
1844
|
bottomNav,
|
|
1825
1845
|
onBottomNavAction,
|
|
1846
|
+
onAssistantToggle,
|
|
1847
|
+
assistantOpen,
|
|
1826
1848
|
mobileHeader = "minimal",
|
|
1827
1849
|
userMenuSlot,
|
|
1850
|
+
drawerUserMenuSlot,
|
|
1828
1851
|
notificationSlot,
|
|
1829
1852
|
unreadCount = 0,
|
|
1830
1853
|
searchLabel,
|
|
@@ -1841,13 +1864,26 @@ function AppShell({
|
|
|
1841
1864
|
rightRailResizeLabel
|
|
1842
1865
|
}) {
|
|
1843
1866
|
const hasBottomNav = !!bottomNav?.length;
|
|
1867
|
+
const barOwnsNav = bottomNavReachesAll(bottomNav);
|
|
1868
|
+
const pageClaimedBack = useHasPageHeaderBack();
|
|
1869
|
+
const barRetracted = !!pageBack || pageClaimedBack;
|
|
1870
|
+
const mobileDrawerOpen = useLayoutStore((st) => st.mobileDrawerOpen);
|
|
1871
|
+
const setMobileDrawerOpen = useLayoutStore((st) => st.setMobileDrawerOpen);
|
|
1872
|
+
React2.useEffect(() => {
|
|
1873
|
+
setMobileDrawerOpen(false);
|
|
1874
|
+
}, [currentPath]);
|
|
1844
1875
|
const bottomBar = hasBottomNav ? /* @__PURE__ */ jsx(
|
|
1845
1876
|
MobileBottomNav,
|
|
1846
1877
|
{
|
|
1847
1878
|
items: bottomNav,
|
|
1848
1879
|
currentPath,
|
|
1849
1880
|
onNavigate,
|
|
1850
|
-
onAction: onBottomNavAction
|
|
1881
|
+
onAction: onBottomNavAction,
|
|
1882
|
+
onMore: () => setMobileDrawerOpen(!mobileDrawerOpen),
|
|
1883
|
+
moreOpen: mobileDrawerOpen,
|
|
1884
|
+
onAssistant: onAssistantToggle,
|
|
1885
|
+
assistantOpen,
|
|
1886
|
+
retracted: barRetracted
|
|
1851
1887
|
}
|
|
1852
1888
|
) : null;
|
|
1853
1889
|
const floatingAvatar = mobileHeader === "transparent" && user ? /* @__PURE__ */ jsx(FloatingProfileAvatar, { user, onClick: onProfile }) : null;
|
|
@@ -1868,6 +1904,7 @@ function AppShell({
|
|
|
1868
1904
|
userMenuExtras,
|
|
1869
1905
|
orgSwitcher,
|
|
1870
1906
|
sidebarTopContent,
|
|
1907
|
+
sidebarIdentity,
|
|
1871
1908
|
sidebarNavTop,
|
|
1872
1909
|
sidebarFooterContent,
|
|
1873
1910
|
frame: contentFrame,
|
|
@@ -1880,12 +1917,14 @@ function AppShell({
|
|
|
1880
1917
|
notificationSlot,
|
|
1881
1918
|
unreadCount,
|
|
1882
1919
|
searchLabel,
|
|
1883
|
-
hasBottomNav,
|
|
1920
|
+
hasBottomNav: hasBottomNav && !barRetracted,
|
|
1921
|
+
barOwnsNav,
|
|
1884
1922
|
mobileHeader,
|
|
1885
1923
|
sidebarSectionLabels,
|
|
1886
1924
|
sidebarNavKey,
|
|
1887
1925
|
sidebarNavDirection,
|
|
1888
1926
|
sidebarNavChildConnector,
|
|
1927
|
+
railOpen: !!rightRail && rightRailOpen,
|
|
1889
1928
|
children
|
|
1890
1929
|
}
|
|
1891
1930
|
);
|
|
@@ -1905,6 +1944,8 @@ function AppShell({
|
|
|
1905
1944
|
userPlan,
|
|
1906
1945
|
billingLabel,
|
|
1907
1946
|
userMenuExtras,
|
|
1947
|
+
userMenuSlot,
|
|
1948
|
+
drawerUserMenuSlot,
|
|
1908
1949
|
orgSwitcher,
|
|
1909
1950
|
frame: contentFrame,
|
|
1910
1951
|
currentPath,
|
|
@@ -1913,13 +1954,14 @@ function AppShell({
|
|
|
1913
1954
|
pageTitle,
|
|
1914
1955
|
pageBack,
|
|
1915
1956
|
notificationSlot,
|
|
1916
|
-
hasBottomNav,
|
|
1957
|
+
hasBottomNav: hasBottomNav && !barRetracted,
|
|
1958
|
+
barOwnsNav,
|
|
1917
1959
|
children
|
|
1918
1960
|
}
|
|
1919
1961
|
);
|
|
1920
1962
|
break;
|
|
1921
1963
|
case "minimal":
|
|
1922
|
-
layoutElement = /* @__PURE__ */ jsx(MinimalLayout, { logo, user, headerEnd: topbarEnd, children });
|
|
1964
|
+
layoutElement = /* @__PURE__ */ jsx(MinimalLayout, { logo, user, headerEnd: topbarEnd, hasBottomNav: hasBottomNav && !barRetracted, children });
|
|
1923
1965
|
break;
|
|
1924
1966
|
default: {
|
|
1925
1967
|
return null;
|
|
@@ -1955,13 +1997,13 @@ function SplitWithRightRail({
|
|
|
1955
1997
|
maxWidth = 720,
|
|
1956
1998
|
resizeLabel = "Resize panel"
|
|
1957
1999
|
}) {
|
|
1958
|
-
const [width, setWidth] =
|
|
1959
|
-
const [dragging, setDragging] =
|
|
1960
|
-
const clamp =
|
|
2000
|
+
const [width, setWidth] = React2.useState(defaultWidth);
|
|
2001
|
+
const [dragging, setDragging] = React2.useState(false);
|
|
2002
|
+
const clamp = React2.useCallback(
|
|
1961
2003
|
(value) => Math.min(maxWidth, Math.max(minWidth, value)),
|
|
1962
2004
|
[minWidth, maxWidth]
|
|
1963
2005
|
);
|
|
1964
|
-
|
|
2006
|
+
React2.useEffect(() => {
|
|
1965
2007
|
if (!dragging) return;
|
|
1966
2008
|
const onMove = (event) => {
|
|
1967
2009
|
event.preventDefault();
|
|
@@ -1983,7 +2025,7 @@ function SplitWithRightRail({
|
|
|
1983
2025
|
document.body.style.cursor = previousCursor;
|
|
1984
2026
|
};
|
|
1985
2027
|
}, [dragging, clamp]);
|
|
1986
|
-
|
|
2028
|
+
React2.useEffect(() => {
|
|
1987
2029
|
if (dragging) return;
|
|
1988
2030
|
onWidthChange?.(width);
|
|
1989
2031
|
}, [dragging]);
|
|
@@ -1993,7 +2035,7 @@ function SplitWithRightRail({
|
|
|
1993
2035
|
// chrome, next to the menu — painting them page-white put a bright strip
|
|
1994
2036
|
// between the dark chrome and the rail, which read as a seam rather than
|
|
1995
2037
|
// as space.
|
|
1996
|
-
/* @__PURE__ */ jsxs("div", { className: cn("flex
|
|
2038
|
+
/* @__PURE__ */ jsxs("div", { className: cn("fz-viewport-h flex w-full overflow-hidden bg-sidebar", !frame && "md:bg-background"), children: [
|
|
1997
2039
|
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children }),
|
|
1998
2040
|
/* @__PURE__ */ jsxs(
|
|
1999
2041
|
"div",
|
|
@@ -2026,7 +2068,11 @@ function SplitWithRightRail({
|
|
|
2026
2068
|
}
|
|
2027
2069
|
},
|
|
2028
2070
|
className: cn(
|
|
2029
|
-
|
|
2071
|
+
// Zero width: the divider USED to be a 4px gutter, which meant the
|
|
2072
|
+
// page and the rail could never touch — there was always a strip of
|
|
2073
|
+
// chrome between them. It now sits exactly on the seam the rail's own
|
|
2074
|
+
// left border draws, and lights that border up when you reach for it.
|
|
2075
|
+
"group relative hidden w-0 shrink-0 cursor-col-resize focus-visible:outline-none",
|
|
2030
2076
|
open && "md:block"
|
|
2031
2077
|
),
|
|
2032
2078
|
children: [
|
|
@@ -2034,7 +2080,7 @@ function SplitWithRightRail({
|
|
|
2034
2080
|
"span",
|
|
2035
2081
|
{
|
|
2036
2082
|
className: cn(
|
|
2037
|
-
"absolute inset-y-6 left-
|
|
2083
|
+
"absolute inset-y-6 -left-px w-0.5 rounded-full bg-transparent transition-colors",
|
|
2038
2084
|
"group-hover:bg-primary/60 group-focus-visible:bg-primary",
|
|
2039
2085
|
dragging && "bg-primary"
|
|
2040
2086
|
)
|
|
@@ -2067,6 +2113,7 @@ function HeaderTabs({
|
|
|
2067
2113
|
value,
|
|
2068
2114
|
onChange,
|
|
2069
2115
|
className,
|
|
2116
|
+
fill,
|
|
2070
2117
|
...rest
|
|
2071
2118
|
}) {
|
|
2072
2119
|
return /* @__PURE__ */ jsx(
|
|
@@ -2075,7 +2122,8 @@ function HeaderTabs({
|
|
|
2075
2122
|
role: "tablist",
|
|
2076
2123
|
"aria-label": rest["aria-label"],
|
|
2077
2124
|
className: cn(
|
|
2078
|
-
"inline-flex max-w-full
|
|
2125
|
+
"inline-flex max-w-full gap-5 overflow-x-auto scrollbar-none",
|
|
2126
|
+
fill ? "items-stretch" : "items-center",
|
|
2079
2127
|
className
|
|
2080
2128
|
),
|
|
2081
2129
|
children: tabs.map((tab) => {
|
|
@@ -2089,7 +2137,10 @@ function HeaderTabs({
|
|
|
2089
2137
|
"aria-selected": active,
|
|
2090
2138
|
onClick: () => onChange(tab.value),
|
|
2091
2139
|
className: cn(
|
|
2092
|
-
"relative flex shrink-0 items-center gap-1.5 whitespace-nowrap
|
|
2140
|
+
"relative flex shrink-0 items-center gap-1.5 whitespace-nowrap text-sm transition-colors",
|
|
2141
|
+
// Filled: the row owns the band's height and the underline sits on
|
|
2142
|
+
// its bottom edge. Otherwise the underline trails the label by 6px.
|
|
2143
|
+
fill ? "h-full" : "pb-1.5",
|
|
2093
2144
|
active ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
2094
2145
|
),
|
|
2095
2146
|
children: [
|
|
@@ -2124,12 +2175,12 @@ function PageTabs({ actions, className, ...props }) {
|
|
|
2124
2175
|
}
|
|
2125
2176
|
);
|
|
2126
2177
|
}
|
|
2127
|
-
var NavTransitionContext =
|
|
2178
|
+
var NavTransitionContext = React2.createContext("slide");
|
|
2128
2179
|
function NavTransitionProvider({ value, children }) {
|
|
2129
2180
|
return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
|
|
2130
2181
|
}
|
|
2131
2182
|
function useNavTransition() {
|
|
2132
|
-
return
|
|
2183
|
+
return React2.useContext(NavTransitionContext);
|
|
2133
2184
|
}
|
|
2134
2185
|
function resolveTransitionClass(transition, direction) {
|
|
2135
2186
|
if (transition === "none") return "";
|
|
@@ -2164,16 +2215,16 @@ function PremiumBadge({ className }) {
|
|
|
2164
2215
|
const tip = t("upgrade.badgeTooltip");
|
|
2165
2216
|
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 }) });
|
|
2166
2217
|
}
|
|
2167
|
-
var ModuleLayoutContext =
|
|
2218
|
+
var ModuleLayoutContext = React2.createContext("rail");
|
|
2168
2219
|
function ModuleLayoutProvider({ variant, children }) {
|
|
2169
2220
|
return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
|
|
2170
2221
|
}
|
|
2171
2222
|
function useModuleLayout() {
|
|
2172
|
-
return
|
|
2223
|
+
return React2.useContext(ModuleLayoutContext);
|
|
2173
2224
|
}
|
|
2174
2225
|
function RailNavItem({ item }) {
|
|
2175
|
-
const [expanded, setExpanded] =
|
|
2176
|
-
const Icon = item.icon ?
|
|
2226
|
+
const [expanded, setExpanded] = React2.useState(true);
|
|
2227
|
+
const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
|
|
2177
2228
|
const hasChildren = item.children && item.children.length > 0;
|
|
2178
2229
|
const hasActiveChild = hasChildren && item.children.some((c) => c.active);
|
|
2179
2230
|
const isActive = item.active || hasActiveChild;
|
|
@@ -2213,10 +2264,14 @@ function RailNavItem({ item }) {
|
|
|
2213
2264
|
)) })
|
|
2214
2265
|
] });
|
|
2215
2266
|
}
|
|
2267
|
+
function defaultChildOf(item) {
|
|
2268
|
+
const kids = item.children ?? [];
|
|
2269
|
+
return kids.find((c) => c.defaultChild) ?? kids.find((c) => /(^|-)list$/i.test(c.id)) ?? kids.find((c) => !/(^|-)(new|novo|nova|create)$/i.test(c.id)) ?? kids[0];
|
|
2270
|
+
}
|
|
2216
2271
|
function MobileTabs({ nav }) {
|
|
2217
|
-
const scrollRef =
|
|
2218
|
-
const [expandedId, setExpandedId] =
|
|
2219
|
-
|
|
2272
|
+
const scrollRef = React2.useRef(null);
|
|
2273
|
+
const [expandedId, setExpandedId] = React2.useState(null);
|
|
2274
|
+
React2.useEffect(() => {
|
|
2220
2275
|
const container = scrollRef.current;
|
|
2221
2276
|
if (!container) return;
|
|
2222
2277
|
const activeEl = container.querySelector('[data-active="true"]');
|
|
@@ -2227,7 +2282,7 @@ function MobileTabs({ nav }) {
|
|
|
2227
2282
|
}, [nav]);
|
|
2228
2283
|
return /* @__PURE__ */ jsxs("div", { className: "md:hidden -mx-1 mb-4 no-print", children: [
|
|
2229
2284
|
/* @__PURE__ */ jsx("div", { ref: scrollRef, className: "flex gap-1 px-1 pb-2 overflow-x-auto scrollbar-none", children: nav.map((item) => {
|
|
2230
|
-
const Icon = item.icon ?
|
|
2285
|
+
const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
|
|
2231
2286
|
const hasChildren = item.children && item.children.length > 0;
|
|
2232
2287
|
const isActive = item.active || item.children?.some((c) => c.active);
|
|
2233
2288
|
const isExpanded = expandedId === item.id;
|
|
@@ -2238,6 +2293,7 @@ function MobileTabs({ nav }) {
|
|
|
2238
2293
|
"data-active": isActive || void 0,
|
|
2239
2294
|
onClick: () => {
|
|
2240
2295
|
if (hasChildren) {
|
|
2296
|
+
if (!isExpanded) defaultChildOf(item)?.onClick?.();
|
|
2241
2297
|
setExpandedId(isExpanded ? null : item.id);
|
|
2242
2298
|
} else {
|
|
2243
2299
|
item.onClick?.();
|
|
@@ -2245,11 +2301,15 @@ function MobileTabs({ nav }) {
|
|
|
2245
2301
|
}
|
|
2246
2302
|
},
|
|
2247
2303
|
className: cn(
|
|
2248
|
-
|
|
2304
|
+
// This strip is the most-tapped control in most modules and it
|
|
2305
|
+
// was 28px tall — a third under the comfortable minimum. Height
|
|
2306
|
+
// and text step up on touch; the desktop rail is unaffected.
|
|
2307
|
+
"flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full px-4 text-sm font-medium transition-colors",
|
|
2308
|
+
"min-h-[2.75rem] active:scale-[0.98]",
|
|
2249
2309
|
isActive ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"
|
|
2250
2310
|
),
|
|
2251
2311
|
children: [
|
|
2252
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-3 w-3" }),
|
|
2312
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
|
|
2253
2313
|
item.label,
|
|
2254
2314
|
item.premium && /* @__PURE__ */ jsx(PremiumBadge, {}),
|
|
2255
2315
|
hasChildren && /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-2.5 w-2.5 transition-transform", isExpanded && "rotate-180") })
|
|
@@ -2283,12 +2343,12 @@ function MobileTabs({ nav }) {
|
|
|
2283
2343
|
})()
|
|
2284
2344
|
] });
|
|
2285
2345
|
}
|
|
2286
|
-
var BackStyleContext =
|
|
2346
|
+
var BackStyleContext = React2.createContext("link");
|
|
2287
2347
|
function BackStyleProvider({ style, children }) {
|
|
2288
2348
|
return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
|
|
2289
2349
|
}
|
|
2290
2350
|
function useBackStyle() {
|
|
2291
|
-
return
|
|
2351
|
+
return React2.useContext(BackStyleContext);
|
|
2292
2352
|
}
|
|
2293
2353
|
function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle, inline }) {
|
|
2294
2354
|
const t = useTranslation();
|
|
@@ -2298,7 +2358,7 @@ function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, ba
|
|
|
2298
2358
|
const effectiveOnBack = inline ? onBack : cross.onBack;
|
|
2299
2359
|
const effectiveParentLabel = inline ? parentLabel : cross.parentLabel;
|
|
2300
2360
|
useBackHandler(effectiveOnBack);
|
|
2301
|
-
const Icon = icon ?
|
|
2361
|
+
const Icon = icon ? ICON_MAP[icon] ?? null : null;
|
|
2302
2362
|
if (inline && effectiveOnBack) {
|
|
2303
2363
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2304
2364
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
|
|
@@ -2380,15 +2440,16 @@ function ShellSubpageHeader({
|
|
|
2380
2440
|
parentLabel,
|
|
2381
2441
|
actions
|
|
2382
2442
|
}) {
|
|
2383
|
-
usePageHeader(
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2443
|
+
usePageHeader(true, { back: !!onBack });
|
|
2444
|
+
const crumb = /* @__PURE__ */ jsx(PageTitlePortal, { children: /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
2445
|
+
Icon && !onBack && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
2446
|
+
/* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel, onBack })
|
|
2447
|
+
] }) });
|
|
2448
|
+
if (!actions) return crumb;
|
|
2449
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2450
|
+
crumb,
|
|
2451
|
+
/* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions }) })
|
|
2452
|
+
] });
|
|
2392
2453
|
}
|
|
2393
2454
|
function ModuleHeader({ nav }) {
|
|
2394
2455
|
function defaultChild(item) {
|
|
@@ -2399,7 +2460,7 @@ function ModuleHeader({ nav }) {
|
|
|
2399
2460
|
const tabs = nav.map((item) => ({
|
|
2400
2461
|
value: item.id,
|
|
2401
2462
|
label: item.label,
|
|
2402
|
-
icon: item.icon ?
|
|
2463
|
+
icon: item.icon ? ICON_MAP[item.icon] ?? void 0 : void 0,
|
|
2403
2464
|
// Plan-gated views keep their upgrade marker — dropping it would hide the
|
|
2404
2465
|
// only signal that the tab costs an upgrade.
|
|
2405
2466
|
badge: item.premium ? /* @__PURE__ */ jsx(PremiumBadge, {}) : void 0
|
|
@@ -2438,12 +2499,12 @@ function ModulePage({ title: _title, subtitle, nav, children, className, headerA
|
|
|
2438
2499
|
] }) }),
|
|
2439
2500
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2440
2501
|
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
2441
|
-
/* @__PURE__ */ jsx(MobileTabs, { nav }),
|
|
2502
|
+
!onSubpage && /* @__PURE__ */ jsx(MobileTabs, { nav }),
|
|
2442
2503
|
/* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
|
|
2443
2504
|
] })
|
|
2444
2505
|
] });
|
|
2445
2506
|
}
|
|
2446
2507
|
|
|
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-
|
|
2449
|
-
//# sourceMappingURL=chunk-
|
|
2508
|
+
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, bottomNavReachesAll, useBackHandler, useBackStyle, useCrossModuleBack, useModuleHeaderActionsSlot, useModuleHeaderSlot, useModuleLayout, useNavTransition, useRailRow, useSaveBar, useSaveBarVisible };
|
|
2509
|
+
//# sourceMappingURL=chunk-PZU7XC2M.js.map
|
|
2510
|
+
//# sourceMappingURL=chunk-PZU7XC2M.js.map
|