@marktiderman/genesis-ui 2.11.0 → 2.12.1
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/SKILL.md +57 -0
- package/dist/AppShell-D5nD0mcL.d.ts +348 -0
- package/dist/chunk-424PT5DM.js +21 -0
- package/dist/chunk-424PT5DM.js.map +1 -0
- package/dist/{chunk-UYI3GLFJ.js → chunk-6N6MUN3T.js} +722 -159
- package/dist/chunk-6N6MUN3T.js.map +1 -0
- package/dist/chunk-J45FHL7D.js +46 -0
- package/dist/chunk-J45FHL7D.js.map +1 -0
- package/dist/{chunk-OITWCIG2.js → chunk-PMCUHSZS.js} +3 -3
- package/dist/{chunk-OITWCIG2.js.map → chunk-PMCUHSZS.js.map} +1 -1
- package/dist/{chunk-5GOQ72WB.js → chunk-STZVZUYO.js} +3 -3
- package/dist/{chunk-5GOQ72WB.js.map → chunk-STZVZUYO.js.map} +1 -1
- package/dist/chunk-WALJMVEF.js +10 -0
- package/dist/chunk-WALJMVEF.js.map +1 -0
- package/dist/components/data/index.d.ts +7 -6
- package/dist/components/data/index.js +15 -13
- package/dist/components/data/index.js.map +1 -1
- package/dist/components/layout/dashboard-page.js +1 -0
- package/dist/components/layout/detail-page.js +1 -0
- package/dist/components/layout/form-page.js +1 -0
- package/dist/components/layout/index.d.ts +3 -2
- package/dist/components/layout/index.js +4 -2
- package/dist/components/layout/settings-page.js +1 -0
- package/dist/components/patterns/empty-state.js +1 -0
- package/dist/components/patterns/form-field.js +1 -0
- package/dist/components/patterns/index.js +1 -0
- package/dist/components/patterns/page-loading.js +1 -0
- package/dist/components/patterns/settings-row.js +1 -0
- package/dist/components/patterns/sortable-list.js +1 -0
- package/dist/components/patterns/status-badge.js +1 -0
- package/dist/components/patterns/toggle-row.js +1 -0
- package/dist/components/patterns/user-avatar.js +1 -0
- package/dist/components/rich-text/rich-text-editor.js +2 -1
- package/dist/components/rich-text/rich-text-editor.js.map +1 -1
- package/dist/components/rich-text/rich-text.js +1 -0
- package/dist/components/sheet-grid/sheet-grid.js +1 -0
- package/dist/components/sheet-grid/sheet-grid.js.map +1 -1
- package/dist/components/ui/accessible-icon.js +1 -0
- package/dist/components/ui/accordion.js +1 -0
- package/dist/components/ui/alert-dialog.js +1 -0
- package/dist/components/ui/alert.js +1 -0
- package/dist/components/ui/aspect-ratio.js +1 -0
- package/dist/components/ui/avatar.js +1 -0
- package/dist/components/ui/badge.js +1 -0
- package/dist/components/ui/breadcrumb.js +1 -0
- package/dist/components/ui/button-group.js +1 -0
- package/dist/components/ui/button.js +1 -0
- package/dist/components/ui/calendar.js +1 -0
- package/dist/components/ui/card.js +1 -0
- package/dist/components/ui/carousel.js +1 -0
- package/dist/components/ui/chart.js +1 -0
- package/dist/components/ui/checkbox.js +1 -0
- package/dist/components/ui/collapsible.js +1 -0
- package/dist/components/ui/command-palette.js +1 -0
- package/dist/components/ui/command.js +1 -0
- package/dist/components/ui/context-menu.js +1 -0
- package/dist/components/ui/date-picker.js +1 -0
- package/dist/components/ui/dialog.js +1 -0
- package/dist/components/ui/drawer.js +1 -0
- package/dist/components/ui/dropdown-menu.js +1 -0
- package/dist/components/ui/hover-card.js +1 -0
- package/dist/components/ui/input-group.js +1 -0
- package/dist/components/ui/input-otp.js +1 -0
- package/dist/components/ui/input.js +1 -0
- package/dist/components/ui/kbd.js +1 -0
- package/dist/components/ui/label.js +1 -0
- package/dist/components/ui/menubar.js +1 -0
- package/dist/components/ui/navigation-menu.js +1 -0
- package/dist/components/ui/pagination.js +1 -0
- package/dist/components/ui/password-toggle-field.js +1 -0
- package/dist/components/ui/popover.js +1 -0
- package/dist/components/ui/progress.js +1 -0
- package/dist/components/ui/radio-group.js +1 -0
- package/dist/components/ui/resizable.js +1 -0
- package/dist/components/ui/scroll-area.js +1 -0
- package/dist/components/ui/select.js +1 -0
- package/dist/components/ui/separator.js +1 -0
- package/dist/components/ui/sheet.js +1 -0
- package/dist/components/ui/skeleton.js +1 -0
- package/dist/components/ui/slider.js +1 -0
- package/dist/components/ui/sonner.js +1 -0
- package/dist/components/ui/spinner.js +1 -0
- package/dist/components/ui/switch.js +1 -0
- package/dist/components/ui/table.js +1 -0
- package/dist/components/ui/tabs.js +1 -0
- package/dist/components/ui/textarea.js +1 -0
- package/dist/components/ui/toggle-group.js +1 -0
- package/dist/components/ui/toggle.js +1 -0
- package/dist/components/ui/toolbar.js +1 -0
- package/dist/components/ui/tooltip.js +1 -0
- package/dist/components/ui/typography.js +1 -0
- package/dist/components/ui/visually-hidden.js +1 -0
- package/dist/hooks/index.d.ts +3 -3
- package/dist/hooks/index.js +4 -3
- package/dist/hooks/use-data-filters.d.ts +1 -1
- package/dist/hooks/use-data-filters.js +3 -2
- package/dist/hooks/use-keyboard-navigation.js +1 -0
- package/dist/hooks/use-mobile.js +1 -0
- package/dist/hooks/use-prefetch.js +1 -0
- package/dist/hooks/use-resource-page.js +1 -0
- package/dist/hooks/use-resource-url-state.d.ts +1 -1
- package/dist/hooks/use-resource-url-state.js +1 -0
- package/dist/hooks/use-saved-views.js +1 -0
- package/dist/hooks/use-view-preference.js +1 -0
- package/dist/hooks/use-view-settings.js +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +361 -3
- package/dist/index.js.map +1 -1
- package/dist/navigation-vmFJ6r1m.d.ts +90 -0
- package/dist/router/react-router.d.ts +1 -1
- package/dist/router/react-router.js +1 -0
- package/dist/router/react-router.js.map +1 -1
- package/dist/router/tanstack-router.d.ts +1 -1
- package/dist/router/tanstack-router.js +2 -1
- package/dist/router/tanstack-router.js.map +1 -1
- package/dist/status-colors.js +2 -0
- package/dist/status-colors.js.map +1 -1
- package/dist/{use-resource-table-Dx1OZRne.d.ts → use-resource-table-easmRDQI.d.ts} +2 -2
- package/dist/utils.js +1 -0
- package/package.json +6 -5
- package/dist/AppShell-BVr4V0UK.d.ts +0 -174
- package/dist/chunk-7S7LKQBD.js +0 -12
- package/dist/chunk-7S7LKQBD.js.map +0 -1
- package/dist/chunk-UYI3GLFJ.js.map +0 -1
- package/dist/chunk-WGMEVX3Y.js +0 -141
- package/dist/chunk-WGMEVX3Y.js.map +0 -1
- package/dist/navigation-Bha0R3Fu.d.ts +0 -166
- package/dist/slots-tR0wf89f.d.ts +0 -60
package/SKILL.md
CHANGED
|
@@ -327,6 +327,63 @@ const navigation: NavigationNode<NavItem> = {
|
|
|
327
327
|
linkComponent={RouterLink}>{children}</AppShell>
|
|
328
328
|
```
|
|
329
329
|
|
|
330
|
+
**Collapsible sidebar.** On desktop the sidebar collapses to an icon rail (56px;
|
|
331
|
+
`options.collapsedRailWidth: 64` for the wider one): the logo mark, one icon per item (name in a
|
|
332
|
+
tooltip on hover and focus), nested groups in a popover, and the expand button at the bottom.
|
|
333
|
+
The panel button beside the logo toggles it; `options.collapseShortcut: "["` adds an opt-in key
|
|
334
|
+
(skipped while typing, in a listbox/combobox/menu, or with a dialog open). Pass
|
|
335
|
+
`optionsStorageKey` (app-scoped, e.g. `"acme:app-shell"`) to remember the user's choice under
|
|
336
|
+
`${key}:collapsed`. A page asks to open collapsed with `defaultCollapsed: true` on its
|
|
337
|
+
`NavigationNode` (the nearest node in the trail that sets it wins) or `defaultCollapsed` on
|
|
338
|
+
`AppShell`; the user's own toggle always wins once they have used it, and "Reset to defaults" in
|
|
339
|
+
the sidebar options hands it back. `options.defaultCollapsed` is the app-wide default, and
|
|
340
|
+
`collapsed` +
|
|
341
|
+
`onCollapsedChange` make it controlled. The mobile drawer is unaffected.
|
|
342
|
+
|
|
343
|
+
```tsx
|
|
344
|
+
{ id: "builder", path: "/forms/$formId/edit", label: "Form builder", items: [], defaultCollapsed: true }
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Sub-navigation (a page's children).** A node's `items` are the current page's child items: a
|
|
348
|
+
form's Questions / Logic and endings / Responses / Settings. A node's items are `NavItem`s,
|
|
349
|
+
so each needs an `icon`. The hook (`useAppShellSubNav`) and the exported `SubNavTabs` take
|
|
350
|
+
`SubNavItem`s instead: a `NavItem` whose `icon` is optional. Without one, the sidebar and mobile
|
|
351
|
+
navigation show the label's first letter, and `SubNavTabs` shows the label alone. Each has `label`, `to` (a route, or a `?search` / `#hash`; active state follows the URL,
|
|
352
|
+
most specific sibling wins) and an optional `badge` (a count or short status, shown as a chip). Gate by role the same way as other items: filter the list before handing
|
|
353
|
+
it over.
|
|
354
|
+
For live data (badge counts) the page calls `useAppShellSubNav(items, { mode? })`, which
|
|
355
|
+
replaces the node's items while it is mounted, and puts a host chip at the end of the tab row with
|
|
356
|
+
`<AppShellSubNavEnd>…</AppShellSubNavEnd>`. The page's resource header (breadcrumb, title,
|
|
357
|
+
status, actions) goes in `<AppShellPageHeader>…</AppShellPageHeader>`, which the shell renders
|
|
358
|
+
above the tab row (on phones too; at the top of the content when there is no row). Both are
|
|
359
|
+
portals; when a layout and its page both mount one, the page's (innermost) renders, and the same
|
|
360
|
+
rule picks between `useAppShellSubNav` callers. Shell sub-navigation is opt-in through
|
|
361
|
+
`options.subNav`:
|
|
362
|
+
|
|
363
|
+
- `"section"` (default): exactly the pre-sub-nav shell. The children sit in a labelled section
|
|
364
|
+
below the global sidebar items and replace the phone bar, with no back item.
|
|
365
|
+
- `"tabs"`: one row of navigation links across the top of the content, badges as chips, the
|
|
366
|
+
current tab underlined in the brand colour, `aria-current="page"`.
|
|
367
|
+
- `"sidebar"`: nested under the active global item ("Forms"); a flyout from it on the rail.
|
|
368
|
+
- `"off"`: nothing, desktop or phone; draw your own (`SubNavTabs` is exported for that).
|
|
369
|
+
|
|
370
|
+
With `"tabs"` or `"sidebar"`, a page with children swaps the phone bar to a back item to the
|
|
371
|
+
logical parent ("Forms") plus the children and their badges; leaving the page brings the main
|
|
372
|
+
bar back. A node (`subNav` on `NavigationNode`, nearest in the trail wins), the `subNav` prop and
|
|
373
|
+
the page's hook override the option, in that order of strength (hook strongest), so one subtree
|
|
374
|
+
can opt in. With it on, a page's children also win over an app-wide `mobileNavItems` on that page;
|
|
375
|
+
in `"section"` / `"off"`, or on pages without children, `mobileNavItems` applies as before.
|
|
376
|
+
|
|
377
|
+
```tsx
|
|
378
|
+
useAppShellSubNav([
|
|
379
|
+
{ to: `/forms/${id}/edit`, label: "Questions", icon: ListChecks, badge: questions.length, end: true },
|
|
380
|
+
{ to: `/forms/${id}/edit/logic`, label: "Logic and endings", icon: GitBranch, badge: rules.length },
|
|
381
|
+
{ to: `/forms/${id}/edit/responses`, label: "Responses", icon: Inbox, badge: responseCount },
|
|
382
|
+
{ to: `/forms/${id}/edit/settings`, label: "Settings", icon: Settings },
|
|
383
|
+
]);
|
|
384
|
+
<AppShellSubNavEnd><Badge variant="success">Ready to publish</Badge></AppShellSubNavEnd>
|
|
385
|
+
```
|
|
386
|
+
|
|
330
387
|
For chrome outside AppShell, call the same resolver: `resolveNavigation(tree, pathname)` (from
|
|
331
388
|
`/layout`, `/data` or the root barrel) returns `{ current, parent, items, trail, params }` with
|
|
332
389
|
every path interpolated from the URL's params.
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { LucideIcon } from 'lucide-react';
|
|
4
|
+
import { L as LinkComponent } from './navigation-vmFJ6r1m.js';
|
|
5
|
+
import { NavigationNode } from '@marktiderman/genesis-core/navigation';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* How AppShell shows the current page's child items. Sub-navigation is
|
|
9
|
+
* opt-in: the default, `section`, is the shell as it always was.
|
|
10
|
+
* - `section` (default): a labelled section below the global sidebar
|
|
11
|
+
* items; on phones the children replace the bottom bar (no back item).
|
|
12
|
+
* - `tabs`: one row of tabs across the top of the page content.
|
|
13
|
+
* - `sidebar`: nested under the active item in the sidebar; a flyout from
|
|
14
|
+
* that item on the collapsed rail.
|
|
15
|
+
* - `off`: nothing, on desktop or phones; the app draws its own
|
|
16
|
+
* (`SubNavTabs` is exported for it).
|
|
17
|
+
*
|
|
18
|
+
* With `tabs` or `sidebar`, phones switch the bottom bar to a back item to
|
|
19
|
+
* the logical parent plus the children, with their badges.
|
|
20
|
+
*
|
|
21
|
+
* @stability Beta
|
|
22
|
+
*/
|
|
23
|
+
type SubNavMode = "section" | "tabs" | "sidebar" | "off";
|
|
24
|
+
/**
|
|
25
|
+
* A sub-navigation item: a `NavItem` whose `icon` is optional (the tab row,
|
|
26
|
+
* the phone bar and the rail show the label's first letter without one).
|
|
27
|
+
* Every `NavItem` is a `SubNavItem`; `NavItem` itself keeps `icon` required.
|
|
28
|
+
*
|
|
29
|
+
* @stability Beta
|
|
30
|
+
*/
|
|
31
|
+
interface SubNavItem extends Omit<NavItem, "icon" | "children"> {
|
|
32
|
+
icon?: LucideIcon;
|
|
33
|
+
children?: SubNavItem[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Props for {@link SubNavTabs}.
|
|
37
|
+
*
|
|
38
|
+
* @stability Beta
|
|
39
|
+
*/
|
|
40
|
+
interface SubNavTabsProps {
|
|
41
|
+
items: SubNavItem[];
|
|
42
|
+
/** Current location (`pathname + search + hash`); drives the active tab. */
|
|
43
|
+
activePath?: string;
|
|
44
|
+
linkComponent: LinkComponent;
|
|
45
|
+
isActive?: (to: string, activePath?: string) => boolean;
|
|
46
|
+
/** Right-aligned slot in the row, e.g. a "Ready to publish" chip. */
|
|
47
|
+
end?: ReactNode;
|
|
48
|
+
/** Accessible name of the `<nav>` (default "Page sections"). */
|
|
49
|
+
label?: string;
|
|
50
|
+
className?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A row of navigation tabs: real links (deep links and middle-click work),
|
|
54
|
+
* `aria-current="page"` on the active one, badges as chips, the active tab
|
|
55
|
+
* underlined in the brand colour. AppShell renders it in `tabs` mode; an app
|
|
56
|
+
* using `subNav: "off"` can place it itself.
|
|
57
|
+
*
|
|
58
|
+
* @stability Beta
|
|
59
|
+
*/
|
|
60
|
+
declare function SubNavTabs({ items, activePath, linkComponent: LinkComp, isActive, end, label, className, }: SubNavTabsProps): React.JSX.Element;
|
|
61
|
+
/**
|
|
62
|
+
* Options for {@link useAppShellSubNav}.
|
|
63
|
+
*
|
|
64
|
+
* @stability Beta
|
|
65
|
+
*/
|
|
66
|
+
interface UseAppShellSubNavOptions {
|
|
67
|
+
/** This page's `subNav` mode; wins over the shell's and the node's. */
|
|
68
|
+
mode?: SubNavMode;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Give the shell this page's child items (sub-navigation) while the page is
|
|
72
|
+
* mounted. They replace the current `NavigationNode`'s `items`; pass
|
|
73
|
+
* `null` / `undefined` to fall back to them (a `mode` still applies). When
|
|
74
|
+
* several components call it (a layout and its page), the innermost wins,
|
|
75
|
+
* for items and for mode separately. Use it when children depend on
|
|
76
|
+
* page data, such as live badge counts. A fresh array each render is fine:
|
|
77
|
+
* the shell only updates when a `to`, label, badge, icon or flag changes.
|
|
78
|
+
*
|
|
79
|
+
* ```tsx
|
|
80
|
+
* useAppShellSubNav([
|
|
81
|
+
* { to: `/forms/${id}/edit`, label: "Questions", icon: ListChecks, badge: questions.length, end: true },
|
|
82
|
+
* { to: `/forms/${id}/edit/logic`, label: "Logic and endings", icon: GitBranch, badge: rules.length },
|
|
83
|
+
* { to: `/forms/${id}/edit/responses`, label: "Responses", icon: Inbox, badge: responses },
|
|
84
|
+
* { to: `/forms/${id}/edit/settings`, label: "Settings", icon: Settings },
|
|
85
|
+
* ]);
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* Does nothing outside an `AppShell`.
|
|
89
|
+
*
|
|
90
|
+
* @stability Beta
|
|
91
|
+
*/
|
|
92
|
+
declare function useAppShellSubNav(items: SubNavItem[] | null | undefined, options?: UseAppShellSubNavOptions): void;
|
|
93
|
+
/**
|
|
94
|
+
* Render `children` in the right-hand slot of the shell's sub-navigation
|
|
95
|
+
* tab row, e.g. a "Ready to publish" status chip. Rendered through a
|
|
96
|
+
* portal, so it keeps the page's context and state. Renders nothing when
|
|
97
|
+
* the shell shows no tab row (other modes, phones, no children). When
|
|
98
|
+
* several are mounted (a layout's and a page's), the innermost renders.
|
|
99
|
+
*
|
|
100
|
+
* @stability Beta
|
|
101
|
+
*/
|
|
102
|
+
declare function AppShellSubNavEnd({ children }: {
|
|
103
|
+
children: ReactNode;
|
|
104
|
+
}): ReactNode;
|
|
105
|
+
/**
|
|
106
|
+
* Render the page's resource header (breadcrumb, title, status, actions) at
|
|
107
|
+
* the top of the shell's content, ABOVE the sub-navigation tab row, where
|
|
108
|
+
* the design puts it. A portal like `AppShellSubNavEnd`, so it keeps the
|
|
109
|
+
* page's state; the innermost mounted one renders. On phones it stays at
|
|
110
|
+
* the top of the content while the bottom bar carries the children. With
|
|
111
|
+
* no tab row (the default `"section"` mode, or a page without children) it
|
|
112
|
+
* simply sits at the top of the content. Mount none and the layout is
|
|
113
|
+
* unchanged.
|
|
114
|
+
*
|
|
115
|
+
* @stability Beta
|
|
116
|
+
*/
|
|
117
|
+
declare function AppShellPageHeader({ children }: {
|
|
118
|
+
children: ReactNode;
|
|
119
|
+
}): ReactNode;
|
|
120
|
+
|
|
121
|
+
interface NavItem {
|
|
122
|
+
/** Route, optionally with `?search` / `#hash` (see `isPathActive`). */
|
|
123
|
+
to: string;
|
|
124
|
+
icon: LucideIcon;
|
|
125
|
+
label: string;
|
|
126
|
+
/** Nested items — rendered as a collapsible group when the rail is expanded */
|
|
127
|
+
children?: NavItem[];
|
|
128
|
+
/**
|
|
129
|
+
* A count (`3`) or a short status (`"New"`), shown as a chip: in the
|
|
130
|
+
* sidebar, the sub-navigation tabs and the mobile bar; as a dot on the
|
|
131
|
+
* collapsed rail. It is part of the item's accessible name where the label
|
|
132
|
+
* is hidden.
|
|
133
|
+
*/
|
|
134
|
+
badge?: string | number;
|
|
135
|
+
disabled?: boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Exact-match active state (e.g. overview `/`). Exact on the path only, so
|
|
138
|
+
* an `activePath` carrying `?search#hash` still matches.
|
|
139
|
+
*/
|
|
140
|
+
end?: boolean;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The "up" link AppShell renders above page content: a real link to the
|
|
144
|
+
* logical parent, never `history.back()`.
|
|
145
|
+
*
|
|
146
|
+
* @stability Beta
|
|
147
|
+
*/
|
|
148
|
+
interface ParentNavigation {
|
|
149
|
+
href: string;
|
|
150
|
+
label: string;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Declarative chrome for AppShell — developer defaults + optional
|
|
154
|
+
* in-sidebar user toggles (Notion/Linear “customize chrome” pattern).
|
|
155
|
+
*
|
|
156
|
+
* @stability Beta
|
|
157
|
+
*/
|
|
158
|
+
interface AppShellOptions {
|
|
159
|
+
showLogo?: boolean;
|
|
160
|
+
showAppName?: boolean;
|
|
161
|
+
showThemeToggle?: boolean;
|
|
162
|
+
/** Desktop rail collapse control */
|
|
163
|
+
showCollapse?: boolean;
|
|
164
|
+
showUserSlot?: boolean;
|
|
165
|
+
showFooter?: boolean;
|
|
166
|
+
showSecondaryNav?: boolean;
|
|
167
|
+
/** Mobile fixed bottom tab bar */
|
|
168
|
+
showMobileBottomNav?: boolean;
|
|
169
|
+
/**
|
|
170
|
+
* Open the desktop sidebar collapsed (an icon rail, `collapsedRailWidth` wide) when the user hasn't
|
|
171
|
+
* chosen. The app-wide default; a page asks for its own through
|
|
172
|
+
* `AppShellProps.defaultCollapsed` or `NavigationNode.defaultCollapsed`.
|
|
173
|
+
*/
|
|
174
|
+
defaultCollapsed?: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Opt-in key that toggles the desktop sidebar, e.g. `"["` as in Linear and
|
|
177
|
+
* Notion. Off (`false`) by default. It never fires while typing in a field,
|
|
178
|
+
* while a listbox, combobox or menu has focus (Select typeahead), while a
|
|
179
|
+
* dialog is open, with Ctrl/Cmd/Alt held, or when `showCollapse` is off.
|
|
180
|
+
*/
|
|
181
|
+
collapseShortcut?: string | false;
|
|
182
|
+
/**
|
|
183
|
+
* Width of the collapsed icon rail in px: `56` (default, as before) or
|
|
184
|
+
* `64` (the Breakthrough design).
|
|
185
|
+
*/
|
|
186
|
+
collapsedRailWidth?: 56 | 64;
|
|
187
|
+
/**
|
|
188
|
+
* Shell sub-navigation, opt-in. `"section"` (default) renders a page's
|
|
189
|
+
* children exactly as AppShell always has; `"tabs"` or `"sidebar"` turn
|
|
190
|
+
* sub-navigation on (see `SubNavMode`), and `"off"` hides them. A
|
|
191
|
+
* navigation node, the `subNav` prop or the page
|
|
192
|
+
* (`useAppShellSubNav(items, { mode })`) can override it.
|
|
193
|
+
*/
|
|
194
|
+
subNav?: SubNavMode;
|
|
195
|
+
/**
|
|
196
|
+
* Show the in-sidebar Sidebar options popover so users can customize chrome.
|
|
197
|
+
* Default true when collapse / secondary / theme / slots are available.
|
|
198
|
+
*/
|
|
199
|
+
configurable?: boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Toggle keys the developer has decided on and users may not override —
|
|
202
|
+
* e.g. hiding `showThemeToggle` for a brand that manages theme elsewhere.
|
|
203
|
+
* Locked keys are dropped from the options popover and any stored user
|
|
204
|
+
* override for them is ignored on resolve.
|
|
205
|
+
*/
|
|
206
|
+
lockedOptions?: (keyof AppShellOptions)[];
|
|
207
|
+
/** Comfortable (default) vs compact row padding */
|
|
208
|
+
density?: "comfortable" | "compact";
|
|
209
|
+
/** Expanded rail width in rem-ish tokens: narrow ≈ 13rem, wide ≈ 16rem */
|
|
210
|
+
railWidth?: "narrow" | "wide";
|
|
211
|
+
/**
|
|
212
|
+
* Where page content scrolls.
|
|
213
|
+
* - `container` (default): main panel scrolls — classic app-shell chrome.
|
|
214
|
+
* - `window`: document/window scrolls with a sticky rail so React Router
|
|
215
|
+
* `ScrollRestoration` keeps working (dashboard dogfood).
|
|
216
|
+
*/
|
|
217
|
+
scrollRoot?: "container" | "window";
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* @stability Beta
|
|
221
|
+
*/
|
|
222
|
+
interface AppShellProps {
|
|
223
|
+
/** App name shown in sidebar header */
|
|
224
|
+
appName: string;
|
|
225
|
+
/** App logo letter or short text (ignored when `logo` is set) */
|
|
226
|
+
appLogo?: string;
|
|
227
|
+
/** Custom logo node — preferred over `appLogo` letter tile */
|
|
228
|
+
logo?: ReactNode;
|
|
229
|
+
/**
|
|
230
|
+
* Primary navigation items. Optional when `navigation` is set: the tree
|
|
231
|
+
* root's `items` are the global nav then.
|
|
232
|
+
*/
|
|
233
|
+
navItems?: NavItem[];
|
|
234
|
+
/**
|
|
235
|
+
* The app's logical navigation hierarchy. AppShell resolves it against
|
|
236
|
+
* `activePath` (see `resolveNavigation`) and derives:
|
|
237
|
+
* - the sidebar's global items from the root's `items` (unless `navItems`),
|
|
238
|
+
* - a contextual section from the current node's `items`, labelled with
|
|
239
|
+
* the node's `label` (`$param` segments in `to` filled from the URL),
|
|
240
|
+
* - the mobile tab bar: the contextual items when there are any, else the
|
|
241
|
+
* global top-level items (unless `mobileNavItems`),
|
|
242
|
+
* - a back link to the current node's logical parent (unless
|
|
243
|
+
* `parentNavigation`).
|
|
244
|
+
*
|
|
245
|
+
* @stability Beta
|
|
246
|
+
*/
|
|
247
|
+
navigation?: NavigationNode<NavItem>;
|
|
248
|
+
/**
|
|
249
|
+
* Back-to-parent link rendered in the content header, above `children`, on
|
|
250
|
+
* mobile and desktop. Derived from `navigation` when omitted; `null` hides
|
|
251
|
+
* it. Use this instead of placing a back row inside page content.
|
|
252
|
+
*
|
|
253
|
+
* @stability Beta
|
|
254
|
+
*/
|
|
255
|
+
parentNavigation?: ParentNavigation | null;
|
|
256
|
+
/** Secondary nav (e.g. “More”) — rendered below a divider when shown */
|
|
257
|
+
secondaryNavItems?: NavItem[];
|
|
258
|
+
/** Label above secondary nav (default “More”) */
|
|
259
|
+
secondaryNavLabel?: string;
|
|
260
|
+
/**
|
|
261
|
+
* Items for the mobile bottom tab bar. Defaults to top-level `navItems`
|
|
262
|
+
* (no children). Pass a short list for apps with large sidebars; pass `[]`
|
|
263
|
+
* and set `options.showMobileBottomNav: false` to hide.
|
|
264
|
+
*/
|
|
265
|
+
mobileNavItems?: NavItem[];
|
|
266
|
+
/** Slot below the logo row (product switcher, workspace picker, …) */
|
|
267
|
+
headerSlot?: ReactNode;
|
|
268
|
+
/**
|
|
269
|
+
* Compact slot shown in the **collapsed** desktop rail, where `headerSlot`
|
|
270
|
+
* is hidden for space. Use it to keep a core affordance reachable without
|
|
271
|
+
* expanding — e.g. an icon-only product switcher. Omit to hide the header
|
|
272
|
+
* region entirely while collapsed.
|
|
273
|
+
*/
|
|
274
|
+
collapsedHeaderSlot?: ReactNode;
|
|
275
|
+
/** Footer region above the user slot (version, links) */
|
|
276
|
+
footer?: ReactNode;
|
|
277
|
+
/**
|
|
278
|
+
* User / account block at the bottom of the sidebar. When the rail is
|
|
279
|
+
* collapsed, AppShell hides any descendant carrying the
|
|
280
|
+
* `data-shell-user-detail` attribute (e.g. name/email text) so only a
|
|
281
|
+
* compact element (avatar, initial) remains — tag the detail wrapper you
|
|
282
|
+
* want hidden in the collapsed rail with that attribute.
|
|
283
|
+
*/
|
|
284
|
+
userSlot?: ReactNode;
|
|
285
|
+
/** Page content */
|
|
286
|
+
children: ReactNode;
|
|
287
|
+
/**
|
|
288
|
+
* @deprecated Prefer `options.showThemeToggle`. Kept for sample-app
|
|
289
|
+
* back-compat; merges into resolved options when `options` omits it.
|
|
290
|
+
*/
|
|
291
|
+
showThemeToggle?: boolean;
|
|
292
|
+
/** Chrome config (developer defaults + optional in-sidebar menu) */
|
|
293
|
+
options?: AppShellOptions;
|
|
294
|
+
/** Persist user sidebar-option overrides + collapse state (localStorage) */
|
|
295
|
+
optionsStorageKey?: string;
|
|
296
|
+
/**
|
|
297
|
+
* Controlled collapse (desktop rail). Overrides the stored user choice and
|
|
298
|
+
* every default.
|
|
299
|
+
*/
|
|
300
|
+
collapsed?: boolean;
|
|
301
|
+
/** Called with the new state whenever the user toggles the sidebar. */
|
|
302
|
+
onCollapsedChange?: (collapsed: boolean) => void;
|
|
303
|
+
/**
|
|
304
|
+
* This page's default: open the desktop sidebar collapsed (`true`) or
|
|
305
|
+
* expanded (`false`) until the user toggles it. Reactive, so a layout can
|
|
306
|
+
* pass it per route. Wins over `navigation`'s `defaultCollapsed` and
|
|
307
|
+
* `options.defaultCollapsed`; the user's own choice (persisted under
|
|
308
|
+
* `optionsStorageKey`) wins over all of them.
|
|
309
|
+
*
|
|
310
|
+
* @stability Beta
|
|
311
|
+
*/
|
|
312
|
+
defaultCollapsed?: boolean;
|
|
313
|
+
/**
|
|
314
|
+
* This page's sub-navigation mode (see `AppShellOptions.subNav`). Wins
|
|
315
|
+
* over `navigation`'s `subNav` and `options.subNav`; the page's own
|
|
316
|
+
* `useAppShellSubNav(items, { mode })` wins over it.
|
|
317
|
+
*
|
|
318
|
+
* @stability Beta
|
|
319
|
+
*/
|
|
320
|
+
subNav?: SubNavMode;
|
|
321
|
+
/**
|
|
322
|
+
* Accessible name of the sub-navigation tab row. Defaults to the current
|
|
323
|
+
* navigation node's label, else "Page sections".
|
|
324
|
+
*/
|
|
325
|
+
subNavLabel?: string;
|
|
326
|
+
/**
|
|
327
|
+
* Injectable link component. Defaults to a plain `<a>` (no router). Pass a
|
|
328
|
+
* router adapter — e.g. React Router `({ href, ...p }) => <Link to={href} {...p} />`
|
|
329
|
+
* or Next.js `next/link` — to get client-side navigation.
|
|
330
|
+
*/
|
|
331
|
+
linkComponent?: LinkComponent;
|
|
332
|
+
/**
|
|
333
|
+
* Current location used to highlight the active nav item. Pass the full
|
|
334
|
+
* location, `pathname + search + hash` (React Router: `useLocation()`), so
|
|
335
|
+
* items whose `to` names a query or hash (`/admin?tab=users`) can become
|
|
336
|
+
* active. A pathname alone still works for path-only items, `end` included.
|
|
337
|
+
* Omit to disable active highlighting.
|
|
338
|
+
*/
|
|
339
|
+
activePath?: string;
|
|
340
|
+
/**
|
|
341
|
+
* Optional custom active-state matcher. Defaults to a prefix match (with `/`
|
|
342
|
+
* treated as exact), mirroring React Router `NavLink`.
|
|
343
|
+
*/
|
|
344
|
+
isActive?: (to: string, activePath?: string) => boolean;
|
|
345
|
+
}
|
|
346
|
+
declare function AppShell({ appName, appLogo, logo, navItems: navItemsProp, navigation, parentNavigation: parentNavigationProp, secondaryNavItems, secondaryNavLabel, mobileNavItems, headerSlot, collapsedHeaderSlot, footer, userSlot, children, showThemeToggle: showThemeToggleProp, options: optionsProp, optionsStorageKey, collapsed: collapsedProp, onCollapsedChange, defaultCollapsed: defaultCollapsedProp, subNav: subNavProp, subNavLabel, linkComponent, activePath, isActive, }: AppShellProps): React.JSX.Element;
|
|
347
|
+
|
|
348
|
+
export { AppShell as A, type NavItem as N, type ParentNavigation as P, type SubNavItem as S, type UseAppShellSubNavOptions as U, type AppShellOptions as a, AppShellPageHeader as b, type AppShellProps as c, AppShellSubNavEnd as d, type SubNavMode as e, SubNavTabs as f, type SubNavTabsProps as g, useAppShellSubNav as u };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
18
|
+
|
|
19
|
+
export { __export, __reExport };
|
|
20
|
+
//# sourceMappingURL=chunk-424PT5DM.js.map
|
|
21
|
+
//# sourceMappingURL=chunk-424PT5DM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-424PT5DM.js"}
|