@fayz-ai/ui 0.15.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/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-5KMAEQA3.js → chunk-5VF45NLE.js} +4 -4
- package/dist/{chunk-5KMAEQA3.js.map → chunk-5VF45NLE.js.map} +1 -1
- package/dist/{chunk-J7F3Z52K.js → chunk-6BJTH3L6.js} +4 -4
- package/dist/chunk-6BJTH3L6.js.map +1 -0
- package/dist/{chunk-KD2V3TAX.js → chunk-E44WOPCR.js} +61 -49
- package/dist/chunk-E44WOPCR.js.map +1 -0
- package/dist/{chunk-FFJ2C5YH.js → chunk-HKGTF2YA.js} +41 -16
- package/dist/chunk-HKGTF2YA.js.map +1 -0
- package/dist/{chunk-NA3GE4H3.js → chunk-PZU7XC2M.js} +362 -252
- package/dist/chunk-PZU7XC2M.js.map +1 -0
- package/dist/{chunk-SLWTKHNY.js → chunk-SDPYDWWV.js} +371 -83
- package/dist/chunk-SDPYDWWV.js.map +1 -0
- package/dist/dashboard/index.js +3 -3
- package/dist/hooks/useIsWide.d.ts +16 -0
- package/dist/hooks/useIsWide.d.ts.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -10
- package/dist/index.js.map +1 -1
- package/dist/layout/AppShell.d.ts +25 -2
- 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.map +1 -1
- package/dist/layout/Topbar.d.ts +5 -1
- 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 +3 -3
- package/dist/primitives/checkbox.d.ts +4 -0
- package/dist/primitives/checkbox.d.ts.map +1 -1
- package/dist/primitives/index.d.ts +3 -0
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +4 -4
- package/dist/primitives/list-view.d.ts +5 -1
- package/dist/primitives/list-view.d.ts.map +1 -1
- package/dist/primitives/modal.d.ts.map +1 -1
- 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.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/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 +166 -3
- package/package.json +2 -2
- package/dist/chunk-FFJ2C5YH.js.map +0 -1
- package/dist/chunk-J7F3Z52K.js.map +0 -1
- package/dist/chunk-KD2V3TAX.js.map +0 -1
- package/dist/chunk-NA3GE4H3.js.map +0 -1
- package/dist/chunk-SLWTKHNY.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useHasPageHeaderHost, usePageHeader, useLayoutStore, useRegisterPageActions,
|
|
1
|
+
import { useHasPageHeaderHost, usePageHeader, PageTitlePortal, useLayoutStore, useHasPageHeaderBack, useRegisterPageActions, usePageHeaderClaimed, usePageTitleSlotHost, useHasPageHeaderOutlet, useHasPageActions, usePageHeaderHost } from './chunk-HKGTF2YA.js';
|
|
2
2
|
import { cn } from './chunk-OKZ6WQB3.js';
|
|
3
3
|
import { ICON_MAP } from './chunk-4GK6IHOI.js';
|
|
4
|
-
import * as
|
|
4
|
+
import * as React2 from 'react';
|
|
5
5
|
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
6
|
-
import { X, Loader2, ArrowLeft, Search, Bell, PanelLeft, PanelLeftClose, Menu, Crown, ChevronDown, ChevronRight, ChevronLeft, ExternalLink, CreditCard, Sparkles, Home, Plus,
|
|
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';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
import { useTranslation, useNavReferrer, routeModule, navHistoryBack } from '@fayz-ai/core';
|
|
9
9
|
import * as Popover from '@radix-ui/react-popover';
|
|
@@ -29,19 +29,19 @@ var Sheet = DialogPrimitive.Root;
|
|
|
29
29
|
var SheetTrigger = DialogPrimitive.Trigger;
|
|
30
30
|
var SheetClose = DialogPrimitive.Close;
|
|
31
31
|
var SheetPortal = DialogPrimitive.Portal;
|
|
32
|
-
var SheetOverlay =
|
|
32
|
+
var SheetOverlay = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
33
33
|
DialogPrimitive.Overlay,
|
|
34
34
|
{
|
|
35
35
|
ref,
|
|
36
36
|
className: cn(
|
|
37
|
-
"saas-sheet-overlay fixed inset-0 z-
|
|
37
|
+
"saas-sheet-overlay fixed inset-0 z-[100] bg-black/40 backdrop-blur-[2px]",
|
|
38
38
|
className
|
|
39
39
|
),
|
|
40
40
|
...props
|
|
41
41
|
}
|
|
42
42
|
));
|
|
43
43
|
SheetOverlay.displayName = "SheetOverlay";
|
|
44
|
-
var SheetContent =
|
|
44
|
+
var SheetContent = React2.forwardRef(({ className, children, width, hideClose, overlay = "blur", onPointerDownOutside, onInteractOutside, ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [
|
|
45
45
|
/* @__PURE__ */ jsx(SheetOverlay, { className: overlay === "none" ? "bg-transparent backdrop-blur-0" : overlay === "dim" ? "backdrop-blur-0" : void 0 }),
|
|
46
46
|
/* @__PURE__ */ jsxs(
|
|
47
47
|
DialogPrimitive.Content,
|
|
@@ -62,7 +62,7 @@ var SheetContent = React7.forwardRef(({ className, children, width, hideClose, o
|
|
|
62
62
|
onInteractOutside?.(e);
|
|
63
63
|
},
|
|
64
64
|
className: cn(
|
|
65
|
-
"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",
|
|
66
66
|
"w-full",
|
|
67
67
|
width ?? "max-w-md",
|
|
68
68
|
className
|
|
@@ -99,7 +99,7 @@ function SheetFooter({ className, ...props }) {
|
|
|
99
99
|
);
|
|
100
100
|
}
|
|
101
101
|
SheetFooter.displayName = "SheetFooter";
|
|
102
|
-
var SheetTitle =
|
|
102
|
+
var SheetTitle = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
103
103
|
DialogPrimitive.Title,
|
|
104
104
|
{
|
|
105
105
|
ref,
|
|
@@ -108,7 +108,7 @@ var SheetTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE_
|
|
|
108
108
|
}
|
|
109
109
|
));
|
|
110
110
|
SheetTitle.displayName = "SheetTitle";
|
|
111
|
-
var SheetDescription =
|
|
111
|
+
var SheetDescription = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
112
112
|
DialogPrimitive.Description,
|
|
113
113
|
{
|
|
114
114
|
ref,
|
|
@@ -127,8 +127,8 @@ function SheetBody({ className, ...props }) {
|
|
|
127
127
|
);
|
|
128
128
|
}
|
|
129
129
|
SheetBody.displayName = "SheetBody";
|
|
130
|
-
var SaveBarApiContext =
|
|
131
|
-
var SaveBarStateContext =
|
|
130
|
+
var SaveBarApiContext = React2.createContext(null);
|
|
131
|
+
var SaveBarStateContext = React2.createContext({ visible: false, saving: false });
|
|
132
132
|
var _saveBarVisible = false;
|
|
133
133
|
var _saveBarSubs = /* @__PURE__ */ new Set();
|
|
134
134
|
function setSaveBarVisibleSignal(v) {
|
|
@@ -137,7 +137,7 @@ function setSaveBarVisibleSignal(v) {
|
|
|
137
137
|
_saveBarSubs.forEach((fn) => fn());
|
|
138
138
|
}
|
|
139
139
|
function useSaveBarVisible() {
|
|
140
|
-
return
|
|
140
|
+
return React2.useSyncExternalStore(
|
|
141
141
|
(cb) => {
|
|
142
142
|
_saveBarSubs.add(cb);
|
|
143
143
|
return () => {
|
|
@@ -149,15 +149,15 @@ function useSaveBarVisible() {
|
|
|
149
149
|
);
|
|
150
150
|
}
|
|
151
151
|
function SaveBarProvider({ children }) {
|
|
152
|
-
const [state, setState] =
|
|
153
|
-
const handlers =
|
|
154
|
-
const api =
|
|
155
|
-
const backStack =
|
|
156
|
-
const backApi =
|
|
157
|
-
|
|
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(() => {
|
|
158
158
|
setSaveBarVisibleSignal(state.visible);
|
|
159
159
|
}, [state.visible]);
|
|
160
|
-
|
|
160
|
+
React2.useEffect(() => {
|
|
161
161
|
function onKey(e) {
|
|
162
162
|
if (state.visible && !state.saving) {
|
|
163
163
|
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
@@ -185,7 +185,7 @@ function SaveBarProvider({ children }) {
|
|
|
185
185
|
}, [state.visible, state.saving]);
|
|
186
186
|
return /* @__PURE__ */ jsx(SaveBarApiContext.Provider, { value: api, children: /* @__PURE__ */ jsx(BackNavApiContext.Provider, { value: backApi, children: /* @__PURE__ */ jsx(SaveBarStateContext.Provider, { value: state, children }) }) });
|
|
187
187
|
}
|
|
188
|
-
var BackNavApiContext =
|
|
188
|
+
var BackNavApiContext = React2.createContext(null);
|
|
189
189
|
function isEditableTarget(target) {
|
|
190
190
|
const el = target;
|
|
191
191
|
if (!el || !el.tagName) return false;
|
|
@@ -193,12 +193,12 @@ function isEditableTarget(target) {
|
|
|
193
193
|
return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
|
|
194
194
|
}
|
|
195
195
|
function useBackHandler(onBack) {
|
|
196
|
-
const api =
|
|
197
|
-
const ref =
|
|
198
|
-
|
|
196
|
+
const api = React2.useContext(BackNavApiContext);
|
|
197
|
+
const ref = React2.useRef(onBack);
|
|
198
|
+
React2.useEffect(() => {
|
|
199
199
|
ref.current = onBack;
|
|
200
200
|
});
|
|
201
|
-
|
|
201
|
+
React2.useEffect(() => {
|
|
202
202
|
if (!api) return;
|
|
203
203
|
const stack = api.stack.current;
|
|
204
204
|
stack.push(ref);
|
|
@@ -209,23 +209,23 @@ function useBackHandler(onBack) {
|
|
|
209
209
|
}, [api]);
|
|
210
210
|
}
|
|
211
211
|
function useSaveBar({ dirty, saving = false, onSave, onDiscard, message, saveLabel, discardLabel }) {
|
|
212
|
-
const api =
|
|
213
|
-
|
|
212
|
+
const api = React2.useContext(SaveBarApiContext);
|
|
213
|
+
React2.useEffect(() => {
|
|
214
214
|
if (api) api.handlers.current = { onSave, onDiscard };
|
|
215
215
|
});
|
|
216
|
-
|
|
216
|
+
React2.useEffect(() => {
|
|
217
217
|
if (!api) return;
|
|
218
218
|
api.setState({ visible: dirty, saving, message, saveLabel, discardLabel });
|
|
219
219
|
return () => api.setState((s) => ({ ...s, visible: false }));
|
|
220
220
|
}, [api, dirty, saving, message, saveLabel, discardLabel]);
|
|
221
221
|
}
|
|
222
222
|
function SaveBar({ className }) {
|
|
223
|
-
const state =
|
|
224
|
-
const api =
|
|
223
|
+
const state = React2.useContext(SaveBarStateContext);
|
|
224
|
+
const api = React2.useContext(SaveBarApiContext);
|
|
225
225
|
const t = useTranslation();
|
|
226
|
-
const [rendered, setRendered] =
|
|
227
|
-
const [closing, setClosing] =
|
|
228
|
-
|
|
226
|
+
const [rendered, setRendered] = React2.useState(state.visible);
|
|
227
|
+
const [closing, setClosing] = React2.useState(false);
|
|
228
|
+
React2.useEffect(() => {
|
|
229
229
|
if (state.visible) {
|
|
230
230
|
setRendered(true);
|
|
231
231
|
setClosing(false);
|
|
@@ -241,40 +241,52 @@ function SaveBar({ className }) {
|
|
|
241
241
|
}, [state.visible, rendered]);
|
|
242
242
|
if (!api || !rendered) return null;
|
|
243
243
|
const { saving, message, saveLabel, discardLabel } = state;
|
|
244
|
-
return /* @__PURE__ */ jsx(
|
|
245
|
-
"
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
/* @__PURE__ */ jsx(
|
|
254
|
-
"button",
|
|
255
|
-
{
|
|
256
|
-
type: "button",
|
|
257
|
-
disabled: saving,
|
|
258
|
-
onClick: () => api.handlers.current.onDiscard?.(),
|
|
259
|
-
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",
|
|
260
|
-
children: discardLabel ?? t("common.discard")
|
|
261
|
-
}
|
|
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
|
|
262
253
|
),
|
|
263
|
-
/* @__PURE__ */ jsxs(
|
|
264
|
-
"
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
+
);
|
|
278
290
|
}
|
|
279
291
|
function useCrossModuleBack(onBack, parentLabel) {
|
|
280
292
|
const currentPath = typeof window !== "undefined" ? (window.location.hash ? window.location.hash.slice(1) : window.location.pathname) || "/" : "/";
|
|
@@ -289,10 +301,14 @@ function useCrossModuleBack(onBack, parentLabel) {
|
|
|
289
301
|
function PageHeaderCrumb({
|
|
290
302
|
title,
|
|
291
303
|
parentLabel,
|
|
292
|
-
onBack
|
|
304
|
+
onBack,
|
|
305
|
+
headingLevel = "h2"
|
|
293
306
|
}) {
|
|
307
|
+
const Heading = headingLevel;
|
|
308
|
+
const asHeading = typeof title === "string" && title !== "";
|
|
294
309
|
if (!onBack) {
|
|
295
|
-
|
|
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 });
|
|
296
312
|
}
|
|
297
313
|
return /* @__PURE__ */ jsxs("nav", { className: "flex min-w-0 items-center gap-1.5 text-sm text-muted-foreground", children: [
|
|
298
314
|
/* @__PURE__ */ jsxs(
|
|
@@ -313,6 +329,11 @@ function PageHeaderCrumb({
|
|
|
313
329
|
// Title still waiting on data: a pulsing bar, otherwise the line
|
|
314
330
|
// appears from nowhere and shoves the rest when the record lands.
|
|
315
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 })
|
|
316
337
|
) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-foreground", children: title })
|
|
317
338
|
] })
|
|
318
339
|
] });
|
|
@@ -321,7 +342,7 @@ function PageHeader({ title, onBack, parentLabel, backLabel }) {
|
|
|
321
342
|
const hasHost = useHasPageHeaderHost();
|
|
322
343
|
const { onBack: effectiveOnBack, parentLabel: effectiveParent } = useCrossModuleBack(onBack, parentLabel);
|
|
323
344
|
useBackHandler(effectiveOnBack);
|
|
324
|
-
const node =
|
|
345
|
+
const node = React2.useMemo(() => /* @__PURE__ */ jsx(
|
|
325
346
|
PageHeaderCrumb,
|
|
326
347
|
{
|
|
327
348
|
title,
|
|
@@ -329,21 +350,21 @@ function PageHeader({ title, onBack, parentLabel, backLabel }) {
|
|
|
329
350
|
onBack: effectiveOnBack
|
|
330
351
|
}
|
|
331
352
|
), [effectiveOnBack, effectiveParent, backLabel, title]);
|
|
332
|
-
usePageHeader(hasHost
|
|
333
|
-
if (hasHost) return
|
|
353
|
+
usePageHeader(hasHost, { back: !!effectiveOnBack });
|
|
354
|
+
if (hasHost) return /* @__PURE__ */ jsx(PageTitlePortal, { children: node });
|
|
334
355
|
return /* @__PURE__ */ jsx("div", { className: "mb-4", children: node });
|
|
335
356
|
}
|
|
336
357
|
var GUTTER_PX = 8;
|
|
337
358
|
var CLOSE_DELAY_MS = 140;
|
|
338
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ?
|
|
359
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React2.useLayoutEffect : React2.useEffect;
|
|
339
360
|
function iconOf(name) {
|
|
340
361
|
if (!name) return null;
|
|
341
362
|
return ICON_MAP[name] ?? null;
|
|
342
363
|
}
|
|
343
|
-
var RailFlyoutContext =
|
|
364
|
+
var RailFlyoutContext = React2.createContext(null);
|
|
344
365
|
var RailFlyoutProvider = RailFlyoutContext.Provider;
|
|
345
366
|
function useRailRow(content) {
|
|
346
|
-
const flyout =
|
|
367
|
+
const flyout = React2.useContext(RailFlyoutContext);
|
|
347
368
|
if (!flyout || !content) return {};
|
|
348
369
|
return flyout.rowProps(content);
|
|
349
370
|
}
|
|
@@ -353,19 +374,19 @@ function useRailFlyout({
|
|
|
353
374
|
activeRoute,
|
|
354
375
|
onNavigate
|
|
355
376
|
}) {
|
|
356
|
-
const [state, setState] =
|
|
357
|
-
const [pos, setPos] =
|
|
358
|
-
const [animate, setAnimate] =
|
|
359
|
-
const panelRef =
|
|
360
|
-
const closeTimer =
|
|
361
|
-
const lastContent =
|
|
362
|
-
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(() => {
|
|
363
384
|
if (closeTimer.current) {
|
|
364
385
|
clearTimeout(closeTimer.current);
|
|
365
386
|
closeTimer.current = null;
|
|
366
387
|
}
|
|
367
388
|
}, []);
|
|
368
|
-
const show =
|
|
389
|
+
const show = React2.useCallback(
|
|
369
390
|
(anchor, content2) => {
|
|
370
391
|
if (!enabled) return;
|
|
371
392
|
cancelClose();
|
|
@@ -373,19 +394,19 @@ function useRailFlyout({
|
|
|
373
394
|
},
|
|
374
395
|
[enabled, cancelClose]
|
|
375
396
|
);
|
|
376
|
-
const hide =
|
|
397
|
+
const hide = React2.useCallback(() => {
|
|
377
398
|
cancelClose();
|
|
378
399
|
closeTimer.current = setTimeout(() => setState(null), CLOSE_DELAY_MS);
|
|
379
400
|
}, [cancelClose]);
|
|
380
|
-
const dismiss =
|
|
401
|
+
const dismiss = React2.useCallback(() => {
|
|
381
402
|
cancelClose();
|
|
382
403
|
setState(null);
|
|
383
404
|
}, [cancelClose]);
|
|
384
|
-
|
|
385
|
-
|
|
405
|
+
React2.useEffect(() => () => cancelClose(), [cancelClose]);
|
|
406
|
+
React2.useEffect(() => {
|
|
386
407
|
if (!enabled) dismiss();
|
|
387
408
|
}, [enabled, dismiss]);
|
|
388
|
-
const reposition =
|
|
409
|
+
const reposition = React2.useCallback(() => {
|
|
389
410
|
const rail = railRef.current;
|
|
390
411
|
const anchor = state?.anchor;
|
|
391
412
|
if (!rail || !anchor) return;
|
|
@@ -400,7 +421,7 @@ function useRailFlyout({
|
|
|
400
421
|
setPos({ top, arrow });
|
|
401
422
|
}, [railRef, state]);
|
|
402
423
|
useIsomorphicLayoutEffect(reposition, [reposition, state?.content.key]);
|
|
403
|
-
|
|
424
|
+
React2.useEffect(() => {
|
|
404
425
|
if (!state) return;
|
|
405
426
|
const rail = railRef.current;
|
|
406
427
|
const onScroll = () => reposition();
|
|
@@ -412,7 +433,7 @@ function useRailFlyout({
|
|
|
412
433
|
};
|
|
413
434
|
}, [state, railRef, reposition]);
|
|
414
435
|
const visible = !!state;
|
|
415
|
-
|
|
436
|
+
React2.useEffect(() => {
|
|
416
437
|
if (!visible) {
|
|
417
438
|
setAnimate(false);
|
|
418
439
|
return;
|
|
@@ -420,7 +441,7 @@ function useRailFlyout({
|
|
|
420
441
|
const id = requestAnimationFrame(() => setAnimate(true));
|
|
421
442
|
return () => cancelAnimationFrame(id);
|
|
422
443
|
}, [visible]);
|
|
423
|
-
|
|
444
|
+
React2.useEffect(() => {
|
|
424
445
|
if (!visible) return;
|
|
425
446
|
const onKey = (event) => {
|
|
426
447
|
if (event.key === "Escape") dismiss();
|
|
@@ -428,7 +449,7 @@ function useRailFlyout({
|
|
|
428
449
|
window.addEventListener("keydown", onKey);
|
|
429
450
|
return () => window.removeEventListener("keydown", onKey);
|
|
430
451
|
}, [visible, dismiss]);
|
|
431
|
-
const rowProps =
|
|
452
|
+
const rowProps = React2.useCallback(
|
|
432
453
|
(content2) => ({
|
|
433
454
|
onMouseEnter: (event) => show(event.currentTarget, content2),
|
|
434
455
|
onFocus: (event) => show(event.currentTarget, content2),
|
|
@@ -545,7 +566,7 @@ function FlyoutBody({
|
|
|
545
566
|
] });
|
|
546
567
|
}
|
|
547
568
|
var RAIL_PUSH_MS = 260;
|
|
548
|
-
var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ?
|
|
569
|
+
var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React2.useLayoutEffect : React2.useEffect;
|
|
549
570
|
function getIcon(name) {
|
|
550
571
|
return ICON_MAP[name] ?? Home;
|
|
551
572
|
}
|
|
@@ -645,7 +666,7 @@ function NavGroup({
|
|
|
645
666
|
flyout
|
|
646
667
|
}) {
|
|
647
668
|
const isChildActive = item.children?.some((c) => c.route === activeRoute);
|
|
648
|
-
const [open, setOpen] =
|
|
669
|
+
const [open, setOpen] = React2.useState(() => isChildActive ?? false);
|
|
649
670
|
const Icon = getIcon(item.icon);
|
|
650
671
|
if (collapsed) {
|
|
651
672
|
const hoverProps = flyout?.rowProps(flyoutContentOf(item));
|
|
@@ -719,7 +740,7 @@ function NavGroup({
|
|
|
719
740
|
child.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 shrink-0 text-amber-500", "aria-hidden": true })
|
|
720
741
|
] });
|
|
721
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 });
|
|
722
|
-
if (!childConnector) return /* @__PURE__ */ jsx(
|
|
743
|
+
if (!childConnector) return /* @__PURE__ */ jsx(React2.Fragment, { children: row }, child.id);
|
|
723
744
|
const activeIndex = item.children.findIndex((c) => c.route === activeRoute);
|
|
724
745
|
const onPath = activeIndex >= 0 && childIndex < activeIndex;
|
|
725
746
|
return (
|
|
@@ -781,9 +802,9 @@ function NavGroup({
|
|
|
781
802
|
] });
|
|
782
803
|
}
|
|
783
804
|
function withCollapsed(slot, collapsed) {
|
|
784
|
-
if (!
|
|
805
|
+
if (!React2.isValidElement(slot)) return slot;
|
|
785
806
|
if (typeof slot.type === "string") return slot;
|
|
786
|
-
return
|
|
807
|
+
return React2.cloneElement(slot, { collapsed });
|
|
787
808
|
}
|
|
788
809
|
function Sidebar({
|
|
789
810
|
navigation,
|
|
@@ -812,7 +833,7 @@ function Sidebar({
|
|
|
812
833
|
const currentPath = currentPathProp;
|
|
813
834
|
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
814
835
|
const activeRoute = resolveActiveRoute(navigation, currentPath);
|
|
815
|
-
const railRef =
|
|
836
|
+
const railRef = React2.useRef(null);
|
|
816
837
|
const flyout = useRailFlyout({
|
|
817
838
|
enabled: collapsed,
|
|
818
839
|
railRef,
|
|
@@ -861,10 +882,10 @@ function Sidebar({
|
|
|
861
882
|
mainItems.length > 0 && renderSection(mainItems, sectionLabels?.main),
|
|
862
883
|
secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6", children: renderSection(secondaryItems, sectionLabels?.secondary) })
|
|
863
884
|
] });
|
|
864
|
-
const [outgoing, setOutgoing] =
|
|
865
|
-
const lastBodyRef =
|
|
866
|
-
const lastKeyRef =
|
|
867
|
-
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);
|
|
868
889
|
useIsomorphicLayoutEffect2(() => {
|
|
869
890
|
if (lastKeyRef.current !== navKey) {
|
|
870
891
|
const previousBody = lastBodyRef.current;
|
|
@@ -877,7 +898,7 @@ function Sidebar({
|
|
|
877
898
|
}
|
|
878
899
|
lastBodyRef.current = navBody;
|
|
879
900
|
});
|
|
880
|
-
|
|
901
|
+
React2.useEffect(() => () => {
|
|
881
902
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
882
903
|
}, []);
|
|
883
904
|
return (
|
|
@@ -1022,34 +1043,41 @@ function Sidebar({
|
|
|
1022
1043
|
// it the padded/bordered wrapper shows as a blank block.
|
|
1023
1044
|
/* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
|
|
1024
1045
|
withCollapsed(orgSwitcher, collapsed),
|
|
1025
|
-
/* @__PURE__ */ jsx(
|
|
1026
|
-
"
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
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(
|
|
1069
|
+
"button",
|
|
1070
|
+
{
|
|
1071
|
+
onClick: onToggle,
|
|
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" })
|
|
1076
|
+
}
|
|
1077
|
+
)
|
|
1078
|
+
] })
|
|
1079
|
+
}
|
|
1080
|
+
),
|
|
1053
1081
|
flyout.panel
|
|
1054
1082
|
]
|
|
1055
1083
|
}
|
|
@@ -1066,7 +1094,7 @@ function TopbarUserMenu({
|
|
|
1066
1094
|
onBilling,
|
|
1067
1095
|
userMenuExtras
|
|
1068
1096
|
}) {
|
|
1069
|
-
const [open, setOpen] =
|
|
1097
|
+
const [open, setOpen] = React2.useState(false);
|
|
1070
1098
|
if (!user) return null;
|
|
1071
1099
|
const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
|
|
1072
1100
|
const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
|
|
@@ -1150,7 +1178,7 @@ function getIcon2(name) {
|
|
|
1150
1178
|
return ICON_MAP[name] ?? Home;
|
|
1151
1179
|
}
|
|
1152
1180
|
function NavDropdown({ item, currentPath, onNavigate }) {
|
|
1153
|
-
const [open, setOpen] =
|
|
1181
|
+
const [open, setOpen] = React2.useState(false);
|
|
1154
1182
|
const Icon = getIcon2(item.icon);
|
|
1155
1183
|
const isChildActive = item.children?.some(
|
|
1156
1184
|
(child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
|
|
@@ -1208,6 +1236,7 @@ function Topbar({
|
|
|
1208
1236
|
logo,
|
|
1209
1237
|
user,
|
|
1210
1238
|
onMenuClick,
|
|
1239
|
+
hideMobileChrome,
|
|
1211
1240
|
orgSwitcher,
|
|
1212
1241
|
leftContent,
|
|
1213
1242
|
rightContent,
|
|
@@ -1231,7 +1260,7 @@ function Topbar({
|
|
|
1231
1260
|
onNavigate?.(route);
|
|
1232
1261
|
};
|
|
1233
1262
|
return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
|
|
1234
|
-
/* @__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: [
|
|
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: [
|
|
1235
1264
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
|
|
1236
1265
|
onMenuClick && /* @__PURE__ */ jsx(
|
|
1237
1266
|
"button",
|
|
@@ -1316,24 +1345,27 @@ function Topbar({
|
|
|
1316
1345
|
}) }) }) })
|
|
1317
1346
|
] });
|
|
1318
1347
|
}
|
|
1319
|
-
var ModuleHeaderSlotContext =
|
|
1348
|
+
var ModuleHeaderSlotContext = React2.createContext(null);
|
|
1320
1349
|
function useModuleHeaderSlot() {
|
|
1321
|
-
return
|
|
1350
|
+
return React2.useContext(ModuleHeaderSlotContext);
|
|
1322
1351
|
}
|
|
1323
|
-
var ModuleHeaderActionsSlotContext =
|
|
1352
|
+
var ModuleHeaderActionsSlotContext = React2.createContext(null);
|
|
1324
1353
|
function useModuleHeaderActionsSlot() {
|
|
1325
|
-
return
|
|
1354
|
+
return React2.useContext(ModuleHeaderActionsSlotContext);
|
|
1326
1355
|
}
|
|
1327
|
-
var PageRailSlotContext =
|
|
1356
|
+
var PageRailSlotContext = React2.createContext(null);
|
|
1328
1357
|
function usePageRailSlot() {
|
|
1329
|
-
return
|
|
1358
|
+
return React2.useContext(PageRailSlotContext);
|
|
1359
|
+
}
|
|
1360
|
+
function bottomNavReachesAll(items) {
|
|
1361
|
+
return !!items?.some((i) => "kind" in i && i.kind === "more");
|
|
1330
1362
|
}
|
|
1331
1363
|
function ShellPageTitle({
|
|
1332
1364
|
title,
|
|
1333
1365
|
back
|
|
1334
1366
|
}) {
|
|
1335
1367
|
if (!title && !back) return null;
|
|
1336
|
-
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" });
|
|
1337
1369
|
}
|
|
1338
1370
|
function SidebarLayout({
|
|
1339
1371
|
navigation = [],
|
|
@@ -1362,6 +1394,7 @@ function SidebarLayout({
|
|
|
1362
1394
|
unreadCount = 0,
|
|
1363
1395
|
searchLabel,
|
|
1364
1396
|
hasBottomNav = false,
|
|
1397
|
+
barOwnsNav = false,
|
|
1365
1398
|
mobileHeader = "minimal",
|
|
1366
1399
|
sidebarSectionLabels,
|
|
1367
1400
|
sidebarNavKey,
|
|
@@ -1370,16 +1403,19 @@ function SidebarLayout({
|
|
|
1370
1403
|
railOpen
|
|
1371
1404
|
}) {
|
|
1372
1405
|
const sidebarCollapsed = useLayoutStore((s) => s.sidebarCollapsed);
|
|
1373
|
-
const
|
|
1406
|
+
const pageHeaderClaimed = usePageHeaderClaimed();
|
|
1407
|
+
const setTitleSlot = usePageTitleSlotHost();
|
|
1374
1408
|
usePageHeaderHost();
|
|
1375
1409
|
const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
|
|
1376
|
-
const
|
|
1377
|
-
const
|
|
1378
|
-
const [
|
|
1379
|
-
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);
|
|
1380
1415
|
const hasPageActions = useHasPageActions();
|
|
1381
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(
|
|
1382
|
-
"shell-enter flex
|
|
1417
|
+
"shell-enter fz-viewport-h flex overflow-hidden bg-background",
|
|
1418
|
+
hasBottomNav && "fz-has-bottom-bar",
|
|
1383
1419
|
// Frame mode: the inset gap around the framed card takes the sidebar (menu)
|
|
1384
1420
|
// color, so the white card reads as a panel floating on the same tone as the
|
|
1385
1421
|
// left menu. The card's own md:m-2 margin reveals this behind it.
|
|
@@ -1437,7 +1473,7 @@ function SidebarLayout({
|
|
|
1437
1473
|
mobileHeader !== "minimal" && !hasPageActions && "hidden md:flex"
|
|
1438
1474
|
), children: [
|
|
1439
1475
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
|
|
1440
|
-
!
|
|
1476
|
+
!barOwnsNav && /* @__PURE__ */ jsx(
|
|
1441
1477
|
"button",
|
|
1442
1478
|
{
|
|
1443
1479
|
onClick: () => setMobileMenuOpen(true),
|
|
@@ -1446,7 +1482,8 @@ function SidebarLayout({
|
|
|
1446
1482
|
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
1447
1483
|
}
|
|
1448
1484
|
),
|
|
1449
|
-
|
|
1485
|
+
!pageHeaderClaimed && /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
|
|
1486
|
+
/* @__PURE__ */ jsx("div", { ref: setTitleSlot, className: "flex min-w-0 items-center empty:hidden" }),
|
|
1450
1487
|
topbarStart,
|
|
1451
1488
|
/* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
|
|
1452
1489
|
] }),
|
|
@@ -1499,6 +1536,7 @@ function TopbarLayout({
|
|
|
1499
1536
|
billingLabel,
|
|
1500
1537
|
userMenuExtras,
|
|
1501
1538
|
userMenuSlot,
|
|
1539
|
+
drawerUserMenuSlot,
|
|
1502
1540
|
orgSwitcher,
|
|
1503
1541
|
frame,
|
|
1504
1542
|
currentPath,
|
|
@@ -1507,19 +1545,23 @@ function TopbarLayout({
|
|
|
1507
1545
|
pageTitle,
|
|
1508
1546
|
pageBack,
|
|
1509
1547
|
notificationSlot,
|
|
1510
|
-
|
|
1548
|
+
unreadCount = 0,
|
|
1549
|
+
hasBottomNav = false,
|
|
1550
|
+
barOwnsNav = false
|
|
1511
1551
|
}) {
|
|
1512
|
-
const
|
|
1513
|
-
const
|
|
1514
|
-
const [
|
|
1515
|
-
const [
|
|
1516
|
-
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();
|
|
1517
1559
|
const hasOutlet = useHasPageHeaderOutlet();
|
|
1518
1560
|
const hasPageActions = useHasPageActions();
|
|
1519
1561
|
usePageHeaderHost();
|
|
1520
|
-
const
|
|
1562
|
+
const pageHeaderClaimed = hasOutlet ? false : rawClaimed;
|
|
1521
1563
|
const showTitle = !hasOutlet && !!pageTitle;
|
|
1522
|
-
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: [
|
|
1523
1565
|
/* @__PURE__ */ jsx(
|
|
1524
1566
|
Topbar,
|
|
1525
1567
|
{
|
|
@@ -1542,19 +1584,30 @@ function TopbarLayout({
|
|
|
1542
1584
|
topbarEnd,
|
|
1543
1585
|
notificationSlot
|
|
1544
1586
|
] }) : void 0,
|
|
1545
|
-
onMenuClick:
|
|
1587
|
+
onMenuClick: barOwnsNav ? void 0 : () => setMobileMenuOpen(true),
|
|
1588
|
+
hideMobileChrome: barOwnsNav
|
|
1546
1589
|
}
|
|
1547
1590
|
),
|
|
1548
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: [
|
|
1549
1592
|
/* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
|
|
1550
1593
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", children: [
|
|
1551
|
-
(
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
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
|
+
),
|
|
1558
1611
|
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", children })
|
|
1559
1612
|
] })
|
|
1560
1613
|
] }) }),
|
|
@@ -1572,7 +1625,9 @@ function TopbarLayout({
|
|
|
1572
1625
|
onSettings,
|
|
1573
1626
|
onBilling,
|
|
1574
1627
|
orgSwitcher,
|
|
1575
|
-
userMenuSlot: user ? /* @__PURE__ */ jsx(DrawerUserRow, { user }) : void 0,
|
|
1628
|
+
userMenuSlot: drawerUserMenuSlot ?? (user ? /* @__PURE__ */ jsx(DrawerUserRow, { user }) : void 0),
|
|
1629
|
+
notificationSlot,
|
|
1630
|
+
unreadCount,
|
|
1576
1631
|
onClose: () => setMobileMenuOpen(false)
|
|
1577
1632
|
}
|
|
1578
1633
|
)
|
|
@@ -1589,9 +1644,10 @@ function MinimalLayout({
|
|
|
1589
1644
|
logo,
|
|
1590
1645
|
user: _user,
|
|
1591
1646
|
children,
|
|
1592
|
-
headerEnd
|
|
1647
|
+
headerEnd,
|
|
1648
|
+
hasBottomNav = false
|
|
1593
1649
|
}) {
|
|
1594
|
-
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: [
|
|
1595
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: [
|
|
1596
1652
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) }),
|
|
1597
1653
|
headerEnd && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: headerEnd })
|
|
@@ -1600,23 +1656,23 @@ function MinimalLayout({
|
|
|
1600
1656
|
] });
|
|
1601
1657
|
}
|
|
1602
1658
|
function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
|
|
1603
|
-
const [visible, setVisible] =
|
|
1604
|
-
|
|
1659
|
+
const [visible, setVisible] = React2.useState(false);
|
|
1660
|
+
React2.useEffect(() => {
|
|
1605
1661
|
const id = requestAnimationFrame(() => setVisible(true));
|
|
1606
1662
|
return () => cancelAnimationFrame(id);
|
|
1607
1663
|
}, []);
|
|
1608
|
-
const close =
|
|
1664
|
+
const close = React2.useCallback(() => {
|
|
1609
1665
|
setVisible(false);
|
|
1610
1666
|
window.setTimeout(onClose, 250);
|
|
1611
1667
|
}, [onClose]);
|
|
1612
|
-
|
|
1668
|
+
React2.useEffect(() => {
|
|
1613
1669
|
const previous = document.body.style.overflow;
|
|
1614
1670
|
document.body.style.overflow = "hidden";
|
|
1615
1671
|
return () => {
|
|
1616
1672
|
document.body.style.overflow = previous;
|
|
1617
1673
|
};
|
|
1618
1674
|
}, []);
|
|
1619
|
-
|
|
1675
|
+
React2.useEffect(() => {
|
|
1620
1676
|
const onKey = (e) => {
|
|
1621
1677
|
if (e.key === "Escape") close();
|
|
1622
1678
|
};
|
|
@@ -1659,19 +1715,7 @@ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
|
|
|
1659
1715
|
)
|
|
1660
1716
|
] });
|
|
1661
1717
|
}
|
|
1662
|
-
var
|
|
1663
|
-
Home: Home,
|
|
1664
|
-
Receipt,
|
|
1665
|
-
ArrowLeftRight,
|
|
1666
|
-
Calendar,
|
|
1667
|
-
CreditCard: CreditCard,
|
|
1668
|
-
Settings: Settings,
|
|
1669
|
-
Menu: Menu,
|
|
1670
|
-
MessageCircle,
|
|
1671
|
-
Wallet,
|
|
1672
|
-
PiggyBank,
|
|
1673
|
-
Plus
|
|
1674
|
-
};
|
|
1718
|
+
var bottomNavIcon = (name, fallback = Home) => (name ? ICON_MAP[name] : void 0) ?? fallback;
|
|
1675
1719
|
function FloatingProfileAvatar({ user, onClick }) {
|
|
1676
1720
|
if (!user) return null;
|
|
1677
1721
|
const label = user.fullName || user.email;
|
|
@@ -1697,43 +1741,80 @@ function MobileBottomNav({
|
|
|
1697
1741
|
items,
|
|
1698
1742
|
currentPath,
|
|
1699
1743
|
onNavigate,
|
|
1700
|
-
onAction
|
|
1744
|
+
onAction,
|
|
1745
|
+
onMore,
|
|
1746
|
+
onAssistant,
|
|
1747
|
+
assistantOpen,
|
|
1748
|
+
moreOpen,
|
|
1749
|
+
retracted
|
|
1701
1750
|
}) {
|
|
1702
1751
|
const path = currentPath ?? "";
|
|
1703
|
-
return /* @__PURE__ */ jsx(
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
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);
|
|
1714
1775
|
}
|
|
1715
|
-
|
|
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
|
+
})
|
|
1716
1816
|
}
|
|
1717
|
-
|
|
1718
|
-
const Icon = BOTTOM_NAV_ICONS[item.icon] ?? Home;
|
|
1719
|
-
return /* @__PURE__ */ jsxs(
|
|
1720
|
-
"button",
|
|
1721
|
-
{
|
|
1722
|
-
type: "button",
|
|
1723
|
-
onClick: () => onNavigate?.(item.route),
|
|
1724
|
-
className: cn(
|
|
1725
|
-
"flex flex-1 flex-col items-center justify-center gap-0.5 px-1 py-1 transition-colors",
|
|
1726
|
-
isActive ? "text-primary" : "text-muted-foreground"
|
|
1727
|
-
),
|
|
1728
|
-
"aria-current": isActive ? "page" : void 0,
|
|
1729
|
-
children: [
|
|
1730
|
-
/* @__PURE__ */ jsx(Icon, { className: "h-5 w-5" }),
|
|
1731
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs", children: item.label })
|
|
1732
|
-
]
|
|
1733
|
-
},
|
|
1734
|
-
item.route
|
|
1735
|
-
);
|
|
1736
|
-
}) });
|
|
1817
|
+
);
|
|
1737
1818
|
}
|
|
1738
1819
|
function AppShell({
|
|
1739
1820
|
variant,
|
|
@@ -1762,8 +1843,11 @@ function AppShell({
|
|
|
1762
1843
|
sidebarFooterContent,
|
|
1763
1844
|
bottomNav,
|
|
1764
1845
|
onBottomNavAction,
|
|
1846
|
+
onAssistantToggle,
|
|
1847
|
+
assistantOpen,
|
|
1765
1848
|
mobileHeader = "minimal",
|
|
1766
1849
|
userMenuSlot,
|
|
1850
|
+
drawerUserMenuSlot,
|
|
1767
1851
|
notificationSlot,
|
|
1768
1852
|
unreadCount = 0,
|
|
1769
1853
|
searchLabel,
|
|
@@ -1780,13 +1864,26 @@ function AppShell({
|
|
|
1780
1864
|
rightRailResizeLabel
|
|
1781
1865
|
}) {
|
|
1782
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]);
|
|
1783
1875
|
const bottomBar = hasBottomNav ? /* @__PURE__ */ jsx(
|
|
1784
1876
|
MobileBottomNav,
|
|
1785
1877
|
{
|
|
1786
1878
|
items: bottomNav,
|
|
1787
1879
|
currentPath,
|
|
1788
1880
|
onNavigate,
|
|
1789
|
-
onAction: onBottomNavAction
|
|
1881
|
+
onAction: onBottomNavAction,
|
|
1882
|
+
onMore: () => setMobileDrawerOpen(!mobileDrawerOpen),
|
|
1883
|
+
moreOpen: mobileDrawerOpen,
|
|
1884
|
+
onAssistant: onAssistantToggle,
|
|
1885
|
+
assistantOpen,
|
|
1886
|
+
retracted: barRetracted
|
|
1790
1887
|
}
|
|
1791
1888
|
) : null;
|
|
1792
1889
|
const floatingAvatar = mobileHeader === "transparent" && user ? /* @__PURE__ */ jsx(FloatingProfileAvatar, { user, onClick: onProfile }) : null;
|
|
@@ -1820,7 +1917,8 @@ function AppShell({
|
|
|
1820
1917
|
notificationSlot,
|
|
1821
1918
|
unreadCount,
|
|
1822
1919
|
searchLabel,
|
|
1823
|
-
hasBottomNav,
|
|
1920
|
+
hasBottomNav: hasBottomNav && !barRetracted,
|
|
1921
|
+
barOwnsNav,
|
|
1824
1922
|
mobileHeader,
|
|
1825
1923
|
sidebarSectionLabels,
|
|
1826
1924
|
sidebarNavKey,
|
|
@@ -1847,6 +1945,7 @@ function AppShell({
|
|
|
1847
1945
|
billingLabel,
|
|
1848
1946
|
userMenuExtras,
|
|
1849
1947
|
userMenuSlot,
|
|
1948
|
+
drawerUserMenuSlot,
|
|
1850
1949
|
orgSwitcher,
|
|
1851
1950
|
frame: contentFrame,
|
|
1852
1951
|
currentPath,
|
|
@@ -1855,13 +1954,14 @@ function AppShell({
|
|
|
1855
1954
|
pageTitle,
|
|
1856
1955
|
pageBack,
|
|
1857
1956
|
notificationSlot,
|
|
1858
|
-
hasBottomNav,
|
|
1957
|
+
hasBottomNav: hasBottomNav && !barRetracted,
|
|
1958
|
+
barOwnsNav,
|
|
1859
1959
|
children
|
|
1860
1960
|
}
|
|
1861
1961
|
);
|
|
1862
1962
|
break;
|
|
1863
1963
|
case "minimal":
|
|
1864
|
-
layoutElement = /* @__PURE__ */ jsx(MinimalLayout, { logo, user, headerEnd: topbarEnd, children });
|
|
1964
|
+
layoutElement = /* @__PURE__ */ jsx(MinimalLayout, { logo, user, headerEnd: topbarEnd, hasBottomNav: hasBottomNav && !barRetracted, children });
|
|
1865
1965
|
break;
|
|
1866
1966
|
default: {
|
|
1867
1967
|
return null;
|
|
@@ -1897,13 +1997,13 @@ function SplitWithRightRail({
|
|
|
1897
1997
|
maxWidth = 720,
|
|
1898
1998
|
resizeLabel = "Resize panel"
|
|
1899
1999
|
}) {
|
|
1900
|
-
const [width, setWidth] =
|
|
1901
|
-
const [dragging, setDragging] =
|
|
1902
|
-
const clamp =
|
|
2000
|
+
const [width, setWidth] = React2.useState(defaultWidth);
|
|
2001
|
+
const [dragging, setDragging] = React2.useState(false);
|
|
2002
|
+
const clamp = React2.useCallback(
|
|
1903
2003
|
(value) => Math.min(maxWidth, Math.max(minWidth, value)),
|
|
1904
2004
|
[minWidth, maxWidth]
|
|
1905
2005
|
);
|
|
1906
|
-
|
|
2006
|
+
React2.useEffect(() => {
|
|
1907
2007
|
if (!dragging) return;
|
|
1908
2008
|
const onMove = (event) => {
|
|
1909
2009
|
event.preventDefault();
|
|
@@ -1925,7 +2025,7 @@ function SplitWithRightRail({
|
|
|
1925
2025
|
document.body.style.cursor = previousCursor;
|
|
1926
2026
|
};
|
|
1927
2027
|
}, [dragging, clamp]);
|
|
1928
|
-
|
|
2028
|
+
React2.useEffect(() => {
|
|
1929
2029
|
if (dragging) return;
|
|
1930
2030
|
onWidthChange?.(width);
|
|
1931
2031
|
}, [dragging]);
|
|
@@ -1935,7 +2035,7 @@ function SplitWithRightRail({
|
|
|
1935
2035
|
// chrome, next to the menu — painting them page-white put a bright strip
|
|
1936
2036
|
// between the dark chrome and the rail, which read as a seam rather than
|
|
1937
2037
|
// as space.
|
|
1938
|
-
/* @__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: [
|
|
1939
2039
|
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children }),
|
|
1940
2040
|
/* @__PURE__ */ jsxs(
|
|
1941
2041
|
"div",
|
|
@@ -2075,12 +2175,12 @@ function PageTabs({ actions, className, ...props }) {
|
|
|
2075
2175
|
}
|
|
2076
2176
|
);
|
|
2077
2177
|
}
|
|
2078
|
-
var NavTransitionContext =
|
|
2178
|
+
var NavTransitionContext = React2.createContext("slide");
|
|
2079
2179
|
function NavTransitionProvider({ value, children }) {
|
|
2080
2180
|
return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
|
|
2081
2181
|
}
|
|
2082
2182
|
function useNavTransition() {
|
|
2083
|
-
return
|
|
2183
|
+
return React2.useContext(NavTransitionContext);
|
|
2084
2184
|
}
|
|
2085
2185
|
function resolveTransitionClass(transition, direction) {
|
|
2086
2186
|
if (transition === "none") return "";
|
|
@@ -2115,15 +2215,15 @@ function PremiumBadge({ className }) {
|
|
|
2115
2215
|
const tip = t("upgrade.badgeTooltip");
|
|
2116
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 }) });
|
|
2117
2217
|
}
|
|
2118
|
-
var ModuleLayoutContext =
|
|
2218
|
+
var ModuleLayoutContext = React2.createContext("rail");
|
|
2119
2219
|
function ModuleLayoutProvider({ variant, children }) {
|
|
2120
2220
|
return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
|
|
2121
2221
|
}
|
|
2122
2222
|
function useModuleLayout() {
|
|
2123
|
-
return
|
|
2223
|
+
return React2.useContext(ModuleLayoutContext);
|
|
2124
2224
|
}
|
|
2125
2225
|
function RailNavItem({ item }) {
|
|
2126
|
-
const [expanded, setExpanded] =
|
|
2226
|
+
const [expanded, setExpanded] = React2.useState(true);
|
|
2127
2227
|
const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
|
|
2128
2228
|
const hasChildren = item.children && item.children.length > 0;
|
|
2129
2229
|
const hasActiveChild = hasChildren && item.children.some((c) => c.active);
|
|
@@ -2164,10 +2264,14 @@ function RailNavItem({ item }) {
|
|
|
2164
2264
|
)) })
|
|
2165
2265
|
] });
|
|
2166
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
|
+
}
|
|
2167
2271
|
function MobileTabs({ nav }) {
|
|
2168
|
-
const scrollRef =
|
|
2169
|
-
const [expandedId, setExpandedId] =
|
|
2170
|
-
|
|
2272
|
+
const scrollRef = React2.useRef(null);
|
|
2273
|
+
const [expandedId, setExpandedId] = React2.useState(null);
|
|
2274
|
+
React2.useEffect(() => {
|
|
2171
2275
|
const container = scrollRef.current;
|
|
2172
2276
|
if (!container) return;
|
|
2173
2277
|
const activeEl = container.querySelector('[data-active="true"]');
|
|
@@ -2189,6 +2293,7 @@ function MobileTabs({ nav }) {
|
|
|
2189
2293
|
"data-active": isActive || void 0,
|
|
2190
2294
|
onClick: () => {
|
|
2191
2295
|
if (hasChildren) {
|
|
2296
|
+
if (!isExpanded) defaultChildOf(item)?.onClick?.();
|
|
2192
2297
|
setExpandedId(isExpanded ? null : item.id);
|
|
2193
2298
|
} else {
|
|
2194
2299
|
item.onClick?.();
|
|
@@ -2196,11 +2301,15 @@ function MobileTabs({ nav }) {
|
|
|
2196
2301
|
}
|
|
2197
2302
|
},
|
|
2198
2303
|
className: cn(
|
|
2199
|
-
|
|
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]",
|
|
2200
2309
|
isActive ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:text-foreground"
|
|
2201
2310
|
),
|
|
2202
2311
|
children: [
|
|
2203
|
-
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-3 w-3" }),
|
|
2312
|
+
Icon && /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
|
|
2204
2313
|
item.label,
|
|
2205
2314
|
item.premium && /* @__PURE__ */ jsx(PremiumBadge, {}),
|
|
2206
2315
|
hasChildren && /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-2.5 w-2.5 transition-transform", isExpanded && "rotate-180") })
|
|
@@ -2234,12 +2343,12 @@ function MobileTabs({ nav }) {
|
|
|
2234
2343
|
})()
|
|
2235
2344
|
] });
|
|
2236
2345
|
}
|
|
2237
|
-
var BackStyleContext =
|
|
2346
|
+
var BackStyleContext = React2.createContext("link");
|
|
2238
2347
|
function BackStyleProvider({ style, children }) {
|
|
2239
2348
|
return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
|
|
2240
2349
|
}
|
|
2241
2350
|
function useBackStyle() {
|
|
2242
|
-
return
|
|
2351
|
+
return React2.useContext(BackStyleContext);
|
|
2243
2352
|
}
|
|
2244
2353
|
function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle, inline }) {
|
|
2245
2354
|
const t = useTranslation();
|
|
@@ -2331,15 +2440,16 @@ function ShellSubpageHeader({
|
|
|
2331
2440
|
parentLabel,
|
|
2332
2441
|
actions
|
|
2333
2442
|
}) {
|
|
2334
|
-
usePageHeader(
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
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
|
+
] });
|
|
2343
2453
|
}
|
|
2344
2454
|
function ModuleHeader({ nav }) {
|
|
2345
2455
|
function defaultChild(item) {
|
|
@@ -2389,12 +2499,12 @@ function ModulePage({ title: _title, subtitle, nav, children, className, headerA
|
|
|
2389
2499
|
] }) }),
|
|
2390
2500
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2391
2501
|
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
2392
|
-
/* @__PURE__ */ jsx(MobileTabs, { nav }),
|
|
2502
|
+
!onSubpage && /* @__PURE__ */ jsx(MobileTabs, { nav }),
|
|
2393
2503
|
/* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
|
|
2394
2504
|
] })
|
|
2395
2505
|
] });
|
|
2396
2506
|
}
|
|
2397
2507
|
|
|
2398
|
-
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 };
|
|
2399
|
-
//# sourceMappingURL=chunk-
|
|
2400
|
-
//# 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
|