@nxgt/material 3.85.1 → 3.86.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/CHANGELOG.md +36 -0
- package/README.md +39 -5
- package/dist/{alert-dialog-1t6ULy5i.js → alert-dialog-DZnt8PYE.js} +1 -1
- package/dist/{async-slice-xyA9Uzct.js → async-slice-D3OdV2BS.js} +1 -1
- package/dist/{button-DhiNGrdT.js → button-MAL5an7L.js} +1 -1
- package/dist/{calculator-BcgxxScL.js → calculator-C3a8YMwl.js} +10 -10
- package/dist/calculator-field.js +6 -6
- package/dist/{calculator-segment-C4WQAHcN.js → calculator-segment-BigMpjhn.js} +1 -1
- package/dist/calculator.js +1 -1
- package/dist/charts.js +3 -3
- package/dist/{chip-m0_usuzd.js → chip-DKAKoNxq.js} +2 -2
- package/dist/{code-editor-BgKFrcld.js → code-editor-B6dFQSUJ.js} +4 -4
- package/dist/code-editor.js +4 -4
- package/dist/{code-language-items-BWw17W9s.js → code-language-items-CXEMiWoF.js} +2 -2
- package/dist/components/components.css +2 -0
- package/dist/components/theme-tokens.css +18 -0
- package/dist/components/ui/layout/app-bar/top-app-bar/top-app-bar-collapse.css +60 -0
- package/dist/components/ui/layout/app-bar/top-app-bar/top-app-bar.css +205 -0
- package/dist/components/ui/layout/app-shell/app-header.css +70 -63
- package/dist/{components-DfSE2F8d.js → components-C2MBVly0.js} +17327 -16929
- package/dist/components.js +24 -24
- package/dist/{conversion-variant-DultZZQm.js → conversion-variant-BT5n4-jm.js} +3 -3
- package/dist/{currency-field-CPbaM7ay.js → currency-field-t41sFYg_.js} +4 -4
- package/dist/{dialog-DmlG9opW.js → dialog-BjKiRXJg.js} +3 -3
- package/dist/dnd.js +1 -1
- package/dist/{dropdown-menu-CDKev8dk.js → dropdown-menu-IRFp4CH9.js} +1 -1
- package/dist/editor.js +7 -7
- package/dist/{empty-state-C56000TB.js → empty-state-C70prDDr.js} +1 -1
- package/dist/{financial-variant-BYjQ690K.js → financial-variant-BIpzs8IL.js} +5 -5
- package/dist/{hooks-BTwLAy_A.js → hooks-CjO11MS7.js} +2 -2
- package/dist/hooks.js +2 -2
- package/dist/{hover-card-CDtW6utN.js → hover-card-cU_aJa18.js} +1 -1
- package/dist/i18n.js +3 -3
- package/dist/{icon-badge-ec7MmUGA.js → icon-badge-D3KO6uEQ.js} +1 -1
- package/dist/{icon-button-CG4aO8Gh.js → icon-button-DrqNk1Ww.js} +1 -1
- package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
- package/dist/lib/components/ui/layout/app-bar/index.d.ts +1 -1
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/index.d.ts +1 -0
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-actions.d.ts +15 -0
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-headline.d.ts +4 -0
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-link.d.ts +9 -0
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-menu-section.d.ts +14 -0
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-primary-action.d.ts +12 -0
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-row.d.ts +23 -0
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/partials/top-app-bar-title-block.d.ts +18 -0
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar.actions.d.ts +43 -0
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar.d.ts +9 -0
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar.types.d.ts +120 -0
- package/dist/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar.utils.d.ts +43 -0
- package/dist/lib/components/ui/layout/app-shell/activity-app-bar.d.ts +6 -6
- package/dist/lib/components/ui/layout/app-shell/activity-chrome.d.ts +6 -3
- package/dist/lib/components/ui/layout/app-shell/activity-column-props.d.ts +1 -1
- package/dist/lib/components/ui/layout/app-shell/activity-column.d.ts +2 -2
- package/dist/lib/components/ui/layout/app-shell/activity-layout-navigation.types.d.ts +32 -4
- package/dist/lib/components/ui/layout/app-shell/activity-layout.types.d.ts +12 -2
- package/dist/lib/components/ui/layout/app-shell/app-header.d.ts +9 -3
- package/dist/lib/components/ui/layout/app-shell/header-look.d.ts +14 -0
- package/dist/lib/components/ui/layout/app-shell/header-title.d.ts +9 -36
- package/dist/lib/components/ui/layout/app-shell/header.utils.d.ts +13 -3
- package/dist/lib/components/ui/layout/app-shell/index.d.ts +1 -1
- package/dist/lib/components/ui/layout/app-shell/navigation.utils.d.ts +7 -2
- package/dist/lib/components/ui/layout/app-shell/shell-scroll.d.ts +23 -1
- package/dist/lib/components/ui/layout/app-shell/shell-top-app-bar.d.ts +10 -0
- package/dist/lib/components/ui/layout/app-shell/shell-top-app-bar.parts.d.ts +58 -0
- package/dist/lib/components/ui/layout/app-shell/shell-top-app-bar.types.d.ts +26 -0
- package/dist/lib/components/ui/layout/app-shell/use-header-scroll.d.ts +16 -9
- package/dist/lib/components/ui/layout/app-shell/use-shell-header.d.ts +1 -1
- package/dist/lib/components/ui/primitives/tabs/tabs-press.d.ts +11 -0
- package/dist/lib/i18n/resources/en/en.d.ts +4 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +4 -0
- package/dist/lib/i18n/resources/fr/fr.d.ts +4 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +4 -0
- package/dist/lib/i18n/resources/resources.d.ts +8 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib.js +1 -1
- package/dist/main.js +28 -28
- package/dist/map.js +7 -7
- package/dist/material.css +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/{percent-field-8_pILs15.js → percent-field-D47IlaPF.js} +1 -1
- package/dist/{picker-surface-pyGv-Qh9.js → picker-surface-Cg4V4-E0.js} +4 -4
- package/dist/{popover-3EF4q2CT.js → popover-Dzk4-l9U.js} +1 -1
- package/dist/{pos-types-Dv2UxKSN.js → pos-types-JOXMY4Y2.js} +1 -1
- package/dist/{pos-variant-DJvtN9N8.js → pos-variant-B2mS8bMB.js} +4 -4
- package/dist/print.js +1 -1
- package/dist/{programmer-variant-Bmcq2KzC.js → programmer-variant-C_Tl0P4_.js} +2 -2
- package/dist/{public-BNebh4z0.js → public-ByAsqBOf.js} +9 -9
- package/dist/{rich-code-block-DmX7d58S.js → rich-code-block-CJAfqk6b.js} +5 -5
- package/dist/{scientific-variant-DijUrpP8.js → scientific-variant-CskM4MLV.js} +2 -2
- package/dist/{sortable-provider-Cnmt_gWu.js → sortable-provider-ICS_vkLA.js} +2 -2
- package/dist/{spinner-C6LP2Rw8.js → spinner-CweqX5A9.js} +1 -1
- package/dist/styles.css +2 -0
- package/dist/{tabs-Bj1zOe7-.js → tabs-95Y2LmnV.js} +65 -43
- package/dist/theme-picker.js +2 -2
- package/dist/{theme-toggle-Cxku5iBP.js → theme-toggle-WAFkncYx.js} +1 -1
- package/dist/{toolbar-items-CEIuSxU_.js → toolbar-items-BsgzHSNf.js} +4 -4
- package/dist/{translate-material-DI3nmQ6I.js → translate-material-DTIAPmlU.js} +1 -1
- package/dist/{translator-BstxG_hN.js → translator-BRZNhsy5.js} +8 -0
- package/dist/{use-chat-composer-BqLkk_iH.js → use-chat-composer-CleSUh_y.js} +15 -15
- package/dist/{use-heading-spy-nFyb9_KE.js → use-heading-spy-D8WbHJuK.js} +4 -4
- package/dist/{use-material-translation-1kCHtB0p.js → use-material-translation-CCy1Me0h.js} +1 -1
- package/dist/{use-return-focus-Hv8rn55-.js → use-return-focus-BA8G1qVU.js} +1 -1
- package/docs/README.md +1 -0
- package/docs/components/navigation-and-layout.md +85 -32
- package/docs/guides/app-shell.md +158 -63
- package/docs/guides/drawing.md +2 -0
- package/docs/guides/layouts.md +3 -3
- package/docs/guides/migrating-to-top-app-bar.md +156 -0
- package/docs/guides/theming.md +5 -0
- package/docs/previews/navigation-layout-appshell-blue-moon.webp +0 -0
- package/docs/previews/navigation-layout-appshell-footer-icon.webp +0 -0
- package/docs/previews/navigation-layout-appshell-menu-badge.webp +0 -0
- package/docs/previews/navigation-layout-appshell-page-ground.webp +0 -0
- package/docs/previews/navigation-layout-appshell-themes.webp +0 -0
- package/docs/previews/navigation-layout-appshell.webp +0 -0
- package/docs/previews/navigation-layout-topappbar.webp +0 -0
- package/docs/roadmap.md +17 -6
- package/docs/troubleshooting.md +80 -0
- package/lib/components/components.css +2 -0
- package/lib/components/theme-tokens.css +18 -0
- package/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar-collapse.css +60 -0
- package/lib/components/ui/layout/app-bar/top-app-bar/top-app-bar.css +205 -0
- package/lib/components/ui/layout/app-shell/app-header.css +70 -63
- package/lib/styles.css +2 -0
- package/lib/themes/ambre.css +5 -1
- package/lib/themes/ardoise.css +5 -1
- package/lib/themes/bleumono.css +5 -1
- package/lib/themes/bluemoon.css +5 -1
- package/lib/themes/corail.css +5 -1
- package/lib/themes/foret.css +5 -1
- package/lib/themes/lavande.css +5 -1
- package/lib/themes/menthe.css +5 -1
- package/lib/themes/ocean.css +5 -1
- package/lib/themes/pink.css +5 -1
- package/lib/themes/prune.css +5 -1
- package/lib/themes/sable.css +5 -1
- package/lib/themes/slate.css +5 -1
- package/lib/themes/teal.css +5 -1
- package/package.json +2 -2
- package/dist/lib/components/ui/layout/app-bar/top-app-bar.d.ts +0 -18
- package/dist/lib/components/ui/layout/app-shell/app-header-row.d.ts +0 -3
- package/dist/lib/components/ui/layout/app-shell/header-context.d.ts +0 -10
- package/dist/lib/components/ui/layout/app-shell/page-title.d.ts +0 -27
- /package/dist/lib/components/ui/layout/{app-shell → app-bar/top-app-bar}/use-truncated-title.d.ts +0 -0
package/docs/README.md
CHANGED
|
@@ -32,6 +32,7 @@ detail: every option, the second example, and the traps.
|
|
|
32
32
|
| [SSR](guides/ssr.md) | Your app renders on a server and bundles this package |
|
|
33
33
|
| [Content Security Policy](guides/csp.md) | Your app sends a strict CSP: the directives to use, the components that still inject a `<style>`, the zod and KaTeX settings, self-hosting a font, the per-request nonce |
|
|
34
34
|
| [The Vite plugin](guides/vite-plugin.md) | You are adding `nxgtMaterial()` to `vite.config.ts`: the CSP by hash or nonce, fonts, the styles check, PDFs on a bundled server, and each option's opt-out |
|
|
35
|
+
| [Moving to TopAppBar](guides/migrating-to-top-app-bar.md) | Your app uses `AppHeader`, `TopAppBar`'s `variant` / `headline`, the shell's `'center'` / `'medium'` / `'large'` header values or the `app-header*` slots: each old spelling and its replacement, the visual changes, the slot rename planned for the next major |
|
|
35
36
|
| [Migrating to 3.0](guides/migrating-to-3.md) | You are upgrading from 2.x: the React 19.3 and React Router 8.5 peers, `ActivityLayout`'s default route transition, `use(browser())` in server HTML, and each API removed in 3.0 with what replaces it |
|
|
36
37
|
| [Date and time fields: from 2.x](guides/date-time-fields-2-x.md) | Your app used `DateField`, `DateRangeField`, `TimeField` or `TimePicker` before their redesign: each change in look and behaviour, the 2.x ways back that 3.0 removed, and what to change in tests |
|
|
37
38
|
| [The field look (2.x)](guides/field-look-2-x.md) | Your fields or pickers look different after an upgrade (8px corners, the focus halo, a dashed read-only, a 44px `NativeSelect`, a list connected to its trigger, a `CountryField` with no chip, new group headers, the date and time fields' boxes) and you want to know what changed or get the earlier look back |
|
|
@@ -7,11 +7,11 @@ documentation-site shell. Everything is imported from
|
|
|
7
7
|
|
|
8
8
|
| | Component | What it does |
|
|
9
9
|
| --- | --- | --- |
|
|
10
|
-
| <img src="../previews/navigation-layout-appshell.webp" width="96" alt=""> | [ActivityLayout](#activitylayout) | App shell: sidebar, header, breadcrumbs, titled page; `header` sets the app bar (small,
|
|
10
|
+
| <img src="../previews/navigation-layout-appshell.webp" width="96" alt=""> | [ActivityLayout](#activitylayout) | App shell: sidebar, header, breadcrumbs, titled page; `header` sets the app bar, a `TopAppBar` (small, medium-flexible, large-flexible or search, collapsing on scroll, hide on scroll, a route's `handle.header`, tabs under it, a route's `handle.back` arrow); a route's `handle.title` is the page's `h1` in the app bar (it labels `main`; `usePageTitleFocus()` focuses it after a navigation), `handle.titleAfter` / `titleAfter` a node right after it, outside the `h1` (a record's `Switcher`, or `<Switcher size="compact">` for an icon-only one), `headerContext` a small tenant line after it from md up, `breadcrumbRoot` a text root crumb or (`icon: true`) the home icon with its own target and name, `breadcrumb={false}` no trail, `context` the tenant line atop the phone's menu sheet; `menuOpen` / `onMenuOpenChange` control the phone's menu sheet, which closes itself on a followed link or any location change |
|
|
11
11
|
| <img src="../previews/navigation-layout-accountmenu.webp" width="96" alt=""> | [AccountMenu](#accountmenu) | The app bar's avatar menu: the account, the app's links, Sign out |
|
|
12
12
|
| <img src="../previews/navigation-layout-authcard.webp" width="96" alt=""> | [AuthCard](#authcard) | Signed-out page frame: one `main`, a centred card, its title the `h1` |
|
|
13
13
|
| <img src="../previews/navigation-layout-sidebar.webp" width="96" alt=""> | [Sidebar](#sidebar) | Collapsible navigation sidebar |
|
|
14
|
-
| <img src="../previews/navigation-layout-topappbar.webp" width="96" alt=""> | [TopAppBar, BottomAppBar](#topappbar) |
|
|
14
|
+
| <img src="../previews/navigation-layout-topappbar.webp" width="96" alt=""> | [TopAppBar, BottomAppBar](#topappbar) | The top app bar (small, medium-flexible, large-flexible, search; collapse, icon actions as data, account menu, primary action) and a bottom bar |
|
|
15
15
|
| | [AppSearchBar, SearchView](#appsearchbar) | The app bar's search: a pill with a docked results panel from 768px, a full-screen search view on a phone |
|
|
16
16
|
| <img src="../previews/navigation-layout-stickyactionbar.webp" width="96" alt=""> | [StickyActionBar](#stickyactionbar) | A page's main decision, stuck to the bottom on a phone, inline from `md` |
|
|
17
17
|
| <img src="../previews/navigation-layout-breadcrumb.webp" width="96" alt=""> | [Breadcrumb](#breadcrumb) | Breadcrumb primitives |
|
|
@@ -68,7 +68,7 @@ const items: MenuItem[] = [
|
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
Parts: `ActivityLayout` (the chrome), `ActivityContent` (one screen),
|
|
71
|
-
`AppSidebar`, `AppHeader` and `AppBreadcrumb`, which the layout composes and
|
|
71
|
+
`AppSidebar`, `AppHeader` (deprecated, use `TopAppBar`) and `AppBreadcrumb`, which the layout composes and
|
|
72
72
|
which work alone. `AppHeader` also takes `tabs` (`ReactNode`, default none: a
|
|
73
73
|
`TabsNav` as a row at the foot of the bar, pinned when the bar hides) and `back`
|
|
74
74
|
(`ShellBack`, default none: the back arrow, named "Back" or "Back to <label>"
|
|
@@ -90,10 +90,11 @@ phone's menu sheet pad clear of `env(safe-area-inset-*)`, as classes; see the
|
|
|
90
90
|
| `headerTitle` | `ReactNode` | — | the header's leading title (the context: an organisation's name), after the phone's menu button, truncated when long; when it is cut off, its full text is the `title` tooltip (#629) |
|
|
91
91
|
| `handle.title` | `ReactNode \| (match: UIMatch) => ReactNode` | — | on a route: the page's title in the app bar, its one `h1`, in the first server HTML; `ActivityContent` leaves its own title out while it is set (see the [app shell guide](../guides/app-shell.md#the-pages-title-in-the-app-bar)) |
|
|
92
92
|
| `titleAfter` / `handle.titleAfter` | `ReactNode \| (match: UIMatch) => ReactNode` | — | a node right after the page's title in the app bar, outside its `h1` (a record's `Switcher`); stays on screen on a phone, the title truncates first; the prop wins over the handle (see the [app shell guide](../guides/app-shell.md#a-slot-after-the-title)) |
|
|
93
|
-
| `headerContext` | `ReactNode` | — | a small muted context line (the tenant)
|
|
93
|
+
| `headerContext` | `ReactNode` | — | the bar's `subtitle`: a small muted context line (the tenant) under the page's title and its slot, from `md` up, outside the `h1`, truncated with its full text as the tooltip; hidden on a phone, where `context` fills the menu sheet (see the [app shell guide](../guides/app-shell.md#a-context-line-after-the-title)) |
|
|
94
94
|
| `breadcrumb` | `boolean` | `true` | `false` hides the breadcrumb trail (an error page); it is also hidden when every matched `handle.breadcrumb` renders `null`; see the [app shell guide](../guides/app-shell.md#breadcrumbs) |
|
|
95
95
|
| `breadcrumbRoot` | `{ label: ReactNode; to: string }` or `{ label: string; to: string; icon: true }` | — | the breadcrumb's first crumb: a text link in place of the home icon, or with `icon: true` the home icon alone linked to `to`, named and tooltipped `label` (`AppBreadcrumb`'s `root`); see the [app shell guide](../guides/app-shell.md#breadcrumbs) |
|
|
96
|
-
| `headerActions` | `ReactNode` | — | header, before the avatar |
|
|
96
|
+
| `headerActions` | `ReactNode \| TopAppBarAction[]` | — | header, before the avatar; data (`TopAppBarAction`: `label`, `icon`, `onClick` / `href`, `placement` `'bar'` or `'account'`, `group`, `selected`, `disabled`; see [TopAppBar](#topappbar)) draws free icon buttons and an account menu |
|
|
97
|
+
| `headerPrimaryAction` | `TopAppBarPrimaryAction` (`{ label, icon, onClick?, disabled? }`) | — | the bar's `primaryAction`, the screen's main action: a `Button` from 768 px, a `Fab` on a phone |
|
|
97
98
|
| `title`, `subtitle`, `actions` | `ReactNode` | — | `ActivityContent`'s header card; `title` is left out while a route's `handle.title` puts the page's title in the app bar |
|
|
98
99
|
| `header` / `hideHeader` | `ReactNode` / `boolean` | — | `ActivityContent`: replace or drop the header card |
|
|
99
100
|
| `contentAs` / `scroll` | `'main' \| 'div'` / `'document' \| 'container'` | `'main'` / `'document'` | the content region is the page's `<main>`; the document scrolls (see the [app shell guide](../guides/app-shell.md#landmarks-headings-and-scrolling)) |
|
|
@@ -107,7 +108,7 @@ phone's menu sheet pad clear of `env(safe-area-inset-*)`, as classes; see the
|
|
|
107
108
|
| `navigation` | `ShellNavigation \| { phone?, tablet?, desktop?, hideOnScroll?, persistKey? }` | `{ phone: 'modal-drawer', tablet: 'rail', desktop: 'rail' }` | `ActivityLayout`: the navigation per window size (phone below 768px, tablet to 1279px, desktop from 1280px). Every `ShellNavigation` name is drawn: `'modal-drawer'`, `'rail'`, `'rail-expanded'`, `'drawer'`, `'sidebar'`, `'top'`, `'two-level'`, `'floating'`, `'bar'` and `'none'`. `hideOnScroll` (a field of this object, `false` by default) slides the `'floating'` toolbar away on scroll; no other preset reads it. `persistKey` (a field of this object) remembers the `'rail-expanded'` rail's, the `'sidebar'`'s and the `'two-level'` panel's state. A name alone (`navigation="sidebar"`, `"drawer"`, `"top"`, `"two-level"`) leaves a phone its default navigation; `{ phone: 'sidebar' }` draws it there. A phone never draws `'rail'` or `'rail-expanded'`: it keeps its menu sheet. `navigation="bar"` alone is phone-only (hidden from 768px, where the default rail shows; `{ tablet: 'bar' }` keeps it there). `'floating'` is drawn at every size, the phone included. See [`navigation` and `header`](../guides/app-shell.md#navigation-and-header) |
|
|
108
109
|
| `navigationFab` | `ReactNode \| ({ expanded }) => ReactNode` | — | `ActivityLayout`: the screen's primary action, at the top of the `'rail-expanded'` rail (a `Fab`; the function form receives the rail's state). The other presets do not draw it |
|
|
109
110
|
| `sidebarExpanded` / `defaultSidebarExpanded` / `onSidebarExpandedChange` | `boolean` / `boolean` / `(expanded: boolean) => void` | — / `false` / — | `ActivityLayout`: the `'sidebar'`'s expanded state (labels beside the icons; it starts collapsed), controlled or not; with `navigation.persistKey` the last choice is kept and read after hydration |
|
|
110
|
-
| `header` | `'small' \| '
|
|
111
|
+
| `header` | `'small' \| 'medium-flexible' \| 'large-flexible' \| 'search' \| 'none' \| { size?, align?, collapse?, hideOnScroll?, tabs?, search? }` | `'small'` | `ActivityLayout`: the app bar, drawn with `TopAppBar`. `size` and `align` pick its look (the old `'center'`, `'medium'`, `'large'` and `variant` still work, deprecated); `collapse` (default on for medium and large) and `hideOnScroll` follow the scroll; `search` draws an `AppSearchBar` (see [AppSearchBar](#appsearchbar)); `tabs` (a `TabsNav`) draws a row stuck under the bar, which stays pinned when the bar hides (see [Tabs under the app bar](../guides/app-shell.md#tabs-under-the-app-bar)); a route's `handle.back` (`ShellBack`: `{ to, label? }` or `true`) draws a back arrow before the title, the phone's menu button following it while the phone has the menu sheet (see [The back arrow](../guides/app-shell.md#the-back-arrow)). A route's `handle.header` overrides it; a medium or large header needs a `handle.title`, else it stays small; `'none'` also drops the phone's menu button, so pair it with `navigation="bar"`. See [Top app bar sizes](../guides/app-shell.md#top-app-bar-sizes), [Scroll direction](../guides/app-shell.md#scroll-direction) and [Room at the bottom](../guides/app-shell.md#room-at-the-bottom) |
|
|
111
112
|
| `align` | `'start' \| 'center'` | `'start'` | `ActivityLayout`, `AppSidebar`: the collapsed rail's entries sit right under the brand, or `'center'` for centred in the height below it |
|
|
112
113
|
| `backdrop` | `ReactNode \| false` | — | `ActivityLayout`: an exception to the theme's background (drawn by default; Lavande's is the built-in gradient and blobs); `false` draws a plain page. A node is a decorative background behind the frame: a `Background` preset from `@nxgt/material/backgrounds`. Rendered in an `inert`, `aria-hidden` layer as wide as the content column and one viewport high, fixed in the viewport while the content scrolls; the chrome (app bar, breadcrumb, tabs strip) is lightly translucent and frosted over it (80%, 87% for the sidebar, breadcrumb and tabs strip, 92% and no blur under `prefers-reduced-transparency` or without `backdrop-filter`), and the lists, tables and cards stay opaque; `--nxgt-backdrop-chrome` sets the chrome's fill and `--nxgt-backdrop-surface` the lists'. See [Use a background preset behind the shell](../guides/app-shell.md#use-a-background-preset-behind-the-shell) |
|
|
113
114
|
| `routeTransition` | `boolean \| Omit<ViewTransitionProps, 'children' \| 'ref'>` | `true` | `ActivityLayout`: React's `<ViewTransition>` around the content region, so a navigation cross-fades it (by default only when the pathname changes, not on a search change, a fetcher or a revalidation). `false` renders none (your app has its own boundary); an object passes `default` / `update` classes, `name`, callbacks through. `ROUTE_TRANSITION_CLASS` (`'nxgt-route'`) is exported for nested boundaries. See [Route transitions](../guides/app-shell.md#route-transitions) |
|
|
@@ -128,7 +129,7 @@ pads its bottom and shortens its sidebar by the bar's height
|
|
|
128
129
|
footer stay above it. Another layout adds `pb-(--nxgt-media-dock)` to what
|
|
129
130
|
scrolls.
|
|
130
131
|
|
|
131
|
-
**Storybook:** `Navigation & layout/AppShell` · **Vue:** `ActivityLayout`, `ActivityContent`, `AppSidebar`, `AppHeader
|
|
132
|
+
**Storybook:** `Navigation & layout/AppShell` · **Vue:** `ActivityLayout`, `ActivityContent`, `AppSidebar`, `AppHeader` (deprecated, use `TopAppBar`), `AppBreadcrumb`
|
|
132
133
|
|
|
133
134
|
---
|
|
134
135
|
|
|
@@ -360,19 +361,79 @@ recolour the rail. See [Theming](../guides/theming.md#the-active-entry-of-the-si
|
|
|
360
361
|
|
|
361
362
|

|
|
362
363
|
|
|
363
|
-
|
|
364
|
+
The top app bar: the bar `ActivityLayout` draws, for a layout of your own. A
|
|
365
|
+
`title` that is the page's one `<h1>`, a `subtitle` under it, four sizes, a
|
|
366
|
+
collapse on scroll, free icon actions with an account menu, and a main action.
|
|
367
|
+
Flat on the page ground; once the page scrolls it takes a soft tint and a hairline.
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
import { Bell, Languages, Plus } from 'lucide-react';
|
|
371
|
+
import { Avatar, TopAppBar } from '@nxgt/material/components';
|
|
372
|
+
|
|
373
|
+
<TopAppBar
|
|
374
|
+
as="header"
|
|
375
|
+
size="large-flexible"
|
|
376
|
+
collapse
|
|
377
|
+
title="Orders"
|
|
378
|
+
subtitle="Acme workspace"
|
|
379
|
+
actions={[
|
|
380
|
+
{ label: 'Notifications', icon: <Bell />, onClick: openInbox },
|
|
381
|
+
{ label: 'English', icon: <Languages />, group: 'Language', selected: true, placement: 'account' },
|
|
382
|
+
{ label: 'Français', group: 'Language', placement: 'account', onClick: () => setLocale('fr') },
|
|
383
|
+
]}
|
|
384
|
+
trailing={<Avatar alt="Ada Lovelace" fallback="AL" />}
|
|
385
|
+
primaryAction={{ label: 'New order', icon: <Plus />, onClick: createOrder }}
|
|
386
|
+
/>
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
`collapse` and `hideOnScroll` only mark the bar (`data-collapse`,
|
|
390
|
+
`data-hide-on-scroll`): the scroll is followed by the shell, so in `ActivityLayout`
|
|
391
|
+
use `header` instead (see [Top app bar sizes](../guides/app-shell.md#top-app-bar-sizes)).
|
|
392
|
+
|
|
393
|
+
| Prop | Type | Default | Effect |
|
|
394
|
+
| --- | --- | --- | --- |
|
|
395
|
+
| `size` | `'small' \| 'medium-flexible' \| 'large-flexible' \| 'search'` | `'small'` | `small`: one row. `medium-flexible` and `large-flexible`: the `title` on a second row (28 px, and 40 px or 36 px on a phone; up to two lines in large), collapsing into the small row. `search`: the `search` field is the bar's centre. Without a `title` a medium or large bar draws as small |
|
|
396
|
+
| `align` | `'start' \| 'center'` | `'start'` | `center` centres a small bar's title |
|
|
397
|
+
| `title` / `titleId` | `ReactNode` / `string` | — | the bar renders `title` as the page's one `<h1>`; `titleId` is its `id`, to name a region |
|
|
398
|
+
| `subtitle` | `ReactNode` | — | a muted `<p>` right after the `<h1>`, one line, truncated |
|
|
399
|
+
| `titleAfter` | `ReactNode` | — | beside the title: a badge, a status |
|
|
400
|
+
| `leading` / `trailing` | `ReactNode` | — | start of the row (menu or back button) / its end (the avatar) |
|
|
401
|
+
| `actions` | `ReactNode \| TopAppBarAction[]` | — | a node is drawn as given. An array of data (every item a plain object with a string `label`) draws free ghost icon buttons (one on a phone, two from 768 px) and gathers the rest, and every `'account'` action, in a menu opened by `trailing` (named "Account menu") or by a More button when there is no `trailing` |
|
|
402
|
+
| `primaryAction` | `TopAppBarPrimaryAction` | — | `{ label, icon, onClick?, disabled? }`: a `Button` from 768 px; on a phone a `Fab` above `--nxgt-bottom-chrome`, portalled to `document.body` after mount |
|
|
403
|
+
| `search` | `ReactNode` | — | the field of a `'search'` bar; the title stays in the page as an `sr-only` `<h1>` |
|
|
404
|
+
| `tabs` | `ReactNode` | — | a row under the bar: a `Tabs` list |
|
|
405
|
+
| `collapse` / `hideOnScroll` | `boolean` | `false` | mark the bar as collapsing its second row, or hiding on scroll down |
|
|
406
|
+
| `linkComponent` | `LinkComponent` | nearest `LinkComponentProvider`'s, else `<a>` | draws an action with an `href` |
|
|
407
|
+
| `as` | `'div' \| 'header'` | `'div'` | the element drawn; use `'header'` for the page's banner |
|
|
408
|
+
| `variant` | `'small' \| 'center' \| 'medium' \| 'large'` | — | deprecated for `size` and `align`: warns once in development |
|
|
409
|
+
| `headline` | `ReactNode` | — | deprecated for `title`: renders in a non-heading element; warns once in development |
|
|
410
|
+
|
|
411
|
+
`TopAppBarAction` (the items of `actions`):
|
|
412
|
+
|
|
413
|
+
| Field | Type | Effect |
|
|
414
|
+
| --- | --- | --- |
|
|
415
|
+
| `label` | `string` | required: the icon button's accessible name and the menu entry's text |
|
|
416
|
+
| `icon` | `ReactNode` | decorative; the action is named by `label` |
|
|
417
|
+
| `onClick` | `() => void` | the action was chosen |
|
|
418
|
+
| `href` | `string` | makes the action a link, drawn with `linkComponent` |
|
|
419
|
+
| `placement` | `'bar' \| 'account'` | `'bar'` (default): a bar icon while there is room. `'account'`: always in the account menu |
|
|
420
|
+
| `group` | `string` | the menu section ("Language", "Theme"): entries sit under that label, inline (never a submenu, which overflows at 360 px); entries without one come first |
|
|
421
|
+
| `selected` | `boolean` | the current choice of its group: a group of non-link entries where one sets `selected` is a radio group (`menuitemradio`, `aria-checked`); a link entry with `selected` is the current page (`aria-current="page"`) |
|
|
422
|
+
| `disabled` | `boolean` | the action cannot be chosen |
|
|
423
|
+
| `id` | `string` | the key; defaults to `label` |
|
|
424
|
+
|
|
425
|
+
The `trailing` avatar becomes the menu's trigger (a `<button>`), so it must hold no
|
|
426
|
+
link, button or input (a warning in development). New message keys:
|
|
427
|
+
`material.top-app-bar.account` and `material.top-app-bar.more` (en, fr).
|
|
428
|
+
|
|
429
|
+
`BottomAppBar` is a row of icon actions that spaces its children and styles the
|
|
430
|
+
one with `data-state="active"`; it takes a `div`'s props.
|
|
364
431
|
|
|
365
432
|
<img src="../previews/navigation-layout-bottomappbar.webp" height="56" alt="BottomAppBar">
|
|
366
433
|
|
|
367
434
|
```tsx
|
|
368
|
-
import { Book, Camera
|
|
369
|
-
import { BottomAppBar, IconButton
|
|
370
|
-
|
|
371
|
-
<TopAppBar>
|
|
372
|
-
<IconButton variant="ghost" aria-label="Open menu"><Menu /></IconButton>
|
|
373
|
-
<Typography variant="title-md" emphasized className="flex-1">Inbox</Typography>
|
|
374
|
-
<IconButton variant="ghost" aria-label="Search"><Search /></IconButton>
|
|
375
|
-
</TopAppBar>
|
|
435
|
+
import { Book, Camera } from 'lucide-react';
|
|
436
|
+
import { BottomAppBar, IconButton } from '@nxgt/material/components';
|
|
376
437
|
|
|
377
438
|
<BottomAppBar>
|
|
378
439
|
<IconButton data-state="active" aria-label="Camera"><Camera /></IconButton>
|
|
@@ -380,22 +441,14 @@ import { BottomAppBar, IconButton, TopAppBar, Typography } from '@nxgt/material/
|
|
|
380
441
|
</BottomAppBar>
|
|
381
442
|
```
|
|
382
443
|
|
|
383
|
-
`
|
|
384
|
-
`data-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
the [app shell guide](../guides/app-shell.md#the-app-bar-variants)).
|
|
444
|
+
The root is a column (`flex-col`) whose children sit in a
|
|
445
|
+
`[data-slot="top-app-bar-row"]`: a `justify-between`, `gap-*` or `items-*` in the
|
|
446
|
+
root's `className` does not reach them (aim it at the row), and `title` is the
|
|
447
|
+
bar's `<h1>`, no longer the HTML `title` attribute. A `className` background,
|
|
448
|
+
radius or shadow wins at rest; once scrolled the tint replaces the background.
|
|
449
|
+
The shell's bar takes its height from `--nxgt-header-height`.
|
|
390
450
|
|
|
391
|
-
|
|
392
|
-
import { Menu } from 'lucide-react';
|
|
393
|
-
import { IconButton, TopAppBar } from '@nxgt/material/components';
|
|
394
|
-
|
|
395
|
-
<TopAppBar variant="large" headline={<h1>Projects</h1>}>
|
|
396
|
-
<IconButton variant="ghost" aria-label="Open menu"><Menu /></IconButton>
|
|
397
|
-
</TopAppBar>
|
|
398
|
-
```
|
|
451
|
+
Moving from `AppHeader` or the old props: [Moving to TopAppBar](../guides/migrating-to-top-app-bar.md).
|
|
399
452
|
|
|
400
453
|
**Storybook:** `Navigation & layout/TopAppBar`, `Navigation & layout/BottomAppBar` · **Vue:** `TopAppBar`, `BottomAppBar`
|
|
401
454
|
|
|
@@ -422,7 +475,7 @@ function Shell() {
|
|
|
422
475
|
<ActivityLayout
|
|
423
476
|
items={items}
|
|
424
477
|
header={{
|
|
425
|
-
|
|
478
|
+
size: 'small',
|
|
426
479
|
search: {
|
|
427
480
|
value: query,
|
|
428
481
|
onValueChange: setQuery,
|
package/docs/guides/app-shell.md
CHANGED
|
@@ -201,10 +201,11 @@ partitionMenu(items); // { content, footer }
|
|
|
201
201
|
| `linkComponent` | `AppShellLinkComponent` | React Router `Link` | The link each menu entry renders, given `href` (see below). Defaults to the [`LinkComponentProvider`](../components/primitives.md#linkcomponentprovider)'s link when one is set. |
|
|
202
202
|
| `headerTitle` | `ReactNode` | — | The header's leading title: the context the app is in (the active organisation's name), right after the phone's menu button, truncated when long; when it is cut off, its full text is the `title` tooltip (#629) |
|
|
203
203
|
| `titleAfter` | `ReactNode` | — | A node right after the page's title in the app bar, outside its `h1` (a record's `Switcher`); wins over a route's `handle.titleAfter` (see [A slot after the title](#a-slot-after-the-title)) |
|
|
204
|
-
| `headerContext` | `ReactNode` | — |
|
|
204
|
+
| `headerContext` | `ReactNode` | — | The bar's `subtitle`: a small muted context line (the tenant's name) under the page's title, from `md` up: outside the `h1`, truncated, its full text the tooltip once cut off. Not shown on a phone, where the menu sheet's `context` carries it (see [A context line](#a-context-line-after-the-title)) |
|
|
205
205
|
| `breadcrumb` | `boolean` | `true` | `false` hides the breadcrumb trail, for an error page whose matched routes would show a misleading one (see [Breadcrumbs](#breadcrumbs)) |
|
|
206
206
|
| `breadcrumbRoot` | `AppBreadcrumbRoot` (`{ label: ReactNode; to: string }`, or `{ label: string; to: string; icon: true }`) | — | The breadcrumb's first crumb, in place of the default home icon: a text link to `to`, or with `icon: true` the home icon alone, linked to `to` and named `label` (see [Breadcrumbs](#breadcrumbs)) |
|
|
207
|
-
| `headerActions` | `ReactNode` | — | In the header, before the avatar: notifications, help, `ThemeToggle
|
|
207
|
+
| `headerActions` | `ReactNode \| TopAppBarAction[]` | — | In the header, before the avatar: notifications, help, `ThemeToggle`; an array of data draws free icon buttons, the rest in the account menu |
|
|
208
|
+
| `headerPrimaryAction` | `TopAppBarPrimaryAction` (`{ label, icon, onClick?, disabled? }`) | — | The screen's main action in the header: a `Button` from 768 px, a `Fab` on a phone |
|
|
208
209
|
| `children` | `ReactNode` | — | The routed content, usually `<Outlet />` |
|
|
209
210
|
| `contentAs` | `'main' \| 'div'` | `'main'` | The content region's element: the page's `<main>` landmark, or a `div` when your app renders its own `<main>` |
|
|
210
211
|
| `scroll` | `'document' \| 'container'` | `'document'` | Who scrolls: the page (header and sidebar sticky), or the content region inside a shell one screen high |
|
|
@@ -212,7 +213,7 @@ partitionMenu(items); // { content, footer }
|
|
|
212
213
|
| `menuOpen` | `boolean` | — | Controls the phone's menu sheet: open while `true`. Leave it out and the layout manages the sheet. Only a phone shows the sheet |
|
|
213
214
|
| `onMenuOpenChange` | `(open: boolean) => void` | — | Called for every change of the sheet: the menu button, Escape, the close button, a followed link, a new location |
|
|
214
215
|
| `navigation` | `ShellNavigation \| { phone?, tablet?, desktop?, hideOnScroll?, persistKey? }` | `modal-drawer` / `rail` / `rail` | The navigation per window size; see [`navigation` and `header`](#navigation-and-header) |
|
|
215
|
-
| `header` | `'small' \| '
|
|
216
|
+
| `header` | `'small' \| 'medium-flexible' \| 'large-flexible' \| 'search' \| 'none' \| { size?, align?, collapse?, hideOnScroll?, tabs?, search? }` | `'small'` | The app bar's size, overridable per route with `handle.header`; the old `'center'`, `'medium'`, `'large'` and `variant` are deprecated; see [Top app bar sizes](#top-app-bar-sizes) |
|
|
216
217
|
| `backdrop` | `ReactNode \| false` | — | An exception to the theme's background (the page ground, [`--ground`](#the-page-ground), is painted either way), which the layout draws by default (Lavande's: the built-in gradient and blobs). A node replaces it for this screen: usually a `Background` preset from `@nxgt/material/backgrounds` (see [Use a background preset behind the shell](#use-a-background-preset-behind-the-shell)); `false` draws no background, a plain page |
|
|
217
218
|
| `keepClear` | `boolean \| 'gaps'` | `true` | how the backdrop's glyphs keep clear of the page: `true` drops every glyph within 8px of a surface, a text or a control; `'gaps'` also keeps a glyph that fits in a gap of 24px or more between two surfaces; `false` keeps nothing clear (the edge and the header band still are). See [Keep the backdrop clear of the content](#keep-the-backdrop-clear-of-the-content) |
|
|
218
219
|
|
|
@@ -250,11 +251,49 @@ const MyLink: AppShellLinkComponent = ({ href, ...props }) => <Link to={href} {.
|
|
|
250
251
|
// <ActivityLayout items={items} linkComponent={MyLink}>…</ActivityLayout>
|
|
251
252
|
```
|
|
252
253
|
|
|
254
|
+
Actions as data: `headerActions` also takes a `TopAppBarAction[]` (`label`,
|
|
255
|
+
`icon`, `onClick` or `href`, `placement: 'bar' | 'account'`, `group`, `selected`,
|
|
256
|
+
`disabled`). The first ones are free icon buttons in the bar (one on a phone, two
|
|
257
|
+
from 768 px); the others, and every `'account'` action, go to the menu opened by
|
|
258
|
+
the `avatar`, which then must hold no link or button. `headerPrimaryAction` is
|
|
259
|
+
the screen's main action:
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
<ActivityLayout
|
|
263
|
+
items={items}
|
|
264
|
+
avatar={<Avatar src={user.photo} />}
|
|
265
|
+
headerActions={[
|
|
266
|
+
{ label: 'Notifications', icon: <Icon name="bell" />, onClick: openInbox },
|
|
267
|
+
{ label: 'Help', icon: <Icon name="circle-question" />, href: '/help' },
|
|
268
|
+
{ label: 'Sign out', placement: 'account', onClick: signOut },
|
|
269
|
+
]}
|
|
270
|
+
headerPrimaryAction={{ label: 'New invoice', icon: <Icon name="plus" />, onClick: create }}
|
|
271
|
+
>
|
|
272
|
+
<Outlet />
|
|
273
|
+
</ActivityLayout>
|
|
274
|
+
```
|
|
275
|
+
|
|
253
276
|
`className` and the rest of the `div` props (`id`, `data-*`…) go to the outer
|
|
254
277
|
element, `[data-slot=activity-layout]`. The sticky app bar (menu button,
|
|
255
278
|
`headerTitle`, `headerActions` and avatar) is `[data-slot=app-header]`, so a
|
|
256
279
|
test or an accessibility helper can name it without a structural selector;
|
|
257
|
-
`AppHeader` carries the same slot when used on
|
|
280
|
+
`AppHeader` (deprecated, use `TopAppBar`) carries the same slot when used on
|
|
281
|
+
its own. The bar is a `TopAppBar` drawn as the page's `<header>` banner; it keeps
|
|
282
|
+
the `app-header*` slot names below, which `TopAppBar` styles too. They become
|
|
283
|
+
`top-app-bar*` at the next major.
|
|
284
|
+
|
|
285
|
+
| `data-slot` | Marks |
|
|
286
|
+
| --- | --- |
|
|
287
|
+
| `app-header` | the bar's root (`<header>`) |
|
|
288
|
+
| `app-header-page-title` | the page's `h1` |
|
|
289
|
+
| `app-header-context` | the muted line under the title (`headerContext`) |
|
|
290
|
+
| `app-header-headline` | the second row of a medium or large bar |
|
|
291
|
+
| `app-header-collapsed-title` | the title's copy in the small row of a collapsing bar |
|
|
292
|
+
| `app-header-tabs` | the tab row under the bar |
|
|
293
|
+
| `app-header-title` | the leading `headerTitle` |
|
|
294
|
+
| `app-header-title-after` | the slot after the title (`titleAfter`) |
|
|
295
|
+
| `app-header-back` | the back arrow |
|
|
296
|
+
|
|
258
297
|
`avatar` and `headerActions` are props because the shell holds no session:
|
|
259
298
|
read the user wherever your app keeps it and pass the node.
|
|
260
299
|
|
|
@@ -328,7 +367,7 @@ today: the menu sheet on a phone, the rail from `md`, a small header.
|
|
|
328
367
|
<ActivityLayout
|
|
329
368
|
items={items}
|
|
330
369
|
navigation={{ phone: 'modal-drawer', tablet: 'rail', desktop: 'rail' }}
|
|
331
|
-
header={{
|
|
370
|
+
header={{ size: 'small' }}
|
|
332
371
|
>
|
|
333
372
|
<Outlet />
|
|
334
373
|
</ActivityLayout>
|
|
@@ -346,9 +385,10 @@ today: the menu sheet on a phone, the rail from `md`, a small header.
|
|
|
346
385
|
name alone draws them at every size, the phone included. Every name is drawn:
|
|
347
386
|
`'modal-drawer'`, `'rail'`, `'rail-expanded'`, `'drawer'`, `'sidebar'`, `'top'`,
|
|
348
387
|
`'two-level'`, `'floating'`, `'bar'` and `'none'`.
|
|
349
|
-
- `header`: `'small'`, `'
|
|
350
|
-
`{
|
|
351
|
-
`
|
|
388
|
+
- `header`: `'small'`, `'medium-flexible'`, `'large-flexible'`, `'search'` or
|
|
389
|
+
`'none'`, or `{ size, align, collapse, hideOnScroll, tabs, search }` (the old
|
|
390
|
+
`'center'`, `'medium'`, `'large'` and `variant` are deprecated). The sizes and
|
|
391
|
+
`hideOnScroll` are drawn (see [Top app bar sizes](#top-app-bar-sizes));
|
|
352
392
|
`search` and `tabs` are drawn (see [The app bar's search](#the-app-bars-search) and
|
|
353
393
|
[Tabs under the app bar](#tabs-under-the-app-bar)). A route's `handle.back` draws a
|
|
354
394
|
back arrow ([The back arrow](#the-back-arrow)).
|
|
@@ -676,71 +716,122 @@ the user asked for less motion: skip the slide and snap. The header
|
|
|
676
716
|
(`navigation.hideOnScroll`) read it; the shell tracks the scroll only when one of
|
|
677
717
|
them asks, and `NavigationToolbar` reads it by hand too.
|
|
678
718
|
|
|
679
|
-
###
|
|
719
|
+
### Top app bar sizes
|
|
680
720
|
|
|
681
|
-
`header` chooses the bar's size, the way Material 3 does
|
|
682
|
-
|
|
721
|
+
`header` chooses the bar's size, the way Material 3 does, and the shell draws it
|
|
722
|
+
with [`TopAppBar`](../components/navigation-and-layout.md#topappbar). Unset is
|
|
723
|
+
`'small'`: one row, the title beside the actions.
|
|
683
724
|
|
|
684
725
|
```tsx
|
|
685
726
|
import { ActivityLayout, type AppShellHandle } from '@nxgt/material/components';
|
|
686
727
|
|
|
687
|
-
<ActivityLayout items={items} header="large">
|
|
728
|
+
<ActivityLayout items={items} header="large-flexible">
|
|
688
729
|
<Outlet />
|
|
689
730
|
</ActivityLayout>
|
|
690
731
|
|
|
691
732
|
// a route opts in, whatever the layout says
|
|
692
|
-
export const handle = { title: 'Projects', header: 'large' } satisfies AppShellHandle;
|
|
733
|
+
export const handle = { title: 'Projects', header: 'large-flexible' } satisfies AppShellHandle;
|
|
693
734
|
|
|
694
|
-
// a bar that goes away on the way down
|
|
695
|
-
<ActivityLayout items={items} header={{
|
|
735
|
+
// a centred small bar that goes away on the way down
|
|
736
|
+
<ActivityLayout items={items} header={{ size: 'small', align: 'center', hideOnScroll: true }} />
|
|
696
737
|
```
|
|
697
738
|
|
|
698
|
-
Defaults: `
|
|
699
|
-
|
|
700
|
-
bar's CSS ships with it.
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
the
|
|
719
|
-
the
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
The
|
|
739
|
+
Defaults: `size` is `'small'`, `align` is `'start'`, `collapse` is `true` for the
|
|
740
|
+
two flexible sizes, `hideOnScroll` is `false`. Nothing to import beyond
|
|
741
|
+
`styles.css`: the bar's CSS ships with it.
|
|
742
|
+
|
|
743
|
+
| Size | Use it for | What it draws |
|
|
744
|
+
| --- | --- | --- |
|
|
745
|
+
| `'small'` | most screens, a dense tool | one row: the title beside the actions. `align: 'center'` centres the title between the menu button and the actions |
|
|
746
|
+
| `'medium-flexible'` | a section's landing screen | the page's title on a second row (28 px), under the small row |
|
|
747
|
+
| `'large-flexible'` | a screen that opens a flow or a long list | the title on a second row, bigger (40 px, 36 px on a phone) and up to two lines |
|
|
748
|
+
| `'search'` | a search-first screen | the `header.search` field is the bar's centre; the title stays in the page as a visually hidden `h1`, so the screen keeps its heading |
|
|
749
|
+
| `'none'` | a full-screen tool that draws its own chrome | no bar. The screen's own `ActivityContent` title is then the page's `h1`; the phone's menu button goes with the bar, so pair it with a navigation that has one on its own (`navigation="bar"`) |
|
|
750
|
+
|
|
751
|
+
- **Collapse.** A flexible bar folds its second row into the small row as the
|
|
752
|
+
page scrolls: the headline fades and the bar shrinks over the headline row's
|
|
753
|
+
measured height, the title shrinks to 90% from its start edge, and the small
|
|
754
|
+
row then shows the title (a copy hidden from assistive technology). A scrolled
|
|
755
|
+
bar takes a soft tint and a 1px hairline. `collapse: false` keeps the headline
|
|
756
|
+
however far the page scrolls.
|
|
757
|
+
- **`hideOnScroll`**: the bar slides away while the page scrolls down (past 64px)
|
|
758
|
+
and comes back on the way up. While away it is `inert` and out of the tab order;
|
|
759
|
+
if the focus was inside it, the focus moves to the content region.
|
|
760
|
+
- **Subtitle.** `headerContext` is the bar's subtitle, a muted line under the title
|
|
761
|
+
(see [A context line after the title](#a-context-line-after-the-title)).
|
|
762
|
+
- **Actions and main action.** `headerActions` takes nodes or a `TopAppBarAction[]`,
|
|
763
|
+
and `headerPrimaryAction` the screen's main action (see
|
|
764
|
+
[The app bar](#the-app-bar-a-title-the-actions-the-account)).
|
|
765
|
+
|
|
766
|
+
The bar's height is `--nxgt-header-height` (default `4rem`, see
|
|
767
|
+
[the header height](./theming.md#the-header-height)): the row is that height
|
|
768
|
+
less the bar's own vertical padding, so a custom value resizes the bar, the
|
|
769
|
+
rail's header and the first-paint total together. A `className` on the bar (a
|
|
770
|
+
background, a radius, a shadow) wins over its at-rest look; once the page
|
|
771
|
+
scrolls, the scrolled tint replaces a `className` background. The bar is drawn
|
|
772
|
+
as the page's `<header>`; `title` on a standalone `TopAppBar` is its `<h1>` (it
|
|
773
|
+
was the HTML `title` attribute), and its root is a column whose children sit in
|
|
774
|
+
a `[data-slot="top-app-bar-row"]`, so layout utilities on the root's `className`
|
|
775
|
+
(`justify-between`, `gap-*`, `items-*`) no longer reach them (see
|
|
776
|
+
[Moving to TopAppBar](./migrating-to-top-app-bar.md#what-looks-different)).
|
|
777
|
+
|
|
778
|
+
A route gives its own through `handle.header`: a size (`'large-flexible'`) or the
|
|
779
|
+
options (`{ size, align, collapse, hideOnScroll, tabs, search }`, like the prop).
|
|
780
|
+
The deepest route that declares one wins, and its options win over the layout's
|
|
781
|
+
one by one: a route that only names a size keeps the layout's `hideOnScroll` and
|
|
782
|
+
`align`. It is read from the matched routes, so the server and the browser draw
|
|
783
|
+
the same bar.
|
|
784
|
+
|
|
785
|
+
The title **is** the page's one `h1` (the small row's copy is `aria-hidden`).
|
|
724
786
|
A medium or large header needs a title in the bar, a route's `handle.title`;
|
|
725
787
|
without one the bar stays small, so nothing changes for a screen that titles
|
|
726
788
|
itself with `ActivityContent`. The server always renders the bar expanded and
|
|
727
789
|
shown; the browser then follows the scroll.
|
|
728
790
|
|
|
791
|
+
**The search size.** `header: { size: 'search', search: { placeholder: 'Search projects' } }`
|
|
792
|
+
puts the search field at the bar's centre. A route that sets a `handle.title`
|
|
793
|
+
still gets its `h1`, visually hidden but in the accessibility tree and
|
|
794
|
+
labelling `<main>`; without a title the screen's own `ActivityContent` title is
|
|
795
|
+
the heading, as for any small bar. See [The app bar's search](#the-app-bars-search)
|
|
796
|
+
for the field's props.
|
|
797
|
+
|
|
798
|
+
**Phone and desktop.**
|
|
799
|
+
|
|
800
|
+
- On a phone the menu button is a ghost icon button leading the bar, with no
|
|
801
|
+
brand or logo; the bar keeps one free icon action, the rest go to the account
|
|
802
|
+
menu as inline sections. `headerPrimaryAction` is a floating button above the
|
|
803
|
+
bottom navigation. `headerContext` is hidden (the sheet's `context` covers it).
|
|
804
|
+
- From 768 px two icon actions fit, `headerPrimaryAction` is a `Button` in the
|
|
805
|
+
bar, and `headerContext` shows under the title.
|
|
806
|
+
- Heights: the headline row of a medium bar is 3 rem and a large bar's 6.5 rem
|
|
807
|
+
before it is measured, then follows the title's real height.
|
|
808
|
+
|
|
809
|
+
**Reduced motion.** Under `prefers-reduced-motion: reduce` the collapse snaps
|
|
810
|
+
between the two states at the halfway point, with no morph, no slide for
|
|
811
|
+
`hideOnScroll` and no transition on the bar's tint.
|
|
812
|
+
|
|
729
813
|
The scroll is the shell's ([Scroll direction](#scroll-direction)): the window,
|
|
730
814
|
or the content region with `scroll="container"` (a hidden bar then gives its
|
|
731
815
|
room back). Once per animation frame the bar sets one CSS variable on itself,
|
|
732
|
-
`--nxgt-header-progress` (0 expanded, 1 collapsed), through `setProperty`,
|
|
733
|
-
no inline style reaches the markup
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
816
|
+
`--nxgt-header-progress` (0 expanded, 1 collapsed), through `setProperty`, and
|
|
817
|
+
`data-scrolled` once the page has moved, so no inline style reaches the markup
|
|
818
|
+
and a frame of scrolling does not re-render. A plain shell, with no collapsing
|
|
819
|
+
or hiding bar, tracks no scroll at all.
|
|
820
|
+
|
|
821
|
+
**Deprecated spellings.** `'center'`, `'medium'`, `'large'` and `variant` in the
|
|
822
|
+
options still work and log a one-time development warning (see
|
|
823
|
+
[troubleshooting](../troubleshooting.md#activitylayout-header-center-medium-or-large-is-deprecated)):
|
|
824
|
+
`'center'` is `{ size: 'small', align: 'center' }`, `'medium'` is
|
|
825
|
+
`'medium-flexible'`, `'large'` is `'large-flexible'`. See
|
|
826
|
+
[Moving to TopAppBar](./migrating-to-top-app-bar.md).
|
|
827
|
+
|
|
828
|
+
`TopAppBar` takes the same as props (`size`, `align`, `collapse`, `hideOnScroll`,
|
|
829
|
+
`title`, `subtitle`, `tabs`, `search`) for a bar of your own; `AppHeader` is its
|
|
830
|
+
deprecated wrapper (see [Using the parts](#using-the-parts)). Known limits: a
|
|
831
|
+
hidden bar in a `scroll="container"` shell that is barely taller than its
|
|
832
|
+
container can flicker at the very bottom (the region grows, so the scroll
|
|
833
|
+
clamps); the scroll-driven CSS animation enhancement is not used, the JavaScript
|
|
834
|
+
path drives every browser.
|
|
744
835
|
|
|
745
836
|
### The app bar's search
|
|
746
837
|
|
|
@@ -755,13 +846,13 @@ import { ActivityLayout } from '@nxgt/material/components';
|
|
|
755
846
|
<ActivityLayout
|
|
756
847
|
items={items}
|
|
757
848
|
header={{
|
|
758
|
-
|
|
849
|
+
size: 'small',
|
|
759
850
|
search: { placeholder: 'Search projects', shortcut: '/', onSubmit: (q) => navigate(`/search?q=${q}`) },
|
|
760
851
|
}}
|
|
761
852
|
/>
|
|
762
853
|
|
|
763
854
|
// a route turns it off, or changes it, like the other options
|
|
764
|
-
export const handle = { title: 'Settings', header: {
|
|
855
|
+
export const handle = { title: 'Settings', header: { size: 'small', search: false } };
|
|
765
856
|
```
|
|
766
857
|
|
|
767
858
|
The search never changes the shape of the page: the content stays mounted when
|
|
@@ -792,7 +883,7 @@ const tabs = (
|
|
|
792
883
|
);
|
|
793
884
|
|
|
794
885
|
export function Layout({ items }: { items: MenuItem[] }) {
|
|
795
|
-
return <ActivityLayout items={items} header={{
|
|
886
|
+
return <ActivityLayout items={items} header={{ size: 'small', tabs }} />;
|
|
796
887
|
}
|
|
797
888
|
```
|
|
798
889
|
|
|
@@ -809,13 +900,13 @@ const projectTabs = (
|
|
|
809
900
|
|
|
810
901
|
export const handle = {
|
|
811
902
|
title: 'Atlas',
|
|
812
|
-
header: {
|
|
903
|
+
header: { size: 'small', tabs: projectTabs }, // tabs: null drops the layout's
|
|
813
904
|
} satisfies AppShellHandle;
|
|
814
905
|
```
|
|
815
906
|
|
|
816
907
|
The option is `header.tabs` (there is no `handle.headerTabs`): a route's `tabs`
|
|
817
908
|
replaces the layout's whole, `tabs: null` drops them for that route, and a route
|
|
818
|
-
that only names a
|
|
909
|
+
that only names a size keeps the layout's. The row's height is one CSS
|
|
819
910
|
variable, `--nxgt-header-tabs` (3rem, in `app-header.css`). The bar's whole
|
|
820
911
|
height (the row, a headline row and the tabs) is `--nxgt-header-total` on the
|
|
821
912
|
`ActivityLayout` root, measured with a `ResizeObserver` (worked out in CSS from
|
|
@@ -1045,14 +1136,14 @@ A multi-tenant app keeps the current tenant visible on a desktop screen with
|
|
|
1045
1136
|
```
|
|
1046
1137
|
|
|
1047
1138
|
- **It is outside the `h1`** (`[data-slot=app-header-context]`), so the
|
|
1048
|
-
heading's name stays the page's title; it
|
|
1049
|
-
its full text is the `title` tooltip once it is cut off (#629).
|
|
1139
|
+
heading's name stays the page's title; it sits under the title (the bar's
|
|
1140
|
+
subtitle), is truncated to the title's width, and its full text is the `title` tooltip once it is cut off (#629).
|
|
1050
1141
|
- **From `md` up only.** On a phone it is hidden: the menu sheet's `context`
|
|
1051
1142
|
line covers it. The two props are independent, so setting `context` alone
|
|
1052
1143
|
adds nothing to the bar, and setting both never shows the name twice on one
|
|
1053
1144
|
screen.
|
|
1054
1145
|
- **The title truncates first**: with a long title, the slot and the line
|
|
1055
|
-
together, the title gives way (two lines on a phone, then an ellipsis); the slot stays whole and the line
|
|
1146
|
+
together, the title gives way (two lines on a phone, then an ellipsis); the slot stays whole and the line stays under the title.
|
|
1056
1147
|
- **It needs a page title**: while the bar shows none (no `handle.title`),
|
|
1057
1148
|
`headerTitle` is the context line and `headerContext` is ignored.
|
|
1058
1149
|
|
|
@@ -1793,8 +1884,8 @@ export function InvoiceTrail() {
|
|
|
1793
1884
|
|
|
1794
1885
|
## Using the parts
|
|
1795
1886
|
|
|
1796
|
-
`AppSidebar`, `AppHeader` and `AppBreadcrumb` are
|
|
1797
|
-
own. They take the same `items`; `AppHeader` takes `actions`, `avatar`,
|
|
1887
|
+
`AppSidebar`, `AppHeader` (deprecated, use `TopAppBar`) and `AppBreadcrumb` are
|
|
1888
|
+
exported for a layout of your own. They take the same `items`; `AppHeader` takes `actions`, `avatar`,
|
|
1798
1889
|
`showMenuIcon` (default `true`), and `menuOpen` / `onMenuOpenChange` to control
|
|
1799
1890
|
its phone menu sheet, `tabs` (a node, default none: a `TabsNav` drawn as a row
|
|
1800
1891
|
at the foot of the bar) and `back` (a `ShellBack`, default none: the back arrow,
|
|
@@ -1802,6 +1893,10 @@ before the menu button). The lower-level `Sidebar`, `TopAppBar` and
|
|
|
1802
1893
|
`Breadcrumb` primitives are on the
|
|
1803
1894
|
[navigation & layout](../components/navigation-and-layout.md) page.
|
|
1804
1895
|
|
|
1896
|
+
The shell draws its bar with `TopAppBar`, as the page's `<header>`. `AppHeader`
|
|
1897
|
+
is deprecated: it stays as a wrapper over `TopAppBar` with the same props, and
|
|
1898
|
+
warns once in development.
|
|
1899
|
+
|
|
1805
1900
|
`AppHeader` needs a React Router context even on its own: its menu sheet reads
|
|
1806
1901
|
the location (`useLocation`) to close when it changes, and outside a router
|
|
1807
1902
|
that hook throws. Render it under your `RouterProvider` — or, in a test, inside
|
package/docs/guides/drawing.md
CHANGED
|
@@ -187,6 +187,8 @@ A fill, a stroke or a gradient stop is a **role**, a **tone** or a free colour.
|
|
|
187
187
|
`outline-variant`, `success`, `info`, `warning`, `neutral`. `ROLES` lists them
|
|
188
188
|
and `ROLE_VARS` gives each one's CSS variable (`on-primary-container` is
|
|
189
189
|
`--primary-text`, `outline` is `--muted-foreground`).
|
|
190
|
+
The drawing role `surface-container` resolves to `--card`; it is not the
|
|
191
|
+
`--surface-container` theme token (`bg-surface-container`).
|
|
190
192
|
They follow light and dark, scoped themes and a retheme with no re-render.
|
|
191
193
|
- **Tones**: `tone('success')`, `tone('success', 'container')` (variants `base`,
|
|
192
194
|
`text`, `on`, `container`) name a theme tone or an app tone of your own, the
|
package/docs/guides/layouts.md
CHANGED
|
@@ -75,7 +75,7 @@ layout of your own. Every preset, its entries (`bar`, `priority`, `group`,
|
|
|
75
75
|
<ActivityLayout
|
|
76
76
|
items={items}
|
|
77
77
|
header={{
|
|
78
|
-
|
|
78
|
+
size: 'large-flexible',
|
|
79
79
|
hideOnScroll: true,
|
|
80
80
|
search: { placeholder: 'Search projects', shortcut: '/' },
|
|
81
81
|
tabs: (
|
|
@@ -95,13 +95,13 @@ A route changes its own bar with a handle, and draws a back arrow:
|
|
|
95
95
|
```tsx
|
|
96
96
|
export const handle = {
|
|
97
97
|
title: 'Atlas redesign',
|
|
98
|
-
header: {
|
|
98
|
+
header: { size: 'medium-flexible' },
|
|
99
99
|
back: { to: '/projects', label: 'Projects' },
|
|
100
100
|
} satisfies AppShellHandle;
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
The search is a pill with a docked results panel from 768px and a full-screen
|
|
104
|
-
view on a phone. See [
|
|
104
|
+
view on a phone. See [Top app bar sizes](app-shell.md#top-app-bar-sizes),
|
|
105
105
|
[the search](app-shell.md#the-app-bars-search),
|
|
106
106
|
[the tabs](app-shell.md#tabs-under-the-app-bar) and
|
|
107
107
|
[the back arrow](app-shell.md#the-back-arrow).
|