@fayz-ai/ui 0.12.0 → 0.15.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 +5 -1
- package/dist/analytics/AnalyticsControls.d.ts.map +1 -1
- package/dist/chunk-3OVZ5JJQ.js +540 -0
- package/dist/chunk-3OVZ5JJQ.js.map +1 -0
- package/dist/{chunk-22EVFMS3.js → chunk-4GK6IHOI.js} +17 -3
- package/dist/chunk-4GK6IHOI.js.map +1 -0
- package/dist/{chunk-7BMXN7MP.js → chunk-5KMAEQA3.js} +25 -24
- package/dist/chunk-5KMAEQA3.js.map +1 -0
- package/dist/chunk-AASANYXN.js +601 -0
- package/dist/chunk-AASANYXN.js.map +1 -0
- package/dist/chunk-FFJ2C5YH.js +92 -0
- package/dist/chunk-FFJ2C5YH.js.map +1 -0
- package/dist/chunk-J7F3Z52K.js +152 -0
- package/dist/chunk-J7F3Z52K.js.map +1 -0
- package/dist/{chunk-JGJNRNQW.js → chunk-KD2V3TAX.js} +290 -67
- package/dist/chunk-KD2V3TAX.js.map +1 -0
- package/dist/{chunk-ZDRHCKRK.js → chunk-NA3GE4H3.js} +746 -533
- package/dist/chunk-NA3GE4H3.js.map +1 -0
- package/dist/{chunk-7BHG3XGJ.js → chunk-SLWTKHNY.js} +296 -269
- package/dist/chunk-SLWTKHNY.js.map +1 -0
- package/dist/dashboard/ChartWidgetInner.d.ts.map +1 -1
- package/dist/dashboard/DashboardCanvas.d.ts.map +1 -1
- package/dist/dashboard/DashboardCustomizeMenu.d.ts.map +1 -1
- package/dist/dashboard/DashboardRange.d.ts +8 -2
- package/dist/dashboard/DashboardRange.d.ts.map +1 -1
- package/dist/dashboard/index.js +4 -4
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +86 -99
- package/dist/index.js.map +1 -1
- package/dist/layout/AppShell.d.ts +8 -2
- package/dist/layout/AppShell.d.ts.map +1 -1
- package/dist/layout/ModulePage.d.ts +24 -1
- package/dist/layout/ModulePage.d.ts.map +1 -1
- package/dist/layout/Sidebar.d.ts +27 -4
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Topbar.d.ts +10 -3
- package/dist/layout/Topbar.d.ts.map +1 -1
- package/dist/layout/index.d.ts +2 -1
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +4 -4
- package/dist/layout/rail-flyout.d.ts +10 -0
- package/dist/layout/rail-flyout.d.ts.map +1 -1
- package/dist/primitives/SearchCombobox.d.ts +9 -1
- package/dist/primitives/SearchCombobox.d.ts.map +1 -1
- package/dist/primitives/bulk-action-bar.d.ts +3 -1
- package/dist/primitives/bulk-action-bar.d.ts.map +1 -1
- package/dist/primitives/color-palette.d.ts +24 -0
- package/dist/primitives/color-palette.d.ts.map +1 -0
- package/dist/primitives/header-tabs.d.ts +47 -7
- 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 +9 -3
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/primitives/index.js +6 -3
- package/dist/primitives/list-view.d.ts +9 -1
- package/dist/primitives/list-view.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-toolbar.d.ts +80 -0
- package/dist/primitives/page-toolbar.d.ts.map +1 -0
- package/dist/primitives/search-select.d.ts +3 -1
- package/dist/primitives/search-select.d.ts.map +1 -1
- package/dist/primitives/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/tabs.d.ts.map +1 -1
- package/dist/primitives/use-row-selection.d.ts +50 -0
- package/dist/primitives/use-row-selection.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/page-header.store.d.ts +26 -2
- package/dist/stores/page-header.store.d.ts.map +1 -1
- package/dist/styles.css +74 -17
- 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 +14 -8
- package/dist/ChartWidgetInner-GDE5PBTR.js.map +0 -1
- package/dist/chunk-22EVFMS3.js.map +0 -1
- package/dist/chunk-7BHG3XGJ.js.map +0 -1
- package/dist/chunk-7BMXN7MP.js.map +0 -1
- package/dist/chunk-JGJNRNQW.js.map +0 -1
- package/dist/chunk-VAKWUUVC.js +0 -425
- package/dist/chunk-VAKWUUVC.js.map +0 -1
- package/dist/chunk-ZBQ772DU.js +0 -17
- package/dist/chunk-ZBQ772DU.js.map +0 -1
- package/dist/chunk-ZDRHCKRK.js.map +0 -1
|
@@ -1,40 +1,371 @@
|
|
|
1
|
-
import { useHasPageHeaderHost,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import * as
|
|
6
|
-
import {
|
|
7
|
-
import { jsxs, Fragment
|
|
1
|
+
import { useHasPageHeaderHost, usePageHeader, useLayoutStore, useRegisterPageActions, useHasPageHeaderBack, usePageHeaderSlot, useHasPageHeaderOutlet, useHasPageActions, usePageHeaderHost } from './chunk-FFJ2C5YH.js';
|
|
2
|
+
import { cn } from './chunk-OKZ6WQB3.js';
|
|
3
|
+
import { ICON_MAP } from './chunk-4GK6IHOI.js';
|
|
4
|
+
import * as React7 from 'react';
|
|
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, PiggyBank, Wallet, MessageCircle, Settings, Calendar, ArrowLeftRight, Receipt, UserCog, LogOut } from 'lucide-react';
|
|
7
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
+
import { useTranslation, useNavReferrer, routeModule, navHistoryBack } from '@fayz-ai/core';
|
|
8
9
|
import * as Popover from '@radix-ui/react-popover';
|
|
9
10
|
import { createPortal } from 'react-dom';
|
|
10
|
-
import { useTranslation } from '@fayz-ai/core';
|
|
11
11
|
|
|
12
|
+
var STYLE_ID = "saas-sheet-styles";
|
|
13
|
+
if (typeof document !== "undefined" && !document.getElementById(STYLE_ID)) {
|
|
14
|
+
const style = document.createElement("style");
|
|
15
|
+
style.id = STYLE_ID;
|
|
16
|
+
style.textContent = `
|
|
17
|
+
@keyframes saas-sheet-fade-in { from { opacity: 0 } to { opacity: 1 } }
|
|
18
|
+
@keyframes saas-sheet-fade-out { from { opacity: 1 } to { opacity: 0 } }
|
|
19
|
+
@keyframes saas-sheet-slide-in { from { transform: translateX(100%) } to { transform: translateX(0) } }
|
|
20
|
+
@keyframes saas-sheet-slide-out { from { transform: translateX(0) } to { transform: translateX(100%) } }
|
|
21
|
+
.saas-sheet-overlay[data-state="open"] { animation: saas-sheet-fade-in 200ms ease-out }
|
|
22
|
+
.saas-sheet-overlay[data-state="closed"] { animation: saas-sheet-fade-out 150ms ease-in }
|
|
23
|
+
.saas-sheet-content[data-state="open"] { animation: saas-sheet-slide-in 280ms cubic-bezier(0.16, 1, 0.3, 1) }
|
|
24
|
+
.saas-sheet-content[data-state="closed"] { animation: saas-sheet-slide-out 200ms ease-in }
|
|
25
|
+
`;
|
|
26
|
+
document.head.appendChild(style);
|
|
27
|
+
}
|
|
28
|
+
var Sheet = DialogPrimitive.Root;
|
|
29
|
+
var SheetTrigger = DialogPrimitive.Trigger;
|
|
30
|
+
var SheetClose = DialogPrimitive.Close;
|
|
31
|
+
var SheetPortal = DialogPrimitive.Portal;
|
|
32
|
+
var SheetOverlay = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
33
|
+
DialogPrimitive.Overlay,
|
|
34
|
+
{
|
|
35
|
+
ref,
|
|
36
|
+
className: cn(
|
|
37
|
+
"saas-sheet-overlay fixed inset-0 z-50 bg-black/40 backdrop-blur-[2px]",
|
|
38
|
+
className
|
|
39
|
+
),
|
|
40
|
+
...props
|
|
41
|
+
}
|
|
42
|
+
));
|
|
43
|
+
SheetOverlay.displayName = "SheetOverlay";
|
|
44
|
+
var SheetContent = React7.forwardRef(({ className, children, width, hideClose, overlay = "blur", onPointerDownOutside, onInteractOutside, ...props }, ref) => /* @__PURE__ */ jsxs(SheetPortal, { children: [
|
|
45
|
+
/* @__PURE__ */ jsx(SheetOverlay, { className: overlay === "none" ? "bg-transparent backdrop-blur-0" : overlay === "dim" ? "backdrop-blur-0" : void 0 }),
|
|
46
|
+
/* @__PURE__ */ jsxs(
|
|
47
|
+
DialogPrimitive.Content,
|
|
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
|
+
},
|
|
64
|
+
className: cn(
|
|
65
|
+
"fayz-glass-surface saas-sheet-content fixed inset-y-0 right-0 z-50 flex flex-col border-l bg-card shadow-2xl outline-none",
|
|
66
|
+
"w-full",
|
|
67
|
+
width ?? "max-w-md",
|
|
68
|
+
className
|
|
69
|
+
),
|
|
70
|
+
...props,
|
|
71
|
+
children: [
|
|
72
|
+
children,
|
|
73
|
+
!hideClose && /* @__PURE__ */ jsxs(DialogPrimitive.Close, { className: "absolute right-4 top-4 z-10 flex h-7 w-7 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring", children: [
|
|
74
|
+
/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
|
|
75
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
76
|
+
] })
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
] }));
|
|
81
|
+
SheetContent.displayName = "SheetContent";
|
|
82
|
+
function SheetHeader({ className, ...props }) {
|
|
83
|
+
return /* @__PURE__ */ jsx(
|
|
84
|
+
"div",
|
|
85
|
+
{
|
|
86
|
+
className: cn("flex flex-col gap-1 px-5 py-4 border-b shrink-0", className),
|
|
87
|
+
...props
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
SheetHeader.displayName = "SheetHeader";
|
|
92
|
+
function SheetFooter({ className, ...props }) {
|
|
93
|
+
return /* @__PURE__ */ jsx(
|
|
94
|
+
"div",
|
|
95
|
+
{
|
|
96
|
+
className: cn("flex items-center gap-2 px-5 py-3 border-t shrink-0", className),
|
|
97
|
+
...props
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
SheetFooter.displayName = "SheetFooter";
|
|
102
|
+
var SheetTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
103
|
+
DialogPrimitive.Title,
|
|
104
|
+
{
|
|
105
|
+
ref,
|
|
106
|
+
className: cn("text-sm font-semibold leading-none tracking-tight", className),
|
|
107
|
+
...props
|
|
108
|
+
}
|
|
109
|
+
));
|
|
110
|
+
SheetTitle.displayName = "SheetTitle";
|
|
111
|
+
var SheetDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
112
|
+
DialogPrimitive.Description,
|
|
113
|
+
{
|
|
114
|
+
ref,
|
|
115
|
+
className: cn("text-xs text-muted-foreground", className),
|
|
116
|
+
...props
|
|
117
|
+
}
|
|
118
|
+
));
|
|
119
|
+
SheetDescription.displayName = "SheetDescription";
|
|
120
|
+
function SheetBody({ className, ...props }) {
|
|
121
|
+
return /* @__PURE__ */ jsx(
|
|
122
|
+
"div",
|
|
123
|
+
{
|
|
124
|
+
className: cn("flex-1 overflow-y-auto px-5 py-5", className),
|
|
125
|
+
...props
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
SheetBody.displayName = "SheetBody";
|
|
130
|
+
var SaveBarApiContext = React7.createContext(null);
|
|
131
|
+
var SaveBarStateContext = React7.createContext({ visible: false, saving: false });
|
|
132
|
+
var _saveBarVisible = false;
|
|
133
|
+
var _saveBarSubs = /* @__PURE__ */ new Set();
|
|
134
|
+
function setSaveBarVisibleSignal(v) {
|
|
135
|
+
if (v === _saveBarVisible) return;
|
|
136
|
+
_saveBarVisible = v;
|
|
137
|
+
_saveBarSubs.forEach((fn) => fn());
|
|
138
|
+
}
|
|
139
|
+
function useSaveBarVisible() {
|
|
140
|
+
return React7.useSyncExternalStore(
|
|
141
|
+
(cb) => {
|
|
142
|
+
_saveBarSubs.add(cb);
|
|
143
|
+
return () => {
|
|
144
|
+
_saveBarSubs.delete(cb);
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
() => _saveBarVisible,
|
|
148
|
+
() => false
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
function SaveBarProvider({ children }) {
|
|
152
|
+
const [state, setState] = React7.useState({ visible: false, saving: false });
|
|
153
|
+
const handlers = React7.useRef({});
|
|
154
|
+
const api = React7.useMemo(() => ({ setState, handlers }), []);
|
|
155
|
+
const backStack = React7.useRef([]);
|
|
156
|
+
const backApi = React7.useMemo(() => ({ stack: backStack }), []);
|
|
157
|
+
React7.useEffect(() => {
|
|
158
|
+
setSaveBarVisibleSignal(state.visible);
|
|
159
|
+
}, [state.visible]);
|
|
160
|
+
React7.useEffect(() => {
|
|
161
|
+
function onKey(e) {
|
|
162
|
+
if (state.visible && !state.saving) {
|
|
163
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
handlers.current.onSave?.();
|
|
166
|
+
} else if (e.key === "Escape") {
|
|
167
|
+
e.preventDefault();
|
|
168
|
+
handlers.current.onDiscard?.();
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (e.key === "Escape" && !e.defaultPrevented && !isEditableTarget(e.target)) {
|
|
173
|
+
for (let i = backStack.current.length - 1; i >= 0; i--) {
|
|
174
|
+
const back = backStack.current[i].current;
|
|
175
|
+
if (back) {
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
back();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
window.addEventListener("keydown", onKey);
|
|
184
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
185
|
+
}, [state.visible, state.saving]);
|
|
186
|
+
return /* @__PURE__ */ jsx(SaveBarApiContext.Provider, { value: api, children: /* @__PURE__ */ jsx(BackNavApiContext.Provider, { value: backApi, children: /* @__PURE__ */ jsx(SaveBarStateContext.Provider, { value: state, children }) }) });
|
|
187
|
+
}
|
|
188
|
+
var BackNavApiContext = React7.createContext(null);
|
|
189
|
+
function isEditableTarget(target) {
|
|
190
|
+
const el = target;
|
|
191
|
+
if (!el || !el.tagName) return false;
|
|
192
|
+
const tag = el.tagName.toLowerCase();
|
|
193
|
+
return tag === "input" || tag === "textarea" || tag === "select" || el.isContentEditable;
|
|
194
|
+
}
|
|
195
|
+
function useBackHandler(onBack) {
|
|
196
|
+
const api = React7.useContext(BackNavApiContext);
|
|
197
|
+
const ref = React7.useRef(onBack);
|
|
198
|
+
React7.useEffect(() => {
|
|
199
|
+
ref.current = onBack;
|
|
200
|
+
});
|
|
201
|
+
React7.useEffect(() => {
|
|
202
|
+
if (!api) return;
|
|
203
|
+
const stack = api.stack.current;
|
|
204
|
+
stack.push(ref);
|
|
205
|
+
return () => {
|
|
206
|
+
const i = stack.indexOf(ref);
|
|
207
|
+
if (i !== -1) stack.splice(i, 1);
|
|
208
|
+
};
|
|
209
|
+
}, [api]);
|
|
210
|
+
}
|
|
211
|
+
function useSaveBar({ dirty, saving = false, onSave, onDiscard, message, saveLabel, discardLabel }) {
|
|
212
|
+
const api = React7.useContext(SaveBarApiContext);
|
|
213
|
+
React7.useEffect(() => {
|
|
214
|
+
if (api) api.handlers.current = { onSave, onDiscard };
|
|
215
|
+
});
|
|
216
|
+
React7.useEffect(() => {
|
|
217
|
+
if (!api) return;
|
|
218
|
+
api.setState({ visible: dirty, saving, message, saveLabel, discardLabel });
|
|
219
|
+
return () => api.setState((s) => ({ ...s, visible: false }));
|
|
220
|
+
}, [api, dirty, saving, message, saveLabel, discardLabel]);
|
|
221
|
+
}
|
|
222
|
+
function SaveBar({ className }) {
|
|
223
|
+
const state = React7.useContext(SaveBarStateContext);
|
|
224
|
+
const api = React7.useContext(SaveBarApiContext);
|
|
225
|
+
const t = useTranslation();
|
|
226
|
+
const [rendered, setRendered] = React7.useState(state.visible);
|
|
227
|
+
const [closing, setClosing] = React7.useState(false);
|
|
228
|
+
React7.useEffect(() => {
|
|
229
|
+
if (state.visible) {
|
|
230
|
+
setRendered(true);
|
|
231
|
+
setClosing(false);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!rendered) return;
|
|
235
|
+
setClosing(true);
|
|
236
|
+
const id = setTimeout(() => {
|
|
237
|
+
setRendered(false);
|
|
238
|
+
setClosing(false);
|
|
239
|
+
}, 200);
|
|
240
|
+
return () => clearTimeout(id);
|
|
241
|
+
}, [state.visible, rendered]);
|
|
242
|
+
if (!api || !rendered) return null;
|
|
243
|
+
const { saving, message, saveLabel, discardLabel } = state;
|
|
244
|
+
return /* @__PURE__ */ jsx("div", { className: cn("pointer-events-none fixed inset-x-0 bottom-5 z-40 flex justify-center px-4", className), children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
245
|
+
"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",
|
|
246
|
+
closing ? "animate-out fade-out slide-out-to-bottom-2" : "animate-in fade-in slide-in-from-bottom-2"
|
|
247
|
+
), children: [
|
|
248
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium", children: [
|
|
249
|
+
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-400" }),
|
|
250
|
+
message ?? t("saveBar.unsaved")
|
|
251
|
+
] }),
|
|
252
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
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
|
+
}
|
|
262
|
+
),
|
|
263
|
+
/* @__PURE__ */ jsxs(
|
|
264
|
+
"button",
|
|
265
|
+
{
|
|
266
|
+
type: "button",
|
|
267
|
+
disabled: saving,
|
|
268
|
+
onClick: () => api.handlers.current.onSave?.(),
|
|
269
|
+
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",
|
|
270
|
+
children: [
|
|
271
|
+
saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1.5 h-3.5 w-3.5 animate-spin" }),
|
|
272
|
+
saveLabel ?? t("common.save")
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
)
|
|
276
|
+
] })
|
|
277
|
+
] }) });
|
|
278
|
+
}
|
|
279
|
+
function useCrossModuleBack(onBack, parentLabel) {
|
|
280
|
+
const currentPath = typeof window !== "undefined" ? (window.location.hash ? window.location.hash.slice(1) : window.location.pathname) || "/" : "/";
|
|
281
|
+
const referrer = useNavReferrer(currentPath);
|
|
282
|
+
const cross = referrer && referrer.label && routeModule(referrer.path) !== routeModule(currentPath) ? referrer : null;
|
|
283
|
+
return {
|
|
284
|
+
onBack: cross ? () => navHistoryBack(onBack) : onBack,
|
|
285
|
+
parentLabel: cross ? cross.label : parentLabel,
|
|
286
|
+
isCrossModule: !!cross
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function PageHeaderCrumb({
|
|
290
|
+
title,
|
|
291
|
+
parentLabel,
|
|
292
|
+
onBack
|
|
293
|
+
}) {
|
|
294
|
+
if (!onBack) {
|
|
295
|
+
return title ? /* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate text-lg font-semibold", children: title }) : null;
|
|
296
|
+
}
|
|
297
|
+
return /* @__PURE__ */ jsxs("nav", { className: "flex min-w-0 items-center gap-1.5 text-sm text-muted-foreground", children: [
|
|
298
|
+
/* @__PURE__ */ jsxs(
|
|
299
|
+
"button",
|
|
300
|
+
{
|
|
301
|
+
type: "button",
|
|
302
|
+
onClick: onBack,
|
|
303
|
+
className: "inline-flex shrink-0 items-center gap-1 transition-colors hover:text-foreground",
|
|
304
|
+
children: [
|
|
305
|
+
/* @__PURE__ */ jsx(ArrowLeft, { className: "h-3.5 w-3.5" }),
|
|
306
|
+
parentLabel ?? "Voltar"
|
|
307
|
+
]
|
|
308
|
+
}
|
|
309
|
+
),
|
|
310
|
+
title != null && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
311
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0", children: "/" }),
|
|
312
|
+
title === "" ? (
|
|
313
|
+
// Title still waiting on data: a pulsing bar, otherwise the line
|
|
314
|
+
// appears from nowhere and shoves the rest when the record lands.
|
|
315
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block h-4 w-24 animate-pulse rounded bg-muted align-middle" })
|
|
316
|
+
) : /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-medium text-foreground", children: title })
|
|
317
|
+
] })
|
|
318
|
+
] });
|
|
319
|
+
}
|
|
320
|
+
function PageHeader({ title, onBack, parentLabel, backLabel }) {
|
|
321
|
+
const hasHost = useHasPageHeaderHost();
|
|
322
|
+
const { onBack: effectiveOnBack, parentLabel: effectiveParent } = useCrossModuleBack(onBack, parentLabel);
|
|
323
|
+
useBackHandler(effectiveOnBack);
|
|
324
|
+
const node = React7.useMemo(() => /* @__PURE__ */ jsx(
|
|
325
|
+
PageHeaderCrumb,
|
|
326
|
+
{
|
|
327
|
+
title,
|
|
328
|
+
parentLabel: effectiveParent ?? backLabel,
|
|
329
|
+
onBack: effectiveOnBack
|
|
330
|
+
}
|
|
331
|
+
), [effectiveOnBack, effectiveParent, backLabel, title]);
|
|
332
|
+
usePageHeader(hasHost ? node : null, { back: !!effectiveOnBack });
|
|
333
|
+
if (hasHost) return null;
|
|
334
|
+
return /* @__PURE__ */ jsx("div", { className: "mb-4", children: node });
|
|
335
|
+
}
|
|
12
336
|
var GUTTER_PX = 8;
|
|
13
337
|
var CLOSE_DELAY_MS = 140;
|
|
14
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ?
|
|
338
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
|
|
15
339
|
function iconOf(name) {
|
|
16
340
|
if (!name) return null;
|
|
17
341
|
return ICON_MAP[name] ?? null;
|
|
18
342
|
}
|
|
343
|
+
var RailFlyoutContext = React7.createContext(null);
|
|
344
|
+
var RailFlyoutProvider = RailFlyoutContext.Provider;
|
|
345
|
+
function useRailRow(content) {
|
|
346
|
+
const flyout = React7.useContext(RailFlyoutContext);
|
|
347
|
+
if (!flyout || !content) return {};
|
|
348
|
+
return flyout.rowProps(content);
|
|
349
|
+
}
|
|
19
350
|
function useRailFlyout({
|
|
20
351
|
enabled,
|
|
21
352
|
railRef,
|
|
22
353
|
activeRoute,
|
|
23
354
|
onNavigate
|
|
24
355
|
}) {
|
|
25
|
-
const [state, setState] =
|
|
26
|
-
const [pos, setPos] =
|
|
27
|
-
const [animate, setAnimate] =
|
|
28
|
-
const panelRef =
|
|
29
|
-
const closeTimer =
|
|
30
|
-
const lastContent =
|
|
31
|
-
const cancelClose =
|
|
356
|
+
const [state, setState] = React7.useState(null);
|
|
357
|
+
const [pos, setPos] = React7.useState({ top: 0, arrow: 20 });
|
|
358
|
+
const [animate, setAnimate] = React7.useState(false);
|
|
359
|
+
const panelRef = React7.useRef(null);
|
|
360
|
+
const closeTimer = React7.useRef(null);
|
|
361
|
+
const lastContent = React7.useRef(null);
|
|
362
|
+
const cancelClose = React7.useCallback(() => {
|
|
32
363
|
if (closeTimer.current) {
|
|
33
364
|
clearTimeout(closeTimer.current);
|
|
34
365
|
closeTimer.current = null;
|
|
35
366
|
}
|
|
36
367
|
}, []);
|
|
37
|
-
const show =
|
|
368
|
+
const show = React7.useCallback(
|
|
38
369
|
(anchor, content2) => {
|
|
39
370
|
if (!enabled) return;
|
|
40
371
|
cancelClose();
|
|
@@ -42,19 +373,19 @@ function useRailFlyout({
|
|
|
42
373
|
},
|
|
43
374
|
[enabled, cancelClose]
|
|
44
375
|
);
|
|
45
|
-
const hide =
|
|
376
|
+
const hide = React7.useCallback(() => {
|
|
46
377
|
cancelClose();
|
|
47
378
|
closeTimer.current = setTimeout(() => setState(null), CLOSE_DELAY_MS);
|
|
48
379
|
}, [cancelClose]);
|
|
49
|
-
const dismiss =
|
|
380
|
+
const dismiss = React7.useCallback(() => {
|
|
50
381
|
cancelClose();
|
|
51
382
|
setState(null);
|
|
52
383
|
}, [cancelClose]);
|
|
53
|
-
|
|
54
|
-
|
|
384
|
+
React7.useEffect(() => () => cancelClose(), [cancelClose]);
|
|
385
|
+
React7.useEffect(() => {
|
|
55
386
|
if (!enabled) dismiss();
|
|
56
387
|
}, [enabled, dismiss]);
|
|
57
|
-
const reposition =
|
|
388
|
+
const reposition = React7.useCallback(() => {
|
|
58
389
|
const rail = railRef.current;
|
|
59
390
|
const anchor = state?.anchor;
|
|
60
391
|
if (!rail || !anchor) return;
|
|
@@ -69,7 +400,7 @@ function useRailFlyout({
|
|
|
69
400
|
setPos({ top, arrow });
|
|
70
401
|
}, [railRef, state]);
|
|
71
402
|
useIsomorphicLayoutEffect(reposition, [reposition, state?.content.key]);
|
|
72
|
-
|
|
403
|
+
React7.useEffect(() => {
|
|
73
404
|
if (!state) return;
|
|
74
405
|
const rail = railRef.current;
|
|
75
406
|
const onScroll = () => reposition();
|
|
@@ -81,7 +412,7 @@ function useRailFlyout({
|
|
|
81
412
|
};
|
|
82
413
|
}, [state, railRef, reposition]);
|
|
83
414
|
const visible = !!state;
|
|
84
|
-
|
|
415
|
+
React7.useEffect(() => {
|
|
85
416
|
if (!visible) {
|
|
86
417
|
setAnimate(false);
|
|
87
418
|
return;
|
|
@@ -89,7 +420,7 @@ function useRailFlyout({
|
|
|
89
420
|
const id = requestAnimationFrame(() => setAnimate(true));
|
|
90
421
|
return () => cancelAnimationFrame(id);
|
|
91
422
|
}, [visible]);
|
|
92
|
-
|
|
423
|
+
React7.useEffect(() => {
|
|
93
424
|
if (!visible) return;
|
|
94
425
|
const onKey = (event) => {
|
|
95
426
|
if (event.key === "Escape") dismiss();
|
|
@@ -97,7 +428,7 @@ function useRailFlyout({
|
|
|
97
428
|
window.addEventListener("keydown", onKey);
|
|
98
429
|
return () => window.removeEventListener("keydown", onKey);
|
|
99
430
|
}, [visible, dismiss]);
|
|
100
|
-
const rowProps =
|
|
431
|
+
const rowProps = React7.useCallback(
|
|
101
432
|
(content2) => ({
|
|
102
433
|
onMouseEnter: (event) => show(event.currentTarget, content2),
|
|
103
434
|
onFocus: (event) => show(event.currentTarget, content2),
|
|
@@ -214,7 +545,7 @@ function FlyoutBody({
|
|
|
214
545
|
] });
|
|
215
546
|
}
|
|
216
547
|
var RAIL_PUSH_MS = 260;
|
|
217
|
-
var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ?
|
|
548
|
+
var useIsomorphicLayoutEffect2 = typeof window !== "undefined" ? React7.useLayoutEffect : React7.useEffect;
|
|
218
549
|
function getIcon(name) {
|
|
219
550
|
return ICON_MAP[name] ?? Home;
|
|
220
551
|
}
|
|
@@ -258,7 +589,7 @@ function NavItem({
|
|
|
258
589
|
// as cramped rather than as dense.
|
|
259
590
|
"flex w-full items-center gap-3 rounded-button px-2.5 py-2 text-[13px] font-medium transition-colors",
|
|
260
591
|
"hover:bg-sidebar-accent/60 hover:text-sidebar-accent-foreground",
|
|
261
|
-
isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground
|
|
592
|
+
isActive ? "bg-sidebar-accent font-semibold text-sidebar-accent-foreground" : "text-sidebar-foreground/80",
|
|
262
593
|
collapsed && "justify-center px-2"
|
|
263
594
|
);
|
|
264
595
|
const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -314,7 +645,7 @@ function NavGroup({
|
|
|
314
645
|
flyout
|
|
315
646
|
}) {
|
|
316
647
|
const isChildActive = item.children?.some((c) => c.route === activeRoute);
|
|
317
|
-
const [open, setOpen] =
|
|
648
|
+
const [open, setOpen] = React7.useState(() => isChildActive ?? false);
|
|
318
649
|
const Icon = getIcon(item.icon);
|
|
319
650
|
if (collapsed) {
|
|
320
651
|
const hoverProps = flyout?.rowProps(flyoutContentOf(item));
|
|
@@ -388,7 +719,7 @@ function NavGroup({
|
|
|
388
719
|
child.premium && /* @__PURE__ */ jsx(Crown, { className: "h-3 w-3 shrink-0 text-amber-500", "aria-hidden": true })
|
|
389
720
|
] });
|
|
390
721
|
const row = childExternal ? /* @__PURE__ */ jsx("a", { href: child.href, target: "_blank", rel: "noopener noreferrer", className: childClass, children: childInner }) : /* @__PURE__ */ jsx("button", { onClick: () => onNavigate(child.route), className: childClass, children: childInner });
|
|
391
|
-
if (!childConnector) return /* @__PURE__ */ jsx(
|
|
722
|
+
if (!childConnector) return /* @__PURE__ */ jsx(React7.Fragment, { children: row }, child.id);
|
|
392
723
|
const activeIndex = item.children.findIndex((c) => c.route === activeRoute);
|
|
393
724
|
const onPath = activeIndex >= 0 && childIndex < activeIndex;
|
|
394
725
|
return (
|
|
@@ -450,9 +781,9 @@ function NavGroup({
|
|
|
450
781
|
] });
|
|
451
782
|
}
|
|
452
783
|
function withCollapsed(slot, collapsed) {
|
|
453
|
-
if (!
|
|
784
|
+
if (!React7.isValidElement(slot)) return slot;
|
|
454
785
|
if (typeof slot.type === "string") return slot;
|
|
455
|
-
return
|
|
786
|
+
return React7.cloneElement(slot, { collapsed });
|
|
456
787
|
}
|
|
457
788
|
function Sidebar({
|
|
458
789
|
navigation,
|
|
@@ -464,6 +795,7 @@ function Sidebar({
|
|
|
464
795
|
user: _user,
|
|
465
796
|
orgSwitcher,
|
|
466
797
|
topContent,
|
|
798
|
+
identity,
|
|
467
799
|
navTop,
|
|
468
800
|
footerContent,
|
|
469
801
|
borderless,
|
|
@@ -474,12 +806,13 @@ function Sidebar({
|
|
|
474
806
|
sectionLabels,
|
|
475
807
|
navKey,
|
|
476
808
|
navDirection = "forward",
|
|
477
|
-
navChildConnector
|
|
809
|
+
navChildConnector,
|
|
810
|
+
drawer
|
|
478
811
|
}) {
|
|
479
812
|
const currentPath = currentPathProp;
|
|
480
813
|
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
481
814
|
const activeRoute = resolveActiveRoute(navigation, currentPath);
|
|
482
|
-
const railRef =
|
|
815
|
+
const railRef = React7.useRef(null);
|
|
483
816
|
const flyout = useRailFlyout({
|
|
484
817
|
enabled: collapsed,
|
|
485
818
|
railRef,
|
|
@@ -525,14 +858,13 @@ function Sidebar({
|
|
|
525
858
|
item.id
|
|
526
859
|
);
|
|
527
860
|
const navBody = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
528
|
-
navTop && /* @__PURE__ */ jsx("div", { className: "mb-2", children: withCollapsed(navTop, collapsed) }),
|
|
529
861
|
mainItems.length > 0 && renderSection(mainItems, sectionLabels?.main),
|
|
530
862
|
secondaryItems.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6", children: renderSection(secondaryItems, sectionLabels?.secondary) })
|
|
531
863
|
] });
|
|
532
|
-
const [outgoing, setOutgoing] =
|
|
533
|
-
const lastBodyRef =
|
|
534
|
-
const lastKeyRef =
|
|
535
|
-
const timerRef =
|
|
864
|
+
const [outgoing, setOutgoing] = React7.useState(null);
|
|
865
|
+
const lastBodyRef = React7.useRef(null);
|
|
866
|
+
const lastKeyRef = React7.useRef(navKey);
|
|
867
|
+
const timerRef = React7.useRef(null);
|
|
536
868
|
useIsomorphicLayoutEffect2(() => {
|
|
537
869
|
if (lastKeyRef.current !== navKey) {
|
|
538
870
|
const previousBody = lastBodyRef.current;
|
|
@@ -545,166 +877,183 @@ function Sidebar({
|
|
|
545
877
|
}
|
|
546
878
|
lastBodyRef.current = navBody;
|
|
547
879
|
});
|
|
548
|
-
|
|
880
|
+
React7.useEffect(() => () => {
|
|
549
881
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
550
882
|
}, []);
|
|
551
|
-
return
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
"
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
"div",
|
|
575
|
-
{
|
|
576
|
-
className: cn(
|
|
577
|
-
"flex h-14 items-center justify-between px-4",
|
|
578
|
-
!borderless && "border-b border-sidebar-border",
|
|
579
|
-
collapsed && "justify-center px-2"
|
|
580
|
-
),
|
|
581
|
-
children: [
|
|
582
|
-
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
583
|
-
logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }),
|
|
584
|
-
/* @__PURE__ */ jsx(
|
|
585
|
-
"button",
|
|
586
|
-
{
|
|
587
|
-
onClick: onToggle,
|
|
588
|
-
className: "ml-auto rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
589
|
-
"aria-label": "Collapse sidebar",
|
|
590
|
-
children: /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
|
|
591
|
-
}
|
|
592
|
-
)
|
|
593
|
-
] }),
|
|
594
|
-
collapsed && /* @__PURE__ */ jsx(
|
|
595
|
-
"button",
|
|
596
|
-
{
|
|
597
|
-
onClick: onToggle,
|
|
598
|
-
className: "rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
599
|
-
"aria-label": "Expand sidebar",
|
|
600
|
-
children: /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" })
|
|
601
|
-
}
|
|
602
|
-
)
|
|
603
|
-
]
|
|
604
|
-
}
|
|
883
|
+
return (
|
|
884
|
+
// O handle do flyout desce por contexto para QUALQUER linha da trilha —
|
|
885
|
+
// inclusive os widgets que os plugins montam no pé dela. Ver useRailRow.
|
|
886
|
+
/* @__PURE__ */ jsx(RailFlyoutProvider, { value: flyout, children: /* @__PURE__ */ jsxs(
|
|
887
|
+
"aside",
|
|
888
|
+
{
|
|
889
|
+
ref: railRef,
|
|
890
|
+
"data-print": "hide",
|
|
891
|
+
onBlur: (event) => {
|
|
892
|
+
if (!event.currentTarget.contains(event.relatedTarget)) flyout.dismiss();
|
|
893
|
+
},
|
|
894
|
+
className: cn(
|
|
895
|
+
// `relative` anchors the flyout, which hangs off the rail's right edge
|
|
896
|
+
// and has to escape the nav's own scroll box to do it.
|
|
897
|
+
"relative flex h-full flex-col bg-sidebar text-sidebar-foreground transition-all duration-200",
|
|
898
|
+
!borderless && "border-r border-sidebar-border",
|
|
899
|
+
// 14rem expanded. It was 16rem, which is the width a rail needs when
|
|
900
|
+
// its entries are sentences ("GlossGenius Financing", "Configure your
|
|
901
|
+
// payment methods"); these are one or two words, so the extra 2rem was
|
|
902
|
+
// paid for by the content column on every screen in every app. The
|
|
903
|
+
// labels still truncate, and a long one now truncates 32px earlier —
|
|
904
|
+
// which is the trade, taken deliberately.
|
|
905
|
+
collapsed ? "w-16" : "w-56"
|
|
605
906
|
),
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
] })
|
|
633
|
-
]
|
|
634
|
-
}
|
|
635
|
-
) }),
|
|
636
|
-
/* @__PURE__ */ jsxs(
|
|
637
|
-
"nav",
|
|
638
|
-
{
|
|
639
|
-
className: cn(
|
|
640
|
-
"relative flex-1 p-2",
|
|
641
|
-
// scrollbar-none: the rail still scrolls, but without the native
|
|
642
|
-
// scrollbar gutter eating into the nav items.
|
|
643
|
-
outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
|
|
907
|
+
children: [
|
|
908
|
+
/* @__PURE__ */ jsxs("div", { className: cn("shrink-0", !borderless && "border-b border-sidebar-border"), children: [
|
|
909
|
+
/* @__PURE__ */ jsxs(
|
|
910
|
+
"div",
|
|
911
|
+
{
|
|
912
|
+
className: cn(
|
|
913
|
+
"flex items-center gap-1 px-2",
|
|
914
|
+
// h-12 com a conta embaixo (48 + 44 + 8 = 100px, na régua de 4/8),
|
|
915
|
+
// h-14 quando a marca é a única coisa aqui e a linha tem de bater
|
|
916
|
+
// com o cabeçalho da página.
|
|
917
|
+
identity ? "h-12" : "h-14",
|
|
918
|
+
collapsed && "justify-center"
|
|
919
|
+
),
|
|
920
|
+
children: [
|
|
921
|
+
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" }) }),
|
|
922
|
+
drawer && /* @__PURE__ */ jsx(
|
|
923
|
+
"button",
|
|
924
|
+
{
|
|
925
|
+
onClick: onToggle,
|
|
926
|
+
className: "fz-tap shrink-0 rounded-button p-1.5 text-sidebar-muted transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
927
|
+
"aria-label": "Close menu",
|
|
928
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
|
|
929
|
+
}
|
|
930
|
+
)
|
|
931
|
+
]
|
|
932
|
+
}
|
|
644
933
|
),
|
|
645
|
-
children:
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
/* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
688
|
-
"flex items-center rounded-button",
|
|
689
|
-
collapsed ? "flex-col gap-1" : "gap-1 px-1"
|
|
690
|
-
), children: [
|
|
691
|
-
userMenuSlot,
|
|
692
|
-
!collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
693
|
-
notificationSlot ?? /* @__PURE__ */ jsxs(
|
|
694
|
-
"button",
|
|
934
|
+
identity && /* @__PURE__ */ jsx("div", { className: cn("px-2 pb-2", collapsed && "flex justify-center"), children: withCollapsed(identity, collapsed) })
|
|
935
|
+
] }),
|
|
936
|
+
navTop ? /* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: withCollapsed(navTop, collapsed) }) : (
|
|
937
|
+
/* Search — above the first destination. It used to be a 16px magnifier
|
|
938
|
+
tucked beside the avatar at the very bottom, which is where you put
|
|
939
|
+
something nobody is meant to find: the fastest way through an admin
|
|
940
|
+
with thirty screens was the one control shaped like an afterthought.
|
|
941
|
+
Here it reads as what it is — a field — and says which key opens it.
|
|
942
|
+
Clicking it opens the same palette ⌘K does; it is a button, not an
|
|
943
|
+
input, because typing happens there. */
|
|
944
|
+
/* @__PURE__ */ jsx("div", { className: "px-2 pb-2 pt-2", children: /* @__PURE__ */ jsxs(
|
|
945
|
+
"button",
|
|
946
|
+
{
|
|
947
|
+
onClick: () => setCommandPaletteOpen(true),
|
|
948
|
+
"aria-keyshortcuts": "Meta+K Control+K",
|
|
949
|
+
className: cn(
|
|
950
|
+
// Tonal surface, no outline. On a dark rail a 1px border is a hard
|
|
951
|
+
// edge competing with every other hard edge; a lighter fill of the
|
|
952
|
+
// same hue reads as "a field" without drawing a box around it — and
|
|
953
|
+
// leaves the outline treatment free to mean focus, which it must.
|
|
954
|
+
"fz-tap group flex items-center rounded-lg bg-sidebar-accent/40 text-sidebar-muted transition-colors",
|
|
955
|
+
"hover:bg-sidebar-accent/70 hover:text-sidebar-foreground",
|
|
956
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sidebar-accent-foreground/50",
|
|
957
|
+
collapsed ? "h-9 w-9 justify-center" : "h-9 w-full gap-2.5 px-2.5"
|
|
958
|
+
),
|
|
959
|
+
"aria-label": searchLabel,
|
|
960
|
+
title: collapsed ? searchLabel : void 0,
|
|
961
|
+
children: [
|
|
962
|
+
/* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0" }),
|
|
963
|
+
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
964
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-left text-[13px]", children: searchLabel }),
|
|
965
|
+
/* @__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: [
|
|
966
|
+
shortcutKey(),
|
|
967
|
+
"K"
|
|
968
|
+
] })
|
|
969
|
+
] })
|
|
970
|
+
]
|
|
971
|
+
}
|
|
972
|
+
) })
|
|
973
|
+
),
|
|
974
|
+
/* @__PURE__ */ jsxs(
|
|
975
|
+
"nav",
|
|
695
976
|
{
|
|
696
|
-
className:
|
|
697
|
-
|
|
977
|
+
className: cn(
|
|
978
|
+
"relative flex-1 p-2",
|
|
979
|
+
// scrollbar-none: the rail still scrolls, but without the native
|
|
980
|
+
// scrollbar gutter eating into the nav items.
|
|
981
|
+
outgoing ? "overflow-hidden" : "overflow-y-auto scrollbar-none"
|
|
982
|
+
),
|
|
698
983
|
children: [
|
|
699
|
-
/* @__PURE__ */ jsx(
|
|
700
|
-
|
|
984
|
+
outgoing && /* @__PURE__ */ jsx(
|
|
985
|
+
"div",
|
|
986
|
+
{
|
|
987
|
+
"aria-hidden": true,
|
|
988
|
+
className: cn(
|
|
989
|
+
"pointer-events-none absolute inset-x-2 top-2 space-y-1",
|
|
990
|
+
outgoing.direction === "back" ? "saas-rail-out-back" : "saas-rail-out-forward"
|
|
991
|
+
),
|
|
992
|
+
children: outgoing.body
|
|
993
|
+
}
|
|
994
|
+
),
|
|
995
|
+
/* @__PURE__ */ jsx(
|
|
996
|
+
"div",
|
|
997
|
+
{
|
|
998
|
+
className: cn(
|
|
999
|
+
"space-y-1",
|
|
1000
|
+
outgoing && (outgoing.direction === "back" ? "saas-rail-in-back" : "saas-rail-in-forward")
|
|
1001
|
+
),
|
|
1002
|
+
children: navBody
|
|
1003
|
+
},
|
|
1004
|
+
navKey
|
|
1005
|
+
)
|
|
701
1006
|
]
|
|
702
1007
|
}
|
|
703
|
-
)
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
1008
|
+
),
|
|
1009
|
+
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(
|
|
1010
|
+
NavItem,
|
|
1011
|
+
{
|
|
1012
|
+
item,
|
|
1013
|
+
collapsed,
|
|
1014
|
+
isActive: item.route === activeRoute,
|
|
1015
|
+
onNavigate: handleNavigate,
|
|
1016
|
+
flyout
|
|
1017
|
+
},
|
|
1018
|
+
item.id
|
|
1019
|
+
)) }),
|
|
1020
|
+
footerContent && // `empty:hidden` — slot children may all render null (e.g. the
|
|
1021
|
+
// SeuSiteWidget self-hides when the tenant has no project); without
|
|
1022
|
+
// it the padded/bordered wrapper shows as a blank block.
|
|
1023
|
+
/* @__PURE__ */ jsx("div", { className: cn("space-y-2 p-2 empty:hidden", !borderless && "border-t border-sidebar-border"), children: footerContent }),
|
|
1024
|
+
withCollapsed(orgSwitcher, collapsed),
|
|
1025
|
+
/* @__PURE__ */ jsx("div", { className: cn("p-2", !borderless && "border-t border-sidebar-border"), children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
1026
|
+
"flex items-center rounded-button",
|
|
1027
|
+
collapsed ? "flex-col gap-1" : "gap-1 px-1"
|
|
1028
|
+
), children: [
|
|
1029
|
+
userMenuSlot,
|
|
1030
|
+
!collapsed && /* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
1031
|
+
notificationSlot ?? /* @__PURE__ */ jsxs(
|
|
1032
|
+
"button",
|
|
1033
|
+
{
|
|
1034
|
+
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",
|
|
1035
|
+
"aria-label": unreadCount > 0 ? `${unreadCount} unread notifications` : "Notifications",
|
|
1036
|
+
children: [
|
|
1037
|
+
/* @__PURE__ */ jsx(Bell, { className: "h-4 w-4" }),
|
|
1038
|
+
unreadCount > 0 && /* @__PURE__ */ jsx("span", { className: "absolute right-1 top-1 h-2 w-2 rounded-full bg-destructive" })
|
|
1039
|
+
]
|
|
1040
|
+
}
|
|
1041
|
+
),
|
|
1042
|
+
!drawer && /* @__PURE__ */ jsx(
|
|
1043
|
+
"button",
|
|
1044
|
+
{
|
|
1045
|
+
onClick: onToggle,
|
|
1046
|
+
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",
|
|
1047
|
+
"aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
1048
|
+
title: collapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
1049
|
+
children: collapsed ? /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
|
|
1050
|
+
}
|
|
1051
|
+
)
|
|
1052
|
+
] }) }),
|
|
1053
|
+
flyout.panel
|
|
1054
|
+
]
|
|
1055
|
+
}
|
|
1056
|
+
) })
|
|
708
1057
|
);
|
|
709
1058
|
}
|
|
710
1059
|
function TopbarUserMenu({
|
|
@@ -717,7 +1066,7 @@ function TopbarUserMenu({
|
|
|
717
1066
|
onBilling,
|
|
718
1067
|
userMenuExtras
|
|
719
1068
|
}) {
|
|
720
|
-
const [open, setOpen] =
|
|
1069
|
+
const [open, setOpen] = React7.useState(false);
|
|
721
1070
|
if (!user) return null;
|
|
722
1071
|
const initial = (user.fullName?.trim()?.[0] ?? user.email?.[0] ?? "?").toUpperCase();
|
|
723
1072
|
const item = (label, onClick, Icon) => onClick ? /* @__PURE__ */ jsxs(
|
|
@@ -797,115 +1146,11 @@ function TopbarUserMenu({
|
|
|
797
1146
|
) })
|
|
798
1147
|
] });
|
|
799
1148
|
}
|
|
800
|
-
var ICON_MAP2 = {
|
|
801
|
-
Home: Home,
|
|
802
|
-
Users,
|
|
803
|
-
Settings: Settings,
|
|
804
|
-
CreditCard,
|
|
805
|
-
Bell: Bell,
|
|
806
|
-
Calendar,
|
|
807
|
-
CalendarClock,
|
|
808
|
-
Package,
|
|
809
|
-
Activity,
|
|
810
|
-
BarChart3,
|
|
811
|
-
FileText,
|
|
812
|
-
Mail,
|
|
813
|
-
DollarSign,
|
|
814
|
-
Megaphone,
|
|
815
|
-
ShoppingCart,
|
|
816
|
-
ShoppingBag,
|
|
817
|
-
Target,
|
|
818
|
-
Wrench,
|
|
819
|
-
ClipboardList,
|
|
820
|
-
ClipboardCheck,
|
|
821
|
-
Briefcase,
|
|
822
|
-
UserCog,
|
|
823
|
-
BookOpen,
|
|
824
|
-
MessageCircle,
|
|
825
|
-
Globe,
|
|
826
|
-
BookOpenCheck,
|
|
827
|
-
Percent,
|
|
828
|
-
Tag,
|
|
829
|
-
Tags,
|
|
830
|
-
Camera,
|
|
831
|
-
UtensilsCrossed,
|
|
832
|
-
Search: Search,
|
|
833
|
-
MapPin,
|
|
834
|
-
Handshake,
|
|
835
|
-
Contact,
|
|
836
|
-
Building2,
|
|
837
|
-
ChevronDown: ChevronDown,
|
|
838
|
-
Filter,
|
|
839
|
-
Plus,
|
|
840
|
-
Shield,
|
|
841
|
-
List,
|
|
842
|
-
ListChecks,
|
|
843
|
-
FolderOpen,
|
|
844
|
-
Dog,
|
|
845
|
-
Cat,
|
|
846
|
-
PawPrint,
|
|
847
|
-
Heart,
|
|
848
|
-
LayoutTemplate,
|
|
849
|
-
LeafyGreen,
|
|
850
|
-
Apple,
|
|
851
|
-
Egg,
|
|
852
|
-
Wheat,
|
|
853
|
-
ArrowDownCircle,
|
|
854
|
-
ArrowUpCircle,
|
|
855
|
-
ArrowLeftRight,
|
|
856
|
-
Plug,
|
|
857
|
-
SlidersHorizontal,
|
|
858
|
-
TableProperties,
|
|
859
|
-
Landmark,
|
|
860
|
-
Receipt,
|
|
861
|
-
TrendingUp,
|
|
862
|
-
CircleDollarSign,
|
|
863
|
-
Clock,
|
|
864
|
-
AlertTriangle,
|
|
865
|
-
Sparkles,
|
|
866
|
-
Wallet,
|
|
867
|
-
Warehouse,
|
|
868
|
-
Ruler,
|
|
869
|
-
ArrowUpRight,
|
|
870
|
-
ArrowDownRight,
|
|
871
|
-
UserPlus,
|
|
872
|
-
UserX,
|
|
873
|
-
TreePalm,
|
|
874
|
-
Boxes,
|
|
875
|
-
Box,
|
|
876
|
-
FileCheck2,
|
|
877
|
-
PartyPopper,
|
|
878
|
-
Radio,
|
|
879
|
-
CalendarDays,
|
|
880
|
-
BadgeDollarSign,
|
|
881
|
-
Banknote,
|
|
882
|
-
Layers,
|
|
883
|
-
Music,
|
|
884
|
-
Eye,
|
|
885
|
-
ListMusic,
|
|
886
|
-
Disc3,
|
|
887
|
-
UsersRound,
|
|
888
|
-
Mic,
|
|
889
|
-
CalendarCheck2,
|
|
890
|
-
CalendarX,
|
|
891
|
-
ListPlus,
|
|
892
|
-
Map,
|
|
893
|
-
Ban,
|
|
894
|
-
Clock3,
|
|
895
|
-
Inbox,
|
|
896
|
-
UserCheck,
|
|
897
|
-
Star,
|
|
898
|
-
Zap,
|
|
899
|
-
Clapperboard,
|
|
900
|
-
Newspaper,
|
|
901
|
-
GraduationCap,
|
|
902
|
-
Repeat
|
|
903
|
-
};
|
|
904
1149
|
function getIcon2(name) {
|
|
905
|
-
return
|
|
1150
|
+
return ICON_MAP[name] ?? Home;
|
|
906
1151
|
}
|
|
907
1152
|
function NavDropdown({ item, currentPath, onNavigate }) {
|
|
908
|
-
const [open, setOpen] =
|
|
1153
|
+
const [open, setOpen] = React7.useState(false);
|
|
909
1154
|
const Icon = getIcon2(item.icon);
|
|
910
1155
|
const isChildActive = item.children?.some(
|
|
911
1156
|
(child) => currentPath === child.route || currentPath.startsWith(child.route + "/")
|
|
@@ -975,7 +1220,8 @@ function Topbar({
|
|
|
975
1220
|
onBilling,
|
|
976
1221
|
userPlan,
|
|
977
1222
|
billingLabel,
|
|
978
|
-
userMenuExtras
|
|
1223
|
+
userMenuExtras,
|
|
1224
|
+
userMenuSlot
|
|
979
1225
|
}) {
|
|
980
1226
|
const setCommandPaletteOpen = useLayoutStore((s) => s.setCommandPaletteOpen);
|
|
981
1227
|
const mainNav = navigation.filter((item) => item.section === "main");
|
|
@@ -986,7 +1232,7 @@ function Topbar({
|
|
|
986
1232
|
};
|
|
987
1233
|
return /* @__PURE__ */ jsxs("header", { "data-print": "hide", className: "sticky top-0 z-50 w-full shrink-0", children: [
|
|
988
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: [
|
|
989
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1235
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
|
|
990
1236
|
onMenuClick && /* @__PURE__ */ jsx(
|
|
991
1237
|
"button",
|
|
992
1238
|
{
|
|
@@ -996,7 +1242,7 @@ function Topbar({
|
|
|
996
1242
|
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
997
1243
|
}
|
|
998
1244
|
),
|
|
999
|
-
/* @__PURE__ */ jsx("div", { className: "hidden shrink-0 items-center md:flex", children: logo ?? /* @__PURE__ */ jsx("span", { className: "text-lg font-bold", children: "App" }) })
|
|
1245
|
+
/* @__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" }) })
|
|
1000
1246
|
] }),
|
|
1001
1247
|
/* @__PURE__ */ jsx("div", { className: "mx-8 hidden max-w-sm flex-1 md:flex", children: /* @__PURE__ */ jsxs(
|
|
1002
1248
|
"button",
|
|
@@ -1011,10 +1257,9 @@ function Topbar({
|
|
|
1011
1257
|
}
|
|
1012
1258
|
) }),
|
|
1013
1259
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-1", children: [
|
|
1014
|
-
orgSwitcher && /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 items-center md:flex", children: orgSwitcher }),
|
|
1015
1260
|
leftContent,
|
|
1016
1261
|
rightContent,
|
|
1017
|
-
/* @__PURE__ */ jsx(
|
|
1262
|
+
userMenuSlot ?? /* @__PURE__ */ jsx(
|
|
1018
1263
|
TopbarUserMenu,
|
|
1019
1264
|
{
|
|
1020
1265
|
user,
|
|
@@ -1071,13 +1316,17 @@ function Topbar({
|
|
|
1071
1316
|
}) }) }) })
|
|
1072
1317
|
] });
|
|
1073
1318
|
}
|
|
1074
|
-
var ModuleHeaderSlotContext =
|
|
1319
|
+
var ModuleHeaderSlotContext = React7.createContext(null);
|
|
1075
1320
|
function useModuleHeaderSlot() {
|
|
1076
|
-
return
|
|
1321
|
+
return React7.useContext(ModuleHeaderSlotContext);
|
|
1077
1322
|
}
|
|
1078
|
-
var ModuleHeaderActionsSlotContext =
|
|
1323
|
+
var ModuleHeaderActionsSlotContext = React7.createContext(null);
|
|
1079
1324
|
function useModuleHeaderActionsSlot() {
|
|
1080
|
-
return
|
|
1325
|
+
return React7.useContext(ModuleHeaderActionsSlotContext);
|
|
1326
|
+
}
|
|
1327
|
+
var PageRailSlotContext = React7.createContext(null);
|
|
1328
|
+
function usePageRailSlot() {
|
|
1329
|
+
return React7.useContext(PageRailSlotContext);
|
|
1081
1330
|
}
|
|
1082
1331
|
function ShellPageTitle({
|
|
1083
1332
|
title,
|
|
@@ -1099,6 +1348,7 @@ function SidebarLayout({
|
|
|
1099
1348
|
userMenuExtras: _userMenuExtras,
|
|
1100
1349
|
orgSwitcher,
|
|
1101
1350
|
sidebarTopContent,
|
|
1351
|
+
sidebarIdentity,
|
|
1102
1352
|
sidebarNavTop,
|
|
1103
1353
|
sidebarFooterContent,
|
|
1104
1354
|
frame,
|
|
@@ -1116,16 +1366,19 @@ function SidebarLayout({
|
|
|
1116
1366
|
sidebarSectionLabels,
|
|
1117
1367
|
sidebarNavKey,
|
|
1118
1368
|
sidebarNavDirection,
|
|
1119
|
-
sidebarNavChildConnector
|
|
1369
|
+
sidebarNavChildConnector,
|
|
1370
|
+
railOpen
|
|
1120
1371
|
}) {
|
|
1121
1372
|
const sidebarCollapsed = useLayoutStore((s) => s.sidebarCollapsed);
|
|
1122
1373
|
const pageHeaderSlot = usePageHeaderSlot();
|
|
1123
1374
|
usePageHeaderHost();
|
|
1124
1375
|
const setSidebarCollapsed = useLayoutStore((s) => s.setSidebarCollapsed);
|
|
1125
|
-
const [mobileMenuOpen, setMobileMenuOpen] =
|
|
1126
|
-
const [headerSlot, setHeaderSlot] =
|
|
1127
|
-
const [actionsSlot, setActionsSlot] =
|
|
1128
|
-
|
|
1376
|
+
const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
|
|
1377
|
+
const [headerSlot, setHeaderSlot] = React7.useState(null);
|
|
1378
|
+
const [actionsSlot, setActionsSlot] = React7.useState(null);
|
|
1379
|
+
const [pageRailSlot, setPageRailSlot] = React7.useState(null);
|
|
1380
|
+
const hasPageActions = useHasPageActions();
|
|
1381
|
+
return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsx(ModuleHeaderSlotContext.Provider, { value: headerSlot, children: /* @__PURE__ */ jsx(ModuleHeaderActionsSlotContext.Provider, { value: actionsSlot, children: /* @__PURE__ */ jsx(PageRailSlotContext.Provider, { value: pageRailSlot, children: /* @__PURE__ */ jsxs("div", { className: cn(
|
|
1129
1382
|
"shell-enter flex h-screen overflow-hidden bg-background",
|
|
1130
1383
|
// Frame mode: the inset gap around the framed card takes the sidebar (menu)
|
|
1131
1384
|
// color, so the white card reads as a panel floating on the same tone as the
|
|
@@ -1148,6 +1401,7 @@ function SidebarLayout({
|
|
|
1148
1401
|
onBilling,
|
|
1149
1402
|
orgSwitcher,
|
|
1150
1403
|
topContent: sidebarTopContent,
|
|
1404
|
+
identity: sidebarIdentity,
|
|
1151
1405
|
navTop: sidebarNavTop,
|
|
1152
1406
|
footerContent: sidebarFooterContent,
|
|
1153
1407
|
borderless: frame,
|
|
@@ -1162,34 +1416,48 @@ function SidebarLayout({
|
|
|
1162
1416
|
}
|
|
1163
1417
|
) }),
|
|
1164
1418
|
/* @__PURE__ */ jsxs("div", { className: cn(
|
|
1165
|
-
"relative flex flex-1
|
|
1166
|
-
|
|
1419
|
+
"relative flex flex-1 overflow-hidden",
|
|
1420
|
+
// `content`, not `card`: this is the page's own surface, and cards have
|
|
1421
|
+
// to sit ON it. Painted card, the frame was the same tone as everything
|
|
1422
|
+
// inside it and the page read as one flat sheet.
|
|
1423
|
+
frame && "md:my-2 md:ml-2 md:rounded-xl md:border md:border-border md:bg-content",
|
|
1424
|
+
// With a companion rail open, the page and the rail are ONE card: the
|
|
1425
|
+
// right edge opens and the rail closes it. Two cards with a strip of
|
|
1426
|
+
// chrome between them made the assistant read as a separate window
|
|
1427
|
+
// parked next to the app rather than as a column of it — the rail's own
|
|
1428
|
+
// left border is then the single seam between the two.
|
|
1429
|
+
frame && (railOpen ? "md:rounded-r-none md:border-r-0" : "md:mr-2")
|
|
1167
1430
|
), children: [
|
|
1168
|
-
/* @__PURE__ */
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
"
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1431
|
+
/* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
|
|
1432
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 flex-1 flex-col overflow-hidden", children: [
|
|
1433
|
+
/* @__PURE__ */ jsxs("div", { className: cn(
|
|
1434
|
+
"flex min-h-14 shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-2",
|
|
1435
|
+
// …unless the page put its actions in it. Hiding the bar on mobile
|
|
1436
|
+
// used to hide the only "Novo" the screen had.
|
|
1437
|
+
mobileHeader !== "minimal" && !hasPageActions && "hidden md:flex"
|
|
1438
|
+
), children: [
|
|
1439
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
|
|
1440
|
+
!hasBottomNav && /* @__PURE__ */ jsx(
|
|
1441
|
+
"button",
|
|
1442
|
+
{
|
|
1443
|
+
onClick: () => setMobileMenuOpen(true),
|
|
1444
|
+
className: "inline-flex items-center justify-center rounded-md p-2 text-muted-foreground hover:bg-accent md:hidden",
|
|
1445
|
+
"aria-label": "Open menu",
|
|
1446
|
+
children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
|
|
1447
|
+
}
|
|
1448
|
+
),
|
|
1449
|
+
pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: pageTitle, back: pageBack }),
|
|
1450
|
+
topbarStart,
|
|
1451
|
+
/* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
|
|
1452
|
+
] }),
|
|
1453
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1454
|
+
/* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex items-center gap-2" }),
|
|
1455
|
+
topbarEnd
|
|
1456
|
+
] })
|
|
1185
1457
|
] }),
|
|
1186
|
-
/* @__PURE__ */
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
] })
|
|
1190
|
-
] }),
|
|
1191
|
-
/* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children }),
|
|
1192
|
-
/* @__PURE__ */ jsx(SaveBar, {})
|
|
1458
|
+
/* @__PURE__ */ jsx("main", { className: "min-w-0 flex-1 overflow-y-auto pb-16 md:pb-0", children }),
|
|
1459
|
+
/* @__PURE__ */ jsx(SaveBar, {})
|
|
1460
|
+
] })
|
|
1193
1461
|
] }),
|
|
1194
1462
|
mobileMenuOpen && /* @__PURE__ */ jsx(
|
|
1195
1463
|
MobileOverlay,
|
|
@@ -1205,6 +1473,7 @@ function SidebarLayout({
|
|
|
1205
1473
|
onBilling,
|
|
1206
1474
|
orgSwitcher,
|
|
1207
1475
|
topContent: sidebarTopContent,
|
|
1476
|
+
identity: sidebarIdentity,
|
|
1208
1477
|
navTop: sidebarNavTop,
|
|
1209
1478
|
footerContent: sidebarFooterContent,
|
|
1210
1479
|
userMenuSlot,
|
|
@@ -1214,7 +1483,7 @@ function SidebarLayout({
|
|
|
1214
1483
|
onClose: () => setMobileMenuOpen(false)
|
|
1215
1484
|
}
|
|
1216
1485
|
)
|
|
1217
|
-
] }) }) }) });
|
|
1486
|
+
] }) }) }) }) });
|
|
1218
1487
|
}
|
|
1219
1488
|
function TopbarLayout({
|
|
1220
1489
|
navigation = [],
|
|
@@ -1229,6 +1498,7 @@ function TopbarLayout({
|
|
|
1229
1498
|
userPlan,
|
|
1230
1499
|
billingLabel,
|
|
1231
1500
|
userMenuExtras,
|
|
1501
|
+
userMenuSlot,
|
|
1232
1502
|
orgSwitcher,
|
|
1233
1503
|
frame,
|
|
1234
1504
|
currentPath,
|
|
@@ -1239,13 +1509,17 @@ function TopbarLayout({
|
|
|
1239
1509
|
notificationSlot,
|
|
1240
1510
|
hasBottomNav = false
|
|
1241
1511
|
}) {
|
|
1242
|
-
const [mobileMenuOpen, setMobileMenuOpen] =
|
|
1512
|
+
const [mobileMenuOpen, setMobileMenuOpen] = React7.useState(false);
|
|
1513
|
+
const [headerSlot, setHeaderSlot] = React7.useState(null);
|
|
1514
|
+
const [actionsSlot, setActionsSlot] = React7.useState(null);
|
|
1515
|
+
const [pageRailSlot, setPageRailSlot] = React7.useState(null);
|
|
1243
1516
|
const rawSlot = usePageHeaderSlot();
|
|
1244
1517
|
const hasOutlet = useHasPageHeaderOutlet();
|
|
1518
|
+
const hasPageActions = useHasPageActions();
|
|
1245
1519
|
usePageHeaderHost();
|
|
1246
1520
|
const pageHeaderSlot = hasOutlet ? null : rawSlot;
|
|
1247
1521
|
const showTitle = !hasOutlet && !!pageTitle;
|
|
1248
|
-
return /* @__PURE__ */ jsx(SaveBarProvider, { children: /* @__PURE__ */ jsxs("div", { className: "shell-enter relative flex h-screen flex-col overflow-hidden bg-background", children: [
|
|
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 h-screen flex-col overflow-hidden bg-background", children: [
|
|
1249
1523
|
/* @__PURE__ */ jsx(
|
|
1250
1524
|
Topbar,
|
|
1251
1525
|
{
|
|
@@ -1261,6 +1535,7 @@ function TopbarLayout({
|
|
|
1261
1535
|
userPlan,
|
|
1262
1536
|
billingLabel,
|
|
1263
1537
|
userMenuExtras,
|
|
1538
|
+
userMenuSlot,
|
|
1264
1539
|
orgSwitcher,
|
|
1265
1540
|
leftContent: topbarStart,
|
|
1266
1541
|
rightContent: topbarEnd || notificationSlot ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -1270,9 +1545,18 @@ function TopbarLayout({
|
|
|
1270
1545
|
onMenuClick: hasBottomNav ? void 0 : () => setMobileMenuOpen(true)
|
|
1271
1546
|
}
|
|
1272
1547
|
),
|
|
1273
|
-
/* @__PURE__ */ jsx("main", { className: cn("flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", frame && "md:bg-sidebar md:p-2"), children: /* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 min-h-0 flex-1
|
|
1274
|
-
|
|
1275
|
-
/* @__PURE__ */
|
|
1548
|
+
/* @__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
|
+
/* @__PURE__ */ jsx("div", { ref: setPageRailSlot, className: "flex shrink-0 empty:hidden" }),
|
|
1550
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 min-h-0 flex-1 flex-col overflow-hidden", children: [
|
|
1551
|
+
(pageHeaderSlot || pageBack || hasPageActions) && /* @__PURE__ */ jsxs("div", { className: "flex min-h-12 shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-2", children: [
|
|
1552
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
|
|
1553
|
+
pageHeaderSlot ?? /* @__PURE__ */ jsx(ShellPageTitle, { title: showTitle ? pageTitle : void 0, back: pageBack }),
|
|
1554
|
+
/* @__PURE__ */ jsx("div", { ref: setHeaderSlot, className: "flex min-w-0 flex-1 items-center" })
|
|
1555
|
+
] }),
|
|
1556
|
+
/* @__PURE__ */ jsx("div", { ref: setActionsSlot, className: "flex shrink-0 items-center gap-2" })
|
|
1557
|
+
] }),
|
|
1558
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto pb-16 md:pb-0", children })
|
|
1559
|
+
] })
|
|
1276
1560
|
] }) }),
|
|
1277
1561
|
/* @__PURE__ */ jsx(SaveBar, {}),
|
|
1278
1562
|
mobileMenuOpen && /* @__PURE__ */ jsx(
|
|
@@ -1292,7 +1576,7 @@ function TopbarLayout({
|
|
|
1292
1576
|
onClose: () => setMobileMenuOpen(false)
|
|
1293
1577
|
}
|
|
1294
1578
|
)
|
|
1295
|
-
] }) });
|
|
1579
|
+
] }) }) }) }) });
|
|
1296
1580
|
}
|
|
1297
1581
|
function DrawerUserRow({ user }) {
|
|
1298
1582
|
const label = user.fullName || user.email;
|
|
@@ -1316,23 +1600,23 @@ function MinimalLayout({
|
|
|
1316
1600
|
] });
|
|
1317
1601
|
}
|
|
1318
1602
|
function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
|
|
1319
|
-
const [visible, setVisible] =
|
|
1320
|
-
|
|
1603
|
+
const [visible, setVisible] = React7.useState(false);
|
|
1604
|
+
React7.useEffect(() => {
|
|
1321
1605
|
const id = requestAnimationFrame(() => setVisible(true));
|
|
1322
1606
|
return () => cancelAnimationFrame(id);
|
|
1323
1607
|
}, []);
|
|
1324
|
-
const close =
|
|
1608
|
+
const close = React7.useCallback(() => {
|
|
1325
1609
|
setVisible(false);
|
|
1326
1610
|
window.setTimeout(onClose, 250);
|
|
1327
1611
|
}, [onClose]);
|
|
1328
|
-
|
|
1612
|
+
React7.useEffect(() => {
|
|
1329
1613
|
const previous = document.body.style.overflow;
|
|
1330
1614
|
document.body.style.overflow = "hidden";
|
|
1331
1615
|
return () => {
|
|
1332
1616
|
document.body.style.overflow = previous;
|
|
1333
1617
|
};
|
|
1334
1618
|
}, []);
|
|
1335
|
-
|
|
1619
|
+
React7.useEffect(() => {
|
|
1336
1620
|
const onKey = (e) => {
|
|
1337
1621
|
if (e.key === "Escape") close();
|
|
1338
1622
|
};
|
|
@@ -1363,6 +1647,7 @@ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
|
|
|
1363
1647
|
...sidebarProps,
|
|
1364
1648
|
collapsed: false,
|
|
1365
1649
|
onToggle: close,
|
|
1650
|
+
drawer: true,
|
|
1366
1651
|
borderless: true,
|
|
1367
1652
|
onNavigate: (route) => {
|
|
1368
1653
|
onNavigate?.(route);
|
|
@@ -1376,16 +1661,16 @@ function MobileOverlay({ onClose, onNavigate, ...sidebarProps }) {
|
|
|
1376
1661
|
}
|
|
1377
1662
|
var BOTTOM_NAV_ICONS = {
|
|
1378
1663
|
Home: Home,
|
|
1379
|
-
Receipt
|
|
1380
|
-
ArrowLeftRight
|
|
1381
|
-
Calendar
|
|
1664
|
+
Receipt,
|
|
1665
|
+
ArrowLeftRight,
|
|
1666
|
+
Calendar,
|
|
1382
1667
|
CreditCard: CreditCard,
|
|
1383
1668
|
Settings: Settings,
|
|
1384
1669
|
Menu: Menu,
|
|
1385
|
-
MessageCircle
|
|
1386
|
-
Wallet
|
|
1670
|
+
MessageCircle,
|
|
1671
|
+
Wallet,
|
|
1387
1672
|
PiggyBank,
|
|
1388
|
-
Plus
|
|
1673
|
+
Plus
|
|
1389
1674
|
};
|
|
1390
1675
|
function FloatingProfileAvatar({ user, onClick }) {
|
|
1391
1676
|
if (!user) return null;
|
|
@@ -1472,6 +1757,7 @@ function AppShell({
|
|
|
1472
1757
|
topbarStart,
|
|
1473
1758
|
topbarEnd,
|
|
1474
1759
|
sidebarTopContent,
|
|
1760
|
+
sidebarIdentity,
|
|
1475
1761
|
sidebarNavTop,
|
|
1476
1762
|
sidebarFooterContent,
|
|
1477
1763
|
bottomNav,
|
|
@@ -1521,6 +1807,7 @@ function AppShell({
|
|
|
1521
1807
|
userMenuExtras,
|
|
1522
1808
|
orgSwitcher,
|
|
1523
1809
|
sidebarTopContent,
|
|
1810
|
+
sidebarIdentity,
|
|
1524
1811
|
sidebarNavTop,
|
|
1525
1812
|
sidebarFooterContent,
|
|
1526
1813
|
frame: contentFrame,
|
|
@@ -1539,6 +1826,7 @@ function AppShell({
|
|
|
1539
1826
|
sidebarNavKey,
|
|
1540
1827
|
sidebarNavDirection,
|
|
1541
1828
|
sidebarNavChildConnector,
|
|
1829
|
+
railOpen: !!rightRail && rightRailOpen,
|
|
1542
1830
|
children
|
|
1543
1831
|
}
|
|
1544
1832
|
);
|
|
@@ -1558,6 +1846,7 @@ function AppShell({
|
|
|
1558
1846
|
userPlan,
|
|
1559
1847
|
billingLabel,
|
|
1560
1848
|
userMenuExtras,
|
|
1849
|
+
userMenuSlot,
|
|
1561
1850
|
orgSwitcher,
|
|
1562
1851
|
frame: contentFrame,
|
|
1563
1852
|
currentPath,
|
|
@@ -1608,13 +1897,13 @@ function SplitWithRightRail({
|
|
|
1608
1897
|
maxWidth = 720,
|
|
1609
1898
|
resizeLabel = "Resize panel"
|
|
1610
1899
|
}) {
|
|
1611
|
-
const [width, setWidth] =
|
|
1612
|
-
const [dragging, setDragging] =
|
|
1613
|
-
const clamp =
|
|
1900
|
+
const [width, setWidth] = React7.useState(defaultWidth);
|
|
1901
|
+
const [dragging, setDragging] = React7.useState(false);
|
|
1902
|
+
const clamp = React7.useCallback(
|
|
1614
1903
|
(value) => Math.min(maxWidth, Math.max(minWidth, value)),
|
|
1615
1904
|
[minWidth, maxWidth]
|
|
1616
1905
|
);
|
|
1617
|
-
|
|
1906
|
+
React7.useEffect(() => {
|
|
1618
1907
|
if (!dragging) return;
|
|
1619
1908
|
const onMove = (event) => {
|
|
1620
1909
|
event.preventDefault();
|
|
@@ -1636,7 +1925,7 @@ function SplitWithRightRail({
|
|
|
1636
1925
|
document.body.style.cursor = previousCursor;
|
|
1637
1926
|
};
|
|
1638
1927
|
}, [dragging, clamp]);
|
|
1639
|
-
|
|
1928
|
+
React7.useEffect(() => {
|
|
1640
1929
|
if (dragging) return;
|
|
1641
1930
|
onWidthChange?.(width);
|
|
1642
1931
|
}, [dragging]);
|
|
@@ -1679,7 +1968,11 @@ function SplitWithRightRail({
|
|
|
1679
1968
|
}
|
|
1680
1969
|
},
|
|
1681
1970
|
className: cn(
|
|
1682
|
-
|
|
1971
|
+
// Zero width: the divider USED to be a 4px gutter, which meant the
|
|
1972
|
+
// page and the rail could never touch — there was always a strip of
|
|
1973
|
+
// chrome between them. It now sits exactly on the seam the rail's own
|
|
1974
|
+
// left border draws, and lights that border up when you reach for it.
|
|
1975
|
+
"group relative hidden w-0 shrink-0 cursor-col-resize focus-visible:outline-none",
|
|
1683
1976
|
open && "md:block"
|
|
1684
1977
|
),
|
|
1685
1978
|
children: [
|
|
@@ -1687,7 +1980,7 @@ function SplitWithRightRail({
|
|
|
1687
1980
|
"span",
|
|
1688
1981
|
{
|
|
1689
1982
|
className: cn(
|
|
1690
|
-
"absolute inset-y-6 left-
|
|
1983
|
+
"absolute inset-y-6 -left-px w-0.5 rounded-full bg-transparent transition-colors",
|
|
1691
1984
|
"group-hover:bg-primary/60 group-focus-visible:bg-primary",
|
|
1692
1985
|
dragging && "bg-primary"
|
|
1693
1986
|
)
|
|
@@ -1715,40 +2008,79 @@ function SplitWithRightRail({
|
|
|
1715
2008
|
] })
|
|
1716
2009
|
);
|
|
1717
2010
|
}
|
|
1718
|
-
function
|
|
2011
|
+
function HeaderTabs({
|
|
2012
|
+
tabs,
|
|
2013
|
+
value,
|
|
2014
|
+
onChange,
|
|
2015
|
+
className,
|
|
2016
|
+
fill,
|
|
2017
|
+
...rest
|
|
2018
|
+
}) {
|
|
1719
2019
|
return /* @__PURE__ */ jsx(
|
|
1720
|
-
"
|
|
2020
|
+
"div",
|
|
1721
2021
|
{
|
|
1722
|
-
"
|
|
2022
|
+
role: "tablist",
|
|
2023
|
+
"aria-label": rest["aria-label"],
|
|
1723
2024
|
className: cn(
|
|
1724
|
-
"
|
|
1725
|
-
"
|
|
2025
|
+
"inline-flex max-w-full gap-5 overflow-x-auto scrollbar-none",
|
|
2026
|
+
fill ? "items-stretch" : "items-center",
|
|
1726
2027
|
className
|
|
1727
|
-
)
|
|
2028
|
+
),
|
|
2029
|
+
children: tabs.map((tab) => {
|
|
2030
|
+
const active = tab.value === value;
|
|
2031
|
+
const Icon = tab.icon;
|
|
2032
|
+
return /* @__PURE__ */ jsxs(
|
|
2033
|
+
"button",
|
|
2034
|
+
{
|
|
2035
|
+
type: "button",
|
|
2036
|
+
role: "tab",
|
|
2037
|
+
"aria-selected": active,
|
|
2038
|
+
onClick: () => onChange(tab.value),
|
|
2039
|
+
className: cn(
|
|
2040
|
+
"relative flex shrink-0 items-center gap-1.5 whitespace-nowrap text-sm transition-colors",
|
|
2041
|
+
// Filled: the row owns the band's height and the underline sits on
|
|
2042
|
+
// its bottom edge. Otherwise the underline trails the label by 6px.
|
|
2043
|
+
fill ? "h-full" : "pb-1.5",
|
|
2044
|
+
active ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
2045
|
+
),
|
|
2046
|
+
children: [
|
|
2047
|
+
Icon ? /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0" }) : null,
|
|
2048
|
+
/* @__PURE__ */ jsx("span", { children: tab.label }),
|
|
2049
|
+
tab.count != null && /* @__PURE__ */ jsx("span", { className: cn(
|
|
2050
|
+
"ml-0.5 rounded-full px-1.5 text-[11px] tabular-nums",
|
|
2051
|
+
active ? "bg-muted text-foreground" : "bg-muted/70 text-muted-foreground"
|
|
2052
|
+
), children: tab.count }),
|
|
2053
|
+
tab.badge,
|
|
2054
|
+
active && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 h-0.5 rounded-full bg-foreground" })
|
|
2055
|
+
]
|
|
2056
|
+
},
|
|
2057
|
+
tab.value
|
|
2058
|
+
);
|
|
2059
|
+
})
|
|
1728
2060
|
}
|
|
1729
2061
|
);
|
|
1730
2062
|
}
|
|
1731
|
-
function
|
|
2063
|
+
function PageTabs({ actions, className, ...props }) {
|
|
1732
2064
|
return /* @__PURE__ */ jsxs(
|
|
1733
2065
|
"div",
|
|
1734
2066
|
{
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
2067
|
+
className: cn(
|
|
2068
|
+
"-mx-3 -mt-3 mb-4 flex flex-wrap items-end justify-between gap-3 border-b border-border px-3 pt-2 md:-mx-6 md:-mt-4 md:px-6",
|
|
2069
|
+
className
|
|
2070
|
+
),
|
|
1738
2071
|
children: [
|
|
1739
|
-
/* @__PURE__ */ jsx(
|
|
1740
|
-
/* @__PURE__ */ jsx(
|
|
2072
|
+
/* @__PURE__ */ jsx(HeaderTabs, { ...props, className: "-mb-px" }),
|
|
2073
|
+
actions && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2 pb-1.5", children: actions })
|
|
1741
2074
|
]
|
|
1742
2075
|
}
|
|
1743
2076
|
);
|
|
1744
2077
|
}
|
|
1745
|
-
|
|
1746
|
-
var NavTransitionContext = React.createContext("slide");
|
|
2078
|
+
var NavTransitionContext = React7.createContext("slide");
|
|
1747
2079
|
function NavTransitionProvider({ value, children }) {
|
|
1748
2080
|
return /* @__PURE__ */ jsx(NavTransitionContext.Provider, { value, children });
|
|
1749
2081
|
}
|
|
1750
2082
|
function useNavTransition() {
|
|
1751
|
-
return
|
|
2083
|
+
return React7.useContext(NavTransitionContext);
|
|
1752
2084
|
}
|
|
1753
2085
|
function resolveTransitionClass(transition, direction) {
|
|
1754
2086
|
if (transition === "none") return "";
|
|
@@ -1767,8 +2099,14 @@ function PageTransition({
|
|
|
1767
2099
|
const animClass = resolveTransitionClass(transition, direction);
|
|
1768
2100
|
return /* @__PURE__ */ jsx("div", { className: cn(animClass, className), children }, transitionKey);
|
|
1769
2101
|
}
|
|
2102
|
+
function PageRail({ children }) {
|
|
2103
|
+
const slot = usePageRailSlot();
|
|
2104
|
+
if (slot) return createPortal(children, slot);
|
|
2105
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
2106
|
+
}
|
|
1770
2107
|
function PageHeaderActions({ children }) {
|
|
1771
2108
|
const slot = useModuleHeaderActionsSlot();
|
|
2109
|
+
useRegisterPageActions();
|
|
1772
2110
|
if (slot) return createPortal(children, slot);
|
|
1773
2111
|
return /* @__PURE__ */ jsx("div", { className: "mb-4 flex items-center justify-end gap-2", children });
|
|
1774
2112
|
}
|
|
@@ -1777,16 +2115,16 @@ function PremiumBadge({ className }) {
|
|
|
1777
2115
|
const tip = t("upgrade.badgeTooltip");
|
|
1778
2116
|
return /* @__PURE__ */ jsx("span", { title: tip, "aria-label": tip, className: cn("inline-flex shrink-0", className), children: /* @__PURE__ */ jsx(Crown, { className: "h-3.5 w-3.5 text-amber-500", "aria-hidden": true }) });
|
|
1779
2117
|
}
|
|
1780
|
-
var ModuleLayoutContext =
|
|
2118
|
+
var ModuleLayoutContext = React7.createContext("rail");
|
|
1781
2119
|
function ModuleLayoutProvider({ variant, children }) {
|
|
1782
2120
|
return /* @__PURE__ */ jsx(ModuleLayoutContext.Provider, { value: variant, children });
|
|
1783
2121
|
}
|
|
1784
2122
|
function useModuleLayout() {
|
|
1785
|
-
return
|
|
2123
|
+
return React7.useContext(ModuleLayoutContext);
|
|
1786
2124
|
}
|
|
1787
|
-
function
|
|
1788
|
-
const [expanded, setExpanded] =
|
|
1789
|
-
const Icon = item.icon ?
|
|
2125
|
+
function RailNavItem({ item }) {
|
|
2126
|
+
const [expanded, setExpanded] = React7.useState(true);
|
|
2127
|
+
const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
|
|
1790
2128
|
const hasChildren = item.children && item.children.length > 0;
|
|
1791
2129
|
const hasActiveChild = hasChildren && item.children.some((c) => c.active);
|
|
1792
2130
|
const isActive = item.active || hasActiveChild;
|
|
@@ -1827,9 +2165,9 @@ function NavItem2({ item }) {
|
|
|
1827
2165
|
] });
|
|
1828
2166
|
}
|
|
1829
2167
|
function MobileTabs({ nav }) {
|
|
1830
|
-
const scrollRef =
|
|
1831
|
-
const [expandedId, setExpandedId] =
|
|
1832
|
-
|
|
2168
|
+
const scrollRef = React7.useRef(null);
|
|
2169
|
+
const [expandedId, setExpandedId] = React7.useState(null);
|
|
2170
|
+
React7.useEffect(() => {
|
|
1833
2171
|
const container = scrollRef.current;
|
|
1834
2172
|
if (!container) return;
|
|
1835
2173
|
const activeEl = container.querySelector('[data-active="true"]');
|
|
@@ -1840,7 +2178,7 @@ function MobileTabs({ nav }) {
|
|
|
1840
2178
|
}, [nav]);
|
|
1841
2179
|
return /* @__PURE__ */ jsxs("div", { className: "md:hidden -mx-1 mb-4 no-print", children: [
|
|
1842
2180
|
/* @__PURE__ */ jsx("div", { ref: scrollRef, className: "flex gap-1 px-1 pb-2 overflow-x-auto scrollbar-none", children: nav.map((item) => {
|
|
1843
|
-
const Icon = item.icon ?
|
|
2181
|
+
const Icon = item.icon ? ICON_MAP[item.icon] ?? null : null;
|
|
1844
2182
|
const hasChildren = item.children && item.children.length > 0;
|
|
1845
2183
|
const isActive = item.active || item.children?.some((c) => c.active);
|
|
1846
2184
|
const isExpanded = expandedId === item.id;
|
|
@@ -1896,12 +2234,12 @@ function MobileTabs({ nav }) {
|
|
|
1896
2234
|
})()
|
|
1897
2235
|
] });
|
|
1898
2236
|
}
|
|
1899
|
-
var BackStyleContext =
|
|
2237
|
+
var BackStyleContext = React7.createContext("link");
|
|
1900
2238
|
function BackStyleProvider({ style, children }) {
|
|
1901
2239
|
return /* @__PURE__ */ jsx(BackStyleContext.Provider, { value: style, children });
|
|
1902
2240
|
}
|
|
1903
2241
|
function useBackStyle() {
|
|
1904
|
-
return
|
|
2242
|
+
return React7.useContext(BackStyleContext);
|
|
1905
2243
|
}
|
|
1906
2244
|
function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, backStyle, inline }) {
|
|
1907
2245
|
const t = useTranslation();
|
|
@@ -1911,7 +2249,7 @@ function SubpageHeader({ title, subtitle, icon, onBack, parentLabel, actions, ba
|
|
|
1911
2249
|
const effectiveOnBack = inline ? onBack : cross.onBack;
|
|
1912
2250
|
const effectiveParentLabel = inline ? parentLabel : cross.parentLabel;
|
|
1913
2251
|
useBackHandler(effectiveOnBack);
|
|
1914
|
-
const Icon = icon ?
|
|
2252
|
+
const Icon = icon ? ICON_MAP[icon] ?? null : null;
|
|
1915
2253
|
if (inline && effectiveOnBack) {
|
|
1916
2254
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1917
2255
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
|
|
@@ -1997,13 +2335,11 @@ function ShellSubpageHeader({
|
|
|
1997
2335
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
1998
2336
|
Icon && !onBack && /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
1999
2337
|
/* @__PURE__ */ jsx(PageHeaderCrumb, { title, parentLabel, onBack })
|
|
2000
|
-
] })
|
|
2338
|
+
] }),
|
|
2339
|
+
{ back: !!onBack }
|
|
2001
2340
|
);
|
|
2002
|
-
if (!
|
|
2003
|
-
return /* @__PURE__ */
|
|
2004
|
-
subtitle ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle }) : /* @__PURE__ */ jsx("span", {}),
|
|
2005
|
-
actions && /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions })
|
|
2006
|
-
] });
|
|
2341
|
+
if (!actions) return null;
|
|
2342
|
+
return /* @__PURE__ */ jsx(PageHeaderActions, { children: /* @__PURE__ */ jsx("div", { className: "no-print flex shrink-0 items-center gap-2", children: actions }) });
|
|
2007
2343
|
}
|
|
2008
2344
|
function ModuleHeader({ nav }) {
|
|
2009
2345
|
function defaultChild(item) {
|
|
@@ -2014,7 +2350,7 @@ function ModuleHeader({ nav }) {
|
|
|
2014
2350
|
const tabs = nav.map((item) => ({
|
|
2015
2351
|
value: item.id,
|
|
2016
2352
|
label: item.label,
|
|
2017
|
-
icon: item.icon ?
|
|
2353
|
+
icon: item.icon ? ICON_MAP[item.icon] ?? void 0 : void 0,
|
|
2018
2354
|
// Plan-gated views keep their upgrade marker — dropping it would hide the
|
|
2019
2355
|
// only signal that the tab costs an upgrade.
|
|
2020
2356
|
badge: item.premium ? /* @__PURE__ */ jsx(PremiumBadge, {}) : void 0
|
|
@@ -2026,12 +2362,13 @@ function ModuleHeader({ nav }) {
|
|
|
2026
2362
|
if (hasChildren) defaultChild(item)?.onClick?.();
|
|
2027
2363
|
else item.onClick?.();
|
|
2028
2364
|
};
|
|
2029
|
-
return /* @__PURE__ */ jsx(
|
|
2365
|
+
return /* @__PURE__ */ jsx(PageTabs, { tabs, value: activeItem?.id ?? nav[0]?.id ?? "", onChange });
|
|
2030
2366
|
}
|
|
2031
|
-
function ModulePage({ title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
|
|
2367
|
+
function ModulePage({ title: _title, subtitle, nav, children, className, headerAction, showHeader = true, sidebarFooter, navVariant, viewKey, direction }) {
|
|
2032
2368
|
const ctxVariant = useModuleLayout();
|
|
2033
2369
|
const variant = navVariant ?? ctxVariant;
|
|
2034
2370
|
const transitionKey = viewKey ?? "view";
|
|
2371
|
+
const onSubpage = useHasPageHeaderBack();
|
|
2035
2372
|
if (variant === "sidebar") {
|
|
2036
2373
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2037
2374
|
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
@@ -2040,148 +2377,24 @@ function ModulePage({ title, subtitle, nav, children, className, headerAction, s
|
|
|
2040
2377
|
}
|
|
2041
2378
|
if (variant === "tabs") {
|
|
2042
2379
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2043
|
-
/* @__PURE__ */ jsx(ModuleHeader, { nav }),
|
|
2380
|
+
!onSubpage && /* @__PURE__ */ jsx(ModuleHeader, { nav }),
|
|
2044
2381
|
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
2045
2382
|
/* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, className: cn("min-w-0", className), children })
|
|
2046
2383
|
] });
|
|
2047
2384
|
}
|
|
2048
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("
|
|
2049
|
-
/* @__PURE__ */ jsx(
|
|
2050
|
-
/* @__PURE__ */
|
|
2051
|
-
|
|
2052
|
-
/* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(NavItem2, { item }, item.id)) })
|
|
2053
|
-
] }),
|
|
2054
|
-
sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto", children: sidebarFooter })
|
|
2385
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("min-w-0", className), children: [
|
|
2386
|
+
/* @__PURE__ */ jsx(PageRail, { children: /* @__PURE__ */ jsxs("div", { className: "hidden w-48 shrink-0 flex-col overflow-y-auto border-r border-border p-3 no-print md:flex", children: [
|
|
2387
|
+
/* @__PURE__ */ jsx("nav", { className: "space-y-0.5", children: nav.map((item) => /* @__PURE__ */ jsx(RailNavItem, { item }, item.id)) }),
|
|
2388
|
+
sidebarFooter && /* @__PURE__ */ jsx("div", { className: "mt-auto pt-3", children: sidebarFooter })
|
|
2055
2389
|
] }) }),
|
|
2056
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
2057
|
-
/* @__PURE__ */ jsx(
|
|
2058
|
-
showHeader && /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-start justify-between gap-3", children: [
|
|
2059
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
2060
|
-
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: title }),
|
|
2061
|
-
subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle })
|
|
2062
|
-
] }),
|
|
2063
|
-
headerAction
|
|
2064
|
-
] }),
|
|
2390
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2391
|
+
showHeader && headerAction && /* @__PURE__ */ jsx(PageHeaderActions, { children: headerAction }),
|
|
2065
2392
|
/* @__PURE__ */ jsx(MobileTabs, { nav }),
|
|
2066
2393
|
/* @__PURE__ */ jsx(PageTransition, { transitionKey, direction, children })
|
|
2067
2394
|
] })
|
|
2068
2395
|
] });
|
|
2069
2396
|
}
|
|
2070
|
-
var EMPTY = [];
|
|
2071
|
-
var PageTrailContext = React.createContext(EMPTY);
|
|
2072
|
-
function PageTrail({
|
|
2073
|
-
label,
|
|
2074
|
-
href,
|
|
2075
|
-
onNavigate,
|
|
2076
|
-
children
|
|
2077
|
-
}) {
|
|
2078
|
-
const parent = React.useContext(PageTrailContext);
|
|
2079
|
-
const trail = React.useMemo(
|
|
2080
|
-
() => [...parent, { label, href, onNavigate }],
|
|
2081
|
-
[parent, label, href, onNavigate]
|
|
2082
|
-
);
|
|
2083
|
-
return /* @__PURE__ */ jsx(PageTrailContext.Provider, { value: trail, children });
|
|
2084
|
-
}
|
|
2085
|
-
function usePageTrail() {
|
|
2086
|
-
return React.useContext(PageTrailContext);
|
|
2087
|
-
}
|
|
2088
|
-
function usePageTrailParent(href) {
|
|
2089
|
-
const trail = React.useContext(PageTrailContext);
|
|
2090
|
-
return React.useMemo(() => {
|
|
2091
|
-
const above = href ? trail.filter((c) => c.href !== href) : trail;
|
|
2092
|
-
return above[above.length - 1];
|
|
2093
|
-
}, [trail, href]);
|
|
2094
|
-
}
|
|
2095
|
-
function navigateToCrumb(crumb) {
|
|
2096
|
-
if (crumb.onNavigate) crumb.onNavigate();
|
|
2097
|
-
else window.location.hash = crumb.href;
|
|
2098
|
-
}
|
|
2099
|
-
var ContentSplitContext = React.createContext(null);
|
|
2100
|
-
function ContentSplit({
|
|
2101
|
-
children,
|
|
2102
|
-
aside,
|
|
2103
|
-
asideTitle,
|
|
2104
|
-
asideDescription,
|
|
2105
|
-
asideWidth = "20rem",
|
|
2106
|
-
asideOpen,
|
|
2107
|
-
onAsideOpenChange,
|
|
2108
|
-
collapsible = false,
|
|
2109
|
-
defaultCollapsed = false,
|
|
2110
|
-
className
|
|
2111
|
-
}) {
|
|
2112
|
-
const [internalOpen, setInternalOpen] = React.useState(false);
|
|
2113
|
-
const open = asideOpen ?? internalOpen;
|
|
2114
|
-
const setOpen = onAsideOpenChange ?? setInternalOpen;
|
|
2115
|
-
const [collapsed, setCollapsed] = React.useState(collapsible && defaultCollapsed);
|
|
2116
|
-
return /* @__PURE__ */ jsxs(ContentSplitContext.Provider, { value: { open, setOpen }, children: [
|
|
2117
|
-
/* @__PURE__ */ jsxs(
|
|
2118
|
-
"div",
|
|
2119
|
-
{
|
|
2120
|
-
className: cn("lg:grid lg:items-start lg:gap-6", className),
|
|
2121
|
-
style: { gridTemplateColumns: `minmax(0, 1fr) ${collapsed ? "auto" : asideWidth}` },
|
|
2122
|
-
children: [
|
|
2123
|
-
/* @__PURE__ */ jsx("div", { className: "min-w-0", children }),
|
|
2124
|
-
/* @__PURE__ */ jsx("aside", { className: "hidden lg:block lg:sticky lg:top-4", children: collapsed ? /* @__PURE__ */ jsx(
|
|
2125
|
-
Button,
|
|
2126
|
-
{
|
|
2127
|
-
variant: "outline",
|
|
2128
|
-
size: "sm",
|
|
2129
|
-
onClick: () => setCollapsed(false),
|
|
2130
|
-
className: "h-9 w-9 p-0",
|
|
2131
|
-
"aria-label": typeof asideTitle === "string" ? asideTitle : void 0,
|
|
2132
|
-
title: typeof asideTitle === "string" ? asideTitle : void 0,
|
|
2133
|
-
children: /* @__PURE__ */ jsx(PanelRightOpen, { className: "h-4 w-4" })
|
|
2134
|
-
}
|
|
2135
|
-
) : /* @__PURE__ */ jsxs("div", { className: "rounded-card border border-border bg-card", children: [
|
|
2136
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 border-b border-border px-4 py-3", children: [
|
|
2137
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2138
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-foreground", children: asideTitle }),
|
|
2139
|
-
asideDescription && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-muted-foreground", children: asideDescription })
|
|
2140
|
-
] }),
|
|
2141
|
-
collapsible && /* @__PURE__ */ jsx(
|
|
2142
|
-
"button",
|
|
2143
|
-
{
|
|
2144
|
-
type: "button",
|
|
2145
|
-
onClick: () => setCollapsed(true),
|
|
2146
|
-
className: "rounded p-1 text-muted-foreground transition-colors hover:bg-muted/60 hover:text-foreground",
|
|
2147
|
-
"aria-label": "collapse",
|
|
2148
|
-
children: /* @__PURE__ */ jsx(PanelRightClose, { className: "h-4 w-4" })
|
|
2149
|
-
}
|
|
2150
|
-
)
|
|
2151
|
-
] }),
|
|
2152
|
-
/* @__PURE__ */ jsx("div", { className: "p-4", children: aside })
|
|
2153
|
-
] }) })
|
|
2154
|
-
]
|
|
2155
|
-
}
|
|
2156
|
-
),
|
|
2157
|
-
/* @__PURE__ */ jsx("div", { className: "lg:hidden", children: /* @__PURE__ */ jsx(Sheet, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(SheetContent, { width: "max-w-sm", children: [
|
|
2158
|
-
/* @__PURE__ */ jsxs(SheetHeader, { children: [
|
|
2159
|
-
/* @__PURE__ */ jsx(SheetTitle, { children: asideTitle }),
|
|
2160
|
-
asideDescription && /* @__PURE__ */ jsx(SheetDescription, { children: asideDescription })
|
|
2161
|
-
] }),
|
|
2162
|
-
/* @__PURE__ */ jsx(SheetBody, { children: aside })
|
|
2163
|
-
] }) }) })
|
|
2164
|
-
] });
|
|
2165
|
-
}
|
|
2166
|
-
function ContentSplitTrigger({ label, iconOnly, className }) {
|
|
2167
|
-
const ctx = React.useContext(ContentSplitContext);
|
|
2168
|
-
if (!ctx) return null;
|
|
2169
|
-
return /* @__PURE__ */ jsxs(
|
|
2170
|
-
Button,
|
|
2171
|
-
{
|
|
2172
|
-
variant: "outline",
|
|
2173
|
-
size: "sm",
|
|
2174
|
-
onClick: () => ctx.setOpen(true),
|
|
2175
|
-
className: cn("lg:hidden", className),
|
|
2176
|
-
"aria-label": typeof label === "string" ? label : void 0,
|
|
2177
|
-
children: [
|
|
2178
|
-
/* @__PURE__ */ jsx(PanelRight, { className: cn("h-4 w-4", !iconOnly && "mr-1.5") }),
|
|
2179
|
-
!iconOnly && label
|
|
2180
|
-
]
|
|
2181
|
-
}
|
|
2182
|
-
);
|
|
2183
|
-
}
|
|
2184
2397
|
|
|
2185
|
-
export { AppShell,
|
|
2186
|
-
//# sourceMappingURL=chunk-
|
|
2187
|
-
//# sourceMappingURL=chunk-
|
|
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-NA3GE4H3.js.map
|
|
2400
|
+
//# sourceMappingURL=chunk-NA3GE4H3.js.map
|