@nxgt/material-vue 0.41.0 → 0.43.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/CHANGELOG.md +124 -0
- package/README.md +20 -6
- package/dist/{Button-BG3oDkrl.js → Button-C3K6dhqd.js} +4 -4
- package/dist/{HoverCardTrigger-Dh1ar_W4.js → HoverCardTrigger-Bga4YjEr.js} +69 -62
- package/dist/backgrounds/backgrounds.css +8 -0
- package/dist/backgrounds/css/base.css +169 -0
- package/dist/backgrounds/css/gradient.css +53 -0
- package/dist/backgrounds/css/grain.css +19 -0
- package/dist/backgrounds/css/motion.css +30 -0
- package/dist/backgrounds.js +111 -0
- package/dist/{camera-DV9jcl_3.js → camera-BbF9_-ys.js} +4 -4
- package/dist/components/base.css +5 -2
- package/dist/components/components.css +5 -0
- package/dist/components/theme-tokens.css +21 -0
- package/dist/{components-C1JYYoQ4.js → components-yCdbry6Q.js} +13880 -13101
- package/dist/components.js +8 -8
- package/dist/composables.js +3 -3
- package/dist/dnd.js +1 -1
- package/dist/{form-scope-CeD0v_mw.js → form-scope-CkWJrBCu.js} +1 -1
- package/dist/{i18n-provider-D5XyLQ3P.js → i18n-provider-tL27-a8y.js} +1 -1
- package/dist/i18n.js +4 -4
- package/dist/{icon-button-DXrmfs6e.js → icon-button-MyFFJpXA.js} +4 -4
- package/dist/lib/backgrounds/background/background.vue.d.ts +22 -0
- package/dist/lib/backgrounds/background/index.d.ts +2 -0
- package/dist/lib/backgrounds/background/partials/background-layers.vue.d.ts +9 -0
- package/dist/lib/backgrounds/background/partials/scene-svg.d.ts +10 -0
- package/dist/lib/backgrounds/background/types.d.ts +24 -0
- package/dist/lib/backgrounds/index.d.ts +13 -0
- package/dist/lib/components/ui/actions/button/button-tones.d.ts +3 -3
- package/dist/lib/components/ui/actions/responsive-button/responsive-button.vue.d.ts +3 -3
- package/dist/lib/components/ui/collections/data-table/data-table-context.d.ts +4 -0
- package/dist/lib/components/ui/collections/data-table/data-table.vue.d.ts +8 -0
- package/dist/lib/components/ui/collections/data-table/partials/data-table-body.vue.d.ts +8 -0
- package/dist/lib/components/ui/collections/data-table/partials/data-table-empty.vue.d.ts +5 -1
- package/dist/lib/components/ui/collections/data-table/partials/data-table-toolbar.d.ts +16 -3
- package/dist/lib/components/ui/collections/data-table/select-checkbox.d.ts +36 -0
- package/dist/lib/components/ui/collections/data-table/types.d.ts +15 -0
- package/dist/lib/components/ui/collections/data-table/use-data-table.d.ts +2 -0
- package/dist/lib/components/ui/feedback/circular-progress/circular-progress.vue.d.ts +1 -1
- package/dist/lib/components/ui/feedback/empty-state/empty-state.vue.d.ts +21 -8
- package/dist/lib/components/ui/feedback/empty-state/index.d.ts +1 -1
- package/dist/lib/components/ui/feedback/error-state/error-state.vue.d.ts +54 -0
- package/dist/lib/components/ui/feedback/error-state/index.d.ts +3 -0
- package/dist/lib/components/ui/feedback/error-state/partials/error-state-details.vue.d.ts +7 -0
- package/dist/lib/components/ui/feedback/index.d.ts +1 -0
- package/dist/lib/components/ui/feedback/state-frame/state-frame.vue.d.ts +36 -0
- package/dist/lib/components/ui/feedback/state-frame/state-icon.vue.d.ts +7 -0
- package/dist/lib/components/ui/feedback/state-frame/state-illustration.vue.d.ts +8 -0
- package/dist/lib/components/ui/feedback/state-frame/state-tones.d.ts +11 -0
- package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
- package/dist/lib/components/ui/layout/account-menu/account-menu-avatar.vue.d.ts +9 -0
- package/dist/lib/components/ui/layout/account-menu/account-menu.vue.d.ts +38 -0
- package/dist/lib/components/ui/layout/account-menu/index.d.ts +3 -0
- package/dist/lib/components/ui/layout/account-menu/types.d.ts +10 -0
- package/dist/lib/components/ui/layout/app-shell/activity-layout.vue.d.ts +21 -5
- package/dist/lib/components/ui/layout/app-shell/app-brand-logo.vue.d.ts +20 -0
- package/dist/lib/components/ui/layout/app-shell/app-brand.vue.d.ts +30 -0
- package/dist/lib/components/ui/layout/app-shell/app-header.vue.d.ts +13 -5
- package/dist/lib/components/ui/layout/app-shell/app-sidebar.vue.d.ts +12 -2
- package/dist/lib/components/ui/layout/app-shell/index.d.ts +1 -1
- package/dist/lib/components/ui/layout/app-shell/types.d.ts +10 -0
- package/dist/lib/components/ui/layout/app-shell/use-truncated-title.d.ts +10 -0
- package/dist/lib/components/ui/layout/background/background.vue.d.ts +18 -3
- package/dist/lib/components/ui/layout/doc/partials/doc-empty.vue.d.ts +7 -4
- package/dist/lib/components/ui/layout/index.d.ts +1 -0
- package/dist/lib/components/ui/media/stx-player/stx-player-viewport.vue.d.ts +2 -2
- package/dist/lib/components/ui/overlays/command/CommandSeparator.vue.d.ts +3 -1
- package/dist/lib/components/ui/overlays/dialog/in-dialog.d.ts +1 -1
- package/dist/lib/components/ui/overlays/dialog/use-escape-forward.d.ts +13 -0
- package/dist/lib/components/ui/overlays/dialog/use-modal-content.d.ts +2 -0
- package/dist/lib/components/ui/primitives/nonce-provider/nonce-provider.vue.d.ts +4 -1
- package/dist/lib/components/ui/selection/autocomplete/autocomplete-keyboard.d.ts +22 -4
- package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-box.vue.d.ts +6 -2
- package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-list.vue.d.ts +7 -0
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-active.d.ts +17 -0
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-keydown.d.ts +21 -0
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-open.d.ts +3 -3
- package/dist/lib/components/ui/selection/autocomplete/use-autocomplete.d.ts +2 -1
- package/dist/lib/components/ui/selection/autocomplete/use-drawer-room.d.ts +11 -0
- package/dist/lib/components/ui/selection/autocomplete/use-query-change.d.ts +8 -0
- package/dist/lib/components/ui/widgets/charts/chart-container.vue.d.ts +2 -2
- package/dist/lib/components/ui/widgets/kanban/partials/kanban-empty.vue.d.ts +1 -0
- package/dist/lib/composables/use-background-activity.d.ts +8 -0
- package/dist/lib/i18n/resources/en/en.d.ts +28 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +29 -1
- package/dist/lib/i18n/resources/fr/fr.d.ts +28 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +29 -1
- package/dist/lib/i18n/resources/resources.d.ts +56 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib/lib/index.d.ts +1 -0
- package/dist/lib/nuxt/component-names.d.ts +1 -1
- package/dist/lib/nuxt/module.d.ts +2 -11
- package/dist/lib/nuxt/pdf-fonts.d.ts +1 -1
- package/dist/lib/nuxt/plugin-options.d.ts +58 -0
- package/dist/lib/vite/index.d.ts +4 -8
- package/dist/lib.js +52 -51
- package/dist/main.js +17 -17
- package/dist/map.js +4 -4
- package/dist/material-vue.css +1 -1
- package/dist/{pdf-outline-sidebar-CgmJjhUz.js → pdf-outline-sidebar-Kqsfd_cO.js} +2 -2
- package/dist/{pdf-page-BjQNWV72.js → pdf-page-Dx3jwxu6.js} +1 -1
- package/dist/{pdf-page-list-nG0l4SjH.js → pdf-page-list-BsQmEHaY.js} +2 -2
- package/dist/{pdf-reader-Dekd4Ymw.js → pdf-reader-CJo8SQVl.js} +15 -15
- package/dist/{pdf-sidebar-gDj63dRH.js → pdf-sidebar-CcCUATl-.js} +3 -3
- package/dist/{pdf-thumbnail-sidebar-C5ikii1Z.js → pdf-thumbnail-sidebar-BRxNRGfG.js} +4 -4
- package/dist/{pdf-toolbar-DmmrSlCq.js → pdf-toolbar-B0ckYBJ2.js} +4 -4
- package/dist/pdf.js +2 -2
- package/dist/{print-format.utils-CiUiQE-i.js → print-format.utils-51joHpFb.js} +2 -2
- package/dist/{sortable-provider-lK0srHJF.js → sortable-provider-BEx1B0Ky.js} +2 -2
- package/dist/{stored-language-dM2e5qaS.js → stored-language-DVP9KzRu.js} +58 -2
- package/dist/{stx-player-C2VQCXFc.js → stx-player-KPp9c7vS.js} +5 -5
- package/dist/styles.css +4 -0
- package/dist/{translator-DXL5RDRg.js → translator-Bm7i7dv-.js} +1 -1
- package/dist/use-background-activity-DdTBbNpX.js +17 -0
- package/dist/{use-heading-spy-Btwc0yad.js → use-heading-spy-jn8f2ioE.js} +13 -13
- package/dist/{use-material-translation-Du3Wvsk1.js → use-material-translation-CfFZPSe0.js} +3 -3
- package/dist/vite.js +7 -2
- package/docs/README.md +3 -0
- package/docs/components/README.md +2 -1
- package/docs/components/buttons-and-actions.md +2 -0
- package/docs/components/data-table-filter-and-lists.md +59 -8
- package/docs/components/feedback-and-status.md +79 -8
- package/docs/components/media-and-files.md +2 -0
- package/docs/components/navigation-and-layout.md +68 -5
- package/docs/components/overlays-and-menus.md +20 -3
- package/docs/components/primitives.md +5 -3
- package/docs/components/rich-widgets.md +1 -1
- package/docs/components/selection-and-choice.md +14 -4
- package/docs/getting-started.md +22 -3
- package/docs/guides/app-shell.md +99 -19
- package/docs/guides/backgrounds.md +143 -0
- package/docs/guides/csp.md +48 -6
- package/docs/guides/helpers.md +1 -0
- package/docs/guides/i18n.md +52 -2
- package/docs/guides/migrate-to-vite-plugin.md +259 -0
- package/docs/guides/pdf.md +11 -4
- package/docs/guides/ssr.md +1 -1
- package/docs/guides/theming.md +52 -4
- package/docs/guides/vite-plugin.md +194 -0
- package/docs/previews/backgrounds-matrix.webp +0 -0
- package/docs/previews/feedback-status-emptystate.webp +0 -0
- package/docs/previews/feedback-status-errorstate.webp +0 -0
- package/docs/previews/navigation-layout-accountmenu.webp +0 -0
- package/docs/roadmap.md +69 -32
- package/docs/troubleshooting.md +251 -7
- package/lib/backgrounds/backgrounds.css +8 -0
- package/lib/backgrounds/css/base.css +169 -0
- package/lib/backgrounds/css/gradient.css +53 -0
- package/lib/backgrounds/css/grain.css +19 -0
- package/lib/backgrounds/css/motion.css +30 -0
- package/lib/components/base.css +5 -2
- package/lib/components/components.css +5 -0
- package/lib/components/theme-tokens.css +21 -0
- package/lib/nuxt/component-names.ts +2 -0
- package/lib/nuxt/module.ts +29 -15
- package/lib/nuxt/pdf-fonts.ts +1 -1
- package/lib/nuxt/plugin-options.ts +92 -0
- package/lib/nuxt/runtime/inter.css +6 -0
- package/lib/styles.css +4 -0
- package/lib/vite/index.ts +19 -2
- package/package.json +24 -2
|
@@ -11,7 +11,8 @@ centre. Everything is imported from `@nxgt/material-vue/components`.
|
|
|
11
11
|
| <img src="../previews/feedback-status-alertdialog.webp" width="96" alt=""> | [AlertDialog](#alertdialog) | Parts for a blocking confirmation dialog |
|
|
12
12
|
| <img src="../previews/feedback-status-confirmdialog.webp" width="96" alt=""> | [ConfirmDialog](#confirmdialog) | Confirm / cancel dialog, `ConfirmationDialog`, `CustomAlertDialog` |
|
|
13
13
|
| <img src="../previews/feedback-status-customdialog.webp" width="96" alt=""> | [CustomDialog](#customdialog) | Ready-made dialog with trigger, icon, title, body and footer |
|
|
14
|
-
| <img src="../previews/feedback-status-emptystate.webp" width="96" alt=""> | [EmptyState](#emptystate) |
|
|
14
|
+
| <img src="../previews/feedback-status-emptystate.webp" width="96" alt=""> | [EmptyState](#emptystate) | Illustration, title, description and action, by kind (empty, search, not-found, unauthorized) |
|
|
15
|
+
| <img src="../previews/feedback-status-errorstate.webp" width="96" alt=""> | [ErrorState](#errorstate) | Failure block with Retry and technical details |
|
|
15
16
|
| <img src="../previews/feedback-status-progress.webp" width="96" alt=""> | [Progress](#progress) | Linear bar, circular ring and labelled progress field |
|
|
16
17
|
| <img src="../previews/feedback-status-scrollprogress.webp" width="96" alt=""> | [ScrollProgress](#scrollprogress) | Hairline tracking the window or a scrollable element |
|
|
17
18
|
| <img src="../previews/feedback-status-spinner.webp" width="96" alt=""> | [Spinner](#spinner) | Loading spinner in several styles and colours |
|
|
@@ -412,8 +413,7 @@ icon for that reason now needs an element such as a `Button` or `IconButton`.
|
|
|
412
413
|
|
|
413
414
|

|
|
414
415
|
|
|
415
|
-
|
|
416
|
-
unauthorized, generic, empty, offline, search and maintenance presets.
|
|
416
|
+
A calm absence: an illustration (an icon when `size="compact"`), title, description and action, for the `empty`, `search`, `not-found` and `unauthorized` kinds. Text is `text-foreground` and reads at 7:1. For something that failed, use an error state instead.
|
|
417
417
|
|
|
418
418
|
```vue
|
|
419
419
|
<script setup lang="ts">
|
|
@@ -423,7 +423,7 @@ function clearFilters() {}
|
|
|
423
423
|
</script>
|
|
424
424
|
|
|
425
425
|
<template>
|
|
426
|
-
<EmptyState
|
|
426
|
+
<EmptyState kind="search">
|
|
427
427
|
<template #action>
|
|
428
428
|
<Button variant="outlined" @click="clearFilters">Clear filters</Button>
|
|
429
429
|
</template>
|
|
@@ -433,17 +433,88 @@ function clearFilters() {}
|
|
|
433
433
|
|
|
434
434
|
| Prop | Type | Default | Effect |
|
|
435
435
|
| --- | --- | --- | --- |
|
|
436
|
-
| `
|
|
437
|
-
|
|
|
438
|
-
| `
|
|
436
|
+
| `kind` | `'empty' \| 'search' \| 'not-found' \| 'unauthorized'` | `'generic'` (legacy, until 1.0) | illustration, icon, title and description |
|
|
437
|
+
| `size` | `'default' \| 'compact'` | `'default'` | `compact` swaps the illustration for a small icon and tightens the spacing |
|
|
438
|
+
| `variant` | the four kinds, plus `'generic' \| 'offline' \| 'maintenance'` | — | **deprecated**, removed in 1.0: use `kind`; the error values render without `role="alert"` |
|
|
439
|
+
| `#illustration` slot | — | the kind's | replaces the illustration |
|
|
440
|
+
| `#icon` slot | — | the kind's | replaces the icon |
|
|
441
|
+
| `title` / `description` | `string` (or `#title` / `#description`) | the kind's (translated) | replace the kind's text; see below |
|
|
439
442
|
| `#action` slot | — | — | shown under the text |
|
|
440
443
|
|
|
441
|
-
The
|
|
444
|
+
The default description is the companion of its title: with your own `title` and no `description`, no description is rendered. Pass `description` to keep a line under your own title. A `#title` slot counts as your own title; a `#description` slot always renders.
|
|
445
|
+
|
|
446
|
+
`EmptyState` cannot hide its media yet, unlike React's `illustration={false}` ([#711](https://github.com/softistx/nxgt-material/issues/711)); `ErrorState` can, with `:illustration="false"`.
|
|
447
|
+
|
|
448
|
+
`<EmptyState />` with no `kind` or `variant` still renders the `generic` (error) copy until 1.0; pass `kind`.
|
|
449
|
+
|
|
450
|
+
### Migrating from `variant`
|
|
451
|
+
|
|
452
|
+
`variant` still works and is removed in 1.0. Until then:
|
|
453
|
+
|
|
454
|
+
| Before | After |
|
|
455
|
+
| --- | --- |
|
|
456
|
+
| `variant="empty"`, `"search"`, `"not-found"`, `"unauthorized"` | `kind="…"` with the same value |
|
|
457
|
+
| `variant="generic"`, `"offline"`, `"maintenance"` | `<ErrorState kind="…">`: it is a `role="alert"` block with Retry and technical details; an `EmptyState` error `variant` has no `role` |
|
|
458
|
+
| no `variant` | pass `kind`: the default stays `generic` until 1.0, then becomes `empty` |
|
|
459
|
+
| class overrides to shrink it (`min-h-0`, smaller padding) | `size="compact"`: a small icon, no minimum height |
|
|
460
|
+
| `material.empty-state.generic\|offline\|maintenance.*` overrides | the same text under `material.error-state.<kind>.*` |
|
|
461
|
+
|
|
462
|
+
```vue
|
|
463
|
+
<!-- before -->
|
|
464
|
+
<EmptyState variant="offline" />
|
|
465
|
+
<EmptyState variant="search" class="min-h-0 py-6" />
|
|
466
|
+
|
|
467
|
+
<!-- after -->
|
|
468
|
+
<ErrorState kind="offline" @retry="refetch" />
|
|
469
|
+
<EmptyState kind="search" size="compact" />
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
In 1.0, `variant` goes, the error kinds leave `EmptyState`, and `kind` defaults to `empty`. The DOM keeps `data-slot="empty-state"` and `data-variant`, and adds `data-kind` and `data-size`.
|
|
442
473
|
|
|
443
474
|
**Storybook:** `Feedback & status/EmptyState` · **React:** `EmptyState`
|
|
444
475
|
|
|
445
476
|
---
|
|
446
477
|
|
|
478
|
+
## ErrorState
|
|
479
|
+
|
|
480
|
+

|
|
481
|
+
|
|
482
|
+
Failure block with `role="alert"`, an illustration, an optional outlined Retry
|
|
483
|
+
and collapsed technical details (name, message and causes, never the stack).
|
|
484
|
+
Text reads at 7:1 in both themes.
|
|
485
|
+
|
|
486
|
+
```vue
|
|
487
|
+
<script setup lang="ts">
|
|
488
|
+
import { ErrorState } from '@nxgt/material-vue/components';
|
|
489
|
+
</script>
|
|
490
|
+
|
|
491
|
+
<template>
|
|
492
|
+
<ErrorState kind="offline" :error="error" @retry="refetch" />
|
|
493
|
+
</template>
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
| Prop | Type | Default | Effect |
|
|
497
|
+
| --- | --- | --- | --- |
|
|
498
|
+
| `kind` | `'generic' \| 'offline' \| 'maintenance'` | `'generic'` | illustration, title and description (`material.error-state.<kind>.*`) |
|
|
499
|
+
| `size` | `'default' \| 'compact'` | `'default'` | compact swaps the illustration for an icon |
|
|
500
|
+
| `title` / `description` | `string` | the kind's (translated) | replace the text; a custom `title` drops the default description |
|
|
501
|
+
| `illustration` | `boolean` | `true` | `:illustration="false"` hides the media |
|
|
502
|
+
| `#illustration` / `#icon` slots | — | the built-in illustration | replace the media |
|
|
503
|
+
| `#details` slot | — | the collapsed technical details | replaces them; React has no equivalent yet ([#712](https://github.com/softistx/nxgt-material/issues/712)) |
|
|
504
|
+
| `error` | `unknown` | none | the thrown value; its name, message and `cause` chain go in the details |
|
|
505
|
+
| `showDetails` | `boolean` | `true` when `error` is set | `false` hides the details |
|
|
506
|
+
| `@retry` | `() => unknown` | none | shows an outlined **Retry** button; a returned promise keeps it loading until it settles |
|
|
507
|
+
| `retrying` / `retryLabel` | `boolean` / `string` | derived / `Retry` | force the loading state; rename the button |
|
|
508
|
+
| `action` | the `#action` slot | none | extra actions after Retry |
|
|
509
|
+
|
|
510
|
+
The details are a native `<details>`: keyboard and server-render friendly.
|
|
511
|
+
Copy keys: `material.error-state.{generic,offline,maintenance}.{title,description}`,
|
|
512
|
+
`error-state.retry`, `error-state.details` and `error-state.details-name|message|cause`.
|
|
513
|
+
|
|
514
|
+
**Storybook:** `Feedback & status/ErrorState` · **React:** `ErrorState`
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
447
518
|
## Progress
|
|
448
519
|
|
|
449
520
|

|
|
@@ -624,6 +624,8 @@ Parts: `PdfReader` (`PdfViewer` is a deprecated alias), `PdfToolbar`, `PdfSideba
|
|
|
624
624
|
`PdfThumbnailSidebar`, `PdfOutlineSidebar`, `PdfPageList`, and the
|
|
625
625
|
`usePdfReader` composable for building your own layout.
|
|
626
626
|
|
|
627
|
+
A failed load shows the reader's own error; the React reader adds a Retry button to it, which is React-only for now ([#702](https://github.com/softistx/nxgt-material/issues/702)).
|
|
628
|
+
|
|
627
629
|
| Prop | Type | Default | Effect |
|
|
628
630
|
| --- | --- | --- | --- |
|
|
629
631
|
| `file` | `PdfSource \| null` | — (required) | a URL (`string` or `URL`), a `File` or `Blob`, an `ArrayBuffer` or `Uint8Array`, or `{ url, httpHeaders?, withCredentials? }`; `null` shows the empty state |
|
|
@@ -8,6 +8,7 @@ documentation-site shell. Everything is imported from
|
|
|
8
8
|
| | Component | What it does |
|
|
9
9
|
| --- | --- | --- |
|
|
10
10
|
| <img src="../previews/navigation-layout-appshell.webp" width="96" alt=""> | [ActivityLayout](#activitylayout) | App shell: sidebar, header, breadcrumbs, titled page |
|
|
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 |
|
|
11
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` |
|
|
12
13
|
| <img src="../previews/navigation-layout-sidebar.webp" width="96" alt=""> | [Sidebar](#sidebar) | Collapsible navigation sidebar |
|
|
13
14
|
| <img src="../previews/navigation-layout-topappbar.webp" width="96" alt=""> | [TopAppBar, BottomAppBar](#topappbar) | Top and bottom bars |
|
|
@@ -75,7 +76,9 @@ which work alone; `findActiveMenuItem`, `isMenuItemActive` and
|
|
|
75
76
|
| `items` | `MenuItem[]` | — | `label` (already translated), `url`, `icon` (a Font Awesome name), `footer` |
|
|
76
77
|
| `linkComponent` | `Component` | `RouterLink` | The link each menu entry renders, given `href`; pass it as `link-component` (see the [app shell guide](../guides/app-shell.md)) |
|
|
77
78
|
| `avatarSrc` | `string` | — | avatar image in the header |
|
|
78
|
-
| `#
|
|
79
|
+
| `logo` (or `#logo`), `name` | `AppLogo` (`string \| AppLogoImage`, `AppLogoImage` = `{ src, alt? }`), `string` | — | the app's logo alone in the collapsed rail, named by `name` (also its tooltip); logo and name side by side in the phone's menu sheet; `#logo` wins over `logo` |
|
|
80
|
+
| `#brand` | slot | — | replaces the sidebar header, in the rail and the menu sheet; wins over `logo` and `name` |
|
|
81
|
+
| `#header-title` | slot | — | 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) |
|
|
79
82
|
| `#header-actions`, `#avatar` | slots | — | header, before the avatar; `#avatar` replaces it |
|
|
80
83
|
| `title`, `subtitle`, `#actions` | `string`, `string`, slot | — | `ActivityContent`'s header card |
|
|
81
84
|
| `#header` / `hideHeader` | slot / `boolean` | — / `false` | `ActivityContent`: replace or drop the header card |
|
|
@@ -100,6 +103,58 @@ entry, and an icon-only one (no label) falls back to its `aria-label`.
|
|
|
100
103
|
|
|
101
104
|
---
|
|
102
105
|
|
|
106
|
+
## AccountMenu
|
|
107
|
+
|
|
108
|
+

|
|
109
|
+
|
|
110
|
+
The account menu of an app bar, for `ActivityLayout`'s `#avatar`: an avatar
|
|
111
|
+
button named "Account menu, Ada Lovelace" (the picture, else the initials on
|
|
112
|
+
`bg-primary`) that opens a menu aligned to its end, headed by the avatar, the
|
|
113
|
+
name and a second line, then the app's links and a destructive Sign out.
|
|
114
|
+
|
|
115
|
+
```vue
|
|
116
|
+
<script setup lang="ts">
|
|
117
|
+
import { AccountMenu, ActivityLayout } from '@nxgt/material-vue/components';
|
|
118
|
+
</script>
|
|
119
|
+
|
|
120
|
+
<template>
|
|
121
|
+
<ActivityLayout :items="items">
|
|
122
|
+
<template #header-title>{{ school.name }}</template>
|
|
123
|
+
<template #avatar>
|
|
124
|
+
<AccountMenu
|
|
125
|
+
:name="user.name"
|
|
126
|
+
:secondary="user.email"
|
|
127
|
+
:src="user.picture"
|
|
128
|
+
:items="[{ label: t('menu.profile'), url: '/profile', icon: 'user' }]"
|
|
129
|
+
:signing-out="pending"
|
|
130
|
+
@sign-out="signOut"
|
|
131
|
+
/>
|
|
132
|
+
</template>
|
|
133
|
+
<RouterView />
|
|
134
|
+
</ActivityLayout>
|
|
135
|
+
</template>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
| Prop / event | Type | Default | Effect |
|
|
139
|
+
| --- | --- | --- | --- |
|
|
140
|
+
| `name` | `string` | — | the trigger's name ("Account menu, {name}", `material.account-menu.trigger`), the menu's header, the initials |
|
|
141
|
+
| `secondary` | `string` | — | a second line under the name, the e-mail say |
|
|
142
|
+
| `src` | `string` | — | the avatar's picture; the initials until it loads, or without one |
|
|
143
|
+
| `items` | `AccountMenuItem[]` | `[]` | `{ label: string; url: string; icon?: IconName }`: links between the header and Sign out; `label` already translated, `icon` a [Font Awesome](https://fontawesome.com/icons) symbol id of the duotone sprite (see [Icon](primitives.md#icon)) |
|
|
144
|
+
| `linkComponent` | `Component` | `RouterLink` | the link each item renders, as in the app shell |
|
|
145
|
+
| `@sign-out` | event | — | Sign out was chosen; the menu stays open, so `signing-out` shows |
|
|
146
|
+
| `signOutLabel` | `string` | "Sign out" | `material.account-menu.sign-out` |
|
|
147
|
+
| `signingOut` | `boolean` | `false` | Sign out is busy (`aria-busy`, a spinner) and ignores a second selection |
|
|
148
|
+
|
|
149
|
+
Enter, Space or ArrowDown on the avatar opens the menu on its first item; the
|
|
150
|
+
arrows move, Escape closes it and gives the focus back to the avatar. The
|
|
151
|
+
items are real links (`<a href>`), named by their label. Its links need
|
|
152
|
+
`vue-router`, as the shell's do.
|
|
153
|
+
|
|
154
|
+
**Storybook:** `Navigation & layout/AccountMenu` · **React:** `AccountMenu` (prop `onSignOut`)
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
103
158
|
## AuthCard
|
|
104
159
|
|
|
105
160
|

|
|
@@ -725,7 +780,7 @@ Parts: `Doc` (the shell), `DocPage` (header, content, see also and pager);
|
|
|
725
780
|
the pieces it composes — `DocTopbar`, `DocSidebar`, `DocNav`, `DocArticle`,
|
|
726
781
|
`DocToc`, `DocPager`, `DocSearch`, `DocSeeAlso`, `DocChildCards`,
|
|
727
782
|
`DocProgress`, `DocSkipLink`, `DocHeader`, `DocCopyPage`, `DocCopyMarkdown`,
|
|
728
|
-
`DocLink` — and `DocEmpty` for a 404.
|
|
783
|
+
`DocLink` — and `DocEmpty` for a 404. `DocEmpty` is an `EmptyState` (`kind` defaults to `not-found`; `size` is `default` or `compact`): see [EmptyState](./feedback-and-status.md#emptystate).
|
|
729
784
|
`useDoc` / `provideDoc` / `useDocContext` expose the state for a shell of
|
|
730
785
|
your own, built from those parts:
|
|
731
786
|
|
|
@@ -823,9 +878,17 @@ import { Background, Card } from '@nxgt/material-vue/components';
|
|
|
823
878
|
</template>
|
|
824
879
|
```
|
|
825
880
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
881
|
+
| Prop | Values | Default |
|
|
882
|
+
| --- | --- | --- |
|
|
883
|
+
| `preset` | `gradient` (the only preset on `/components`) | `gradient` |
|
|
884
|
+
| `tone` | `primary`, `secondary`, `tertiary`, `neutral`, `success`, `warning`, `error`, `info`, `mixed` (primary, secondary and tertiary), `custom` (reads `--nxgt-bg-custom-1..3`) | `primary` |
|
|
885
|
+
| `intensity` | `subtle`, `default`, `vivid` | `default` |
|
|
886
|
+
| `animated` | drifts the blobs; paused while the page is hidden or the background is off screen, and never under reduced motion | `false` |
|
|
887
|
+
|
|
888
|
+
It fills the viewport; pass a `class` such as `h-96 w-full` to use it as a band.
|
|
889
|
+
It is plain CSS from `styles.css`, with no inline `style`, and its colours lift
|
|
890
|
+
in dark. `ActivityLayout` and `ThreadLayout` sit on it. The superset with `grain`,
|
|
891
|
+
`seed`, `fill` and the other presets is `Background` from `/backgrounds`; see the [backgrounds guide](../guides/backgrounds.md).
|
|
829
892
|
|
|
830
893
|
**Storybook:** `Navigation & layout/Background` · **React:** `Background`
|
|
831
894
|
|
|
@@ -39,7 +39,16 @@ default) leave the page as it is.
|
|
|
39
39
|
|
|
40
40
|

|
|
41
41
|
|
|
42
|
-
Modal dialog parts with a close button.
|
|
42
|
+
Modal dialog parts with a close button. An open `SelectField` list inside it has always taken the first Escape, as a layer of its own. So does an open `ComboboxField` or `Autocomplete` list: the second Escape closes the dialog. The same holds in a `Sheet`, a `Drawer`, an `AlertDialog` and a `Popover`.
|
|
43
|
+
|
|
44
|
+
Your own `@escape-key-down` on the content receives the Escape that only closes an open list with `event.defaultPrevented === true`; a handler that asks "close without saving?" returns early on it:
|
|
45
|
+
|
|
46
|
+
```vue
|
|
47
|
+
<DialogContent @escape-key-down="(event) => {
|
|
48
|
+
if (event.defaultPrevented) return; // only an open list is closing
|
|
49
|
+
if (dirty) event.preventDefault(); // ask "close without saving?"
|
|
50
|
+
}">
|
|
51
|
+
```
|
|
43
52
|
|
|
44
53
|
```vue
|
|
45
54
|
<script setup lang="ts">
|
|
@@ -198,6 +207,8 @@ Vue.
|
|
|
198
207
|
|
|
199
208
|
Swipeable bottom or side drawer (Reka UI's drawer).
|
|
200
209
|
|
|
210
|
+
An `Autocomplete` inside a Drawer keeps its list floating, stopped above the Drawer's footer, and a drag that starts on the list scrolls it instead of dragging the Drawer.
|
|
211
|
+
|
|
201
212
|
```vue
|
|
202
213
|
<script setup lang="ts">
|
|
203
214
|
import {
|
|
@@ -638,20 +649,26 @@ Parts: `Command`, `CommandDialog`, `CommandInput`, `CommandList`,
|
|
|
638
649
|
`CommandEmpty`, `CommandGroup`, `CommandItem`, `CommandShortcut`,
|
|
639
650
|
`CommandSeparator`.
|
|
640
651
|
|
|
652
|
+
`CommandEmpty` shows when the search matches nothing. With no children it
|
|
653
|
+
renders a compact `EmptyState` ("No results", `material.command.empty`); give it
|
|
654
|
+
children and they replace that, as before.
|
|
655
|
+
|
|
641
656
|
| Prop | Type | Default | Effect |
|
|
642
657
|
| --- | --- | --- | --- |
|
|
643
658
|
| `value` (`CommandItem`) | `string` | — (required) | the item's value, emitted by `@select` and by `Command`'s `v-model` |
|
|
644
659
|
| `checked` (`CommandItem`) | `boolean` | — | sets `aria-checked` and shows a checkbox indicator, for a multi-choice list; reka's `aria-selected` keeps meaning "selected value" (`multiple` is not used) |
|
|
645
660
|
| `multiselectable` (`CommandList`) | `boolean` | — | sets `aria-multiselectable` on the list |
|
|
646
661
|
| `@select` (`CommandItem`) | `(event) => void` | — | Enter or click on the item; `event.detail.value` is its `value` |
|
|
662
|
+
| `aria-label` (`CommandList`) | `string` | `"Suggestions"` (translated, `material.command.list`) | the list's accessible name; your own `aria-label` replaces the default |
|
|
663
|
+
| `decorative` (`CommandSeparator`) | `boolean` | `true` | the separator is purely visual (`role="none"`): a `role="separator"` is not allowed inside the listbox |
|
|
647
664
|
| `heading` (`CommandGroup`) | `string` | — | the group's label |
|
|
648
665
|
| `title` / `description` (`CommandDialog`) | `string` | `"Command Palette"` / `"Search for a command to run..."` (translated) | the dialog's title and description, visually hidden |
|
|
649
666
|
| `v-model:open` (`CommandDialog`) | `boolean` | — | `CommandDialog` takes every `Dialog` prop |
|
|
650
667
|
|
|
651
668
|
The filter matches the item's text, case- and accent-insensitive; there is no
|
|
652
669
|
`shouldFilter` or `keywords` in Vue. `CommandDialog`'s default title and
|
|
653
|
-
description
|
|
654
|
-
keyboard shortcut: listen for ⌘K yourself (`useMagicKeys` from VueUse) and set
|
|
670
|
+
description follow `I18nProvider` (`material.command.title` / `description`).
|
|
671
|
+
It does not bind a keyboard shortcut: listen for ⌘K yourself (`useMagicKeys` from VueUse) and set
|
|
655
672
|
`open`.
|
|
656
673
|
|
|
657
674
|
**Storybook:** `Overlays & menus/Command` · **React:** `Command`, `CommandDialog` (same parts)
|
|
@@ -193,7 +193,7 @@ import { Typography } from '@nxgt/material-vue/components';
|
|
|
193
193
|
| Prop | Type | Default | Effect |
|
|
194
194
|
| --- | --- | --- | --- |
|
|
195
195
|
| `variant` | `'huge-larger' \| 'huge-large' \| 'huge-medium' \| 'huge-small' \| 'headline-larger' \| 'headline-large' \| 'headline-medium' \| 'headline-small' \| 'title-large' \| 'title-medium' \| 'title-small' \| 'body-medium' \| 'body-small' \| 'caption' \| 'normal'` | `'normal'` | size and weight; `caption` is also muted |
|
|
196
|
-
| `tone` | `'default' \| 'muted' \| 'primary' \| 'error'` | `'default'` | text colour: `muted` is `text-muted-foreground`, `primary` is `text-primary
|
|
196
|
+
| `tone` | `'default' \| 'muted' \| 'primary' \| 'error'` | `'default'` | text colour: `muted` is `text-muted-foreground`, `primary` is `text-primary-text` (primary lightened in dark), `error` is `text-error-text`; `default` inherits (a `class` colour still wins) |
|
|
197
197
|
| `as` | a tag name or a component | `'div'` | the element to render, keeping the variant, e.g. `as="h2"` or `as="span"` |
|
|
198
198
|
| `asChild` | `boolean` | `false` | merge the classes onto the single child instead of rendering an element |
|
|
199
199
|
|
|
@@ -364,6 +364,8 @@ Takes every `<input>` attribute. `v-model` carries `string | number`;
|
|
|
364
364
|
the error outline. For a labelled field with helper text and a form wrapper,
|
|
365
365
|
use `TextField` on the [Form fields](form-fields.md) page.
|
|
366
366
|
|
|
367
|
+
In an input group (`InputGroupInput`, `InputGroupField`) the fill tint follows the value: an empty field is not tinted, with or without a `placeholder` (one without renders a blank `placeholder=" "` so that CSS can tell). The bare `Input` and `Textarea` have no tint and get no placeholder.
|
|
368
|
+
|
|
367
369
|
**Emits:** `update:modelValue` — `string | number`.
|
|
368
370
|
**Storybook:** `Primitives/Input` · **React:** `Input`
|
|
369
371
|
|
|
@@ -461,7 +463,7 @@ import { Icon } from '@nxgt/material-vue/components';
|
|
|
461
463
|
|
|
462
464
|
<template>
|
|
463
465
|
<Icon name="bell" />
|
|
464
|
-
<Icon name="bell" type="solid" class="size-6 text-primary" aria-hidden="true" />
|
|
466
|
+
<Icon name="bell" type="solid" class="size-6 text-primary-text" aria-hidden="true" />
|
|
465
467
|
</template>
|
|
466
468
|
```
|
|
467
469
|
|
|
@@ -901,7 +903,7 @@ defineProps<{ nonce?: string }>();
|
|
|
901
903
|
|
|
902
904
|
| Prop | Type | Default | Effect |
|
|
903
905
|
| --- | --- | --- | --- |
|
|
904
|
-
| `nonce` | `string` | `undefined` | the nonce of the page's `style-src-elem 'nonce-…'`; `undefined` or `''` inherits the nearest outer `NonceProvider`'s (or `ConfigProvider`'s) nonce (
|
|
906
|
+
| `nonce` | `string` | `undefined` | the nonce of the page's `style-src-elem 'nonce-…'`; `undefined` or `''` inherits the nearest outer `NonceProvider`'s (or `ConfigProvider`'s) nonce, else, on the client from the first render (read in `setup`), the page's own (`csp-nonce` meta tag, …); a server has no page, so it passes the prop |
|
|
905
907
|
|
|
906
908
|
`useCspNonce()` (`@nxgt/material-vue/composables`) reads it in a component of
|
|
907
909
|
your own; `getCspNonce()` and `setCspNonce()` (`@nxgt/material-vue/lib`) read
|
|
@@ -238,7 +238,7 @@ props below, and `option` for anything else.
|
|
|
238
238
|
| `height` | `string \| number` | `320` | the container's height; the chart fills it |
|
|
239
239
|
| `loading` | `boolean` | `false` | a skeleton instead of the chart |
|
|
240
240
|
| `empty` | `boolean` | when `data` is empty | force the empty state on or off |
|
|
241
|
-
| `#emptyState` | slot | built-in
|
|
241
|
+
| `#emptyState` | slot | the built-in compact `EmptyState` | what an empty chart shows; a custom slot keeps its centring wrapper |
|
|
242
242
|
| `colors` | `string[]` | from the `--chart-*` tokens | override the palette; any CSS colour, `oklch()` included (converted to `rgb()` for ECharts) |
|
|
243
243
|
| `margin` | `ChartMargin` | — | plot padding |
|
|
244
244
|
| `option` | `EChartsOption` | — | merged into the generated ECharts option |
|
|
@@ -234,6 +234,8 @@ options by label. Picking the selected option again clears it.
|
|
|
234
234
|
|
|
235
235
|
Single or multiple (chips) autocomplete typed by its options, with free-solo values, accent-insensitive matching, a debounced server search and option slots.
|
|
236
236
|
|
|
237
|
+
The input is a `combobox` (`aria-expanded`, `aria-controls`, `aria-activedescendant`) and the list a `listbox` of `option`s, as in `@nxgt/material`; the list opens on a click or on typing, not on focus alone. In tests, use `getByRole('combobox', { name })` and `getByRole('option', { name })`.
|
|
238
|
+
|
|
237
239
|
```vue
|
|
238
240
|
<script setup lang="ts">
|
|
239
241
|
import { ref } from 'vue';
|
|
@@ -336,15 +338,23 @@ update them on `@query-change`, and pass `filter="none"` when the server
|
|
|
336
338
|
already filtered.
|
|
337
339
|
|
|
338
340
|
A disabled option (`disabled: true` on it, in `options` or in what `on-search`
|
|
339
|
-
returns) is greyed out, carries `aria-disabled` and a native `disabled`, so
|
|
340
|
-
|
|
341
|
-
enabled option), in `single` and `multiple` mode.
|
|
341
|
+
returns) is greyed out, carries `aria-disabled` and a native `disabled`, so the arrow keys skip it, and is never picked, by click or by Enter (which picks the
|
|
342
|
+
highlighted option, the first enabled one until the arrows move it), in `single` and `multiple` mode.
|
|
342
343
|
|
|
343
344
|
**Accessibility.** A polite live region (`role="status"`) says "Loading…",
|
|
344
345
|
then "3 results" or "No results" (or the failure) once the search answers or
|
|
345
346
|
typing pauses — never on every keystroke. The list is `aria-busy` while
|
|
346
347
|
loading. The strings are `material.autocomplete.loading`, `.results`,
|
|
347
|
-
`.search-error` and `.min-query`.
|
|
348
|
+
`.search-error` and `.min-query`.
|
|
349
|
+
|
|
350
|
+
**Keyboard.** The input keeps the focus and points at the highlighted option
|
|
351
|
+
with `aria-activedescendant`. `ArrowDown` and `ArrowUp` open a closed list
|
|
352
|
+
and move the highlight over the enabled options (wrapping at the ends); with
|
|
353
|
+
an empty query `Home` and `End` jump to the first and last one. `Enter`
|
|
354
|
+
picks the highlighted option (or adds the free-solo value when "Add …" stands
|
|
355
|
+
for it) and, with the list closed, submits the form; hovering an option moves
|
|
356
|
+
the highlight. `Escape` closes the list, and `Backspace` on an empty query removes
|
|
357
|
+
the last chip. The options are not tab stops: Tab leaves the field.
|
|
348
358
|
|
|
349
359
|
It is a generic component: `T` is inferred from `options`, and the slots —
|
|
350
360
|
React's render props — receive it whole:
|
package/docs/getting-started.md
CHANGED
|
@@ -35,7 +35,7 @@ does not import that entry never has them in its tree.
|
|
|
35
35
|
The tarball contains the built `dist/` (with KaTeX's fonts in
|
|
36
36
|
`dist/fonts/katex/`, which the compiled stylesheet loads),
|
|
37
37
|
`lib/styles.css`, the component
|
|
38
|
-
stylesheets it imports (`lib/components/**/*.css`), `lib/map/map.css`, the
|
|
38
|
+
stylesheets it imports (`lib/components/**/*.css`), `lib/map/map.css`, `lib/backgrounds/backgrounds.css`, `lib/backgrounds/css/*.css`, the
|
|
39
39
|
Nuxt module (`lib/nuxt`, with the
|
|
40
40
|
`lib/vite/index.ts` it imports), these docs and `CHANGELOG.md` (from the
|
|
41
41
|
release after 0.25.1). There is no `postinstall` build step.
|
|
@@ -112,6 +112,18 @@ Your own components can use the same per-instance store factory
|
|
|
112
112
|
(`defineLocalStore` and the status, filter and step-form stores) from
|
|
113
113
|
`@nxgt/material-vue/lib`; see [helpers](guides/helpers.md#pinia-store-factories).
|
|
114
114
|
|
|
115
|
+
## The Vite plugin
|
|
116
|
+
|
|
117
|
+
In `vite.config.ts`, add the plugin after `tailwindcss()`:
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import { nxgtMaterial } from '@nxgt/material-vue/vite';
|
|
121
|
+
|
|
122
|
+
export default defineConfig({ plugins: [/* …, */ tailwindcss(), ...nxgtMaterial()] });
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
It sets up a strict Content Security Policy (the header is written to `dist/.vite/nxgt-csp.json`), keeps fonts out of `data:` URIs, loads Inter when `@fontsource-variable/inter` is installed and fails the build when the stylesheet above is not imported. Each part has an opt-out: [Vite plugin guide](guides/vite-plugin.md). On Nuxt, see the guide's note.
|
|
126
|
+
|
|
115
127
|
## Icons
|
|
116
128
|
|
|
117
129
|
`Icon` renders a Font Awesome **sprite** referenced by a root-relative URL on
|
|
@@ -174,7 +186,9 @@ export default defineNuxtConfig({
|
|
|
174
186
|
// prefix: 'Nxgt', // <NxgtButton />
|
|
175
187
|
// components: true, // auto-import components
|
|
176
188
|
// imports: true, // auto-import composables and i18n helpers
|
|
177
|
-
// pdf: {
|
|
189
|
+
// pdf: { bundle: true }, // a server with no node_modules that renders /pdf
|
|
190
|
+
// fonts: { sans: 'inter' }, // self-hosted Inter (bun add @fontsource-variable/inter)
|
|
191
|
+
// csp: { zodJitless: true }, // zod's jitless script in every head (needs nuxt-security's nonce or hash)
|
|
178
192
|
},
|
|
179
193
|
});
|
|
180
194
|
```
|
|
@@ -184,7 +198,10 @@ export default defineNuxtConfig({
|
|
|
184
198
|
| `prefix` | `string` | `''` | Prepended to every auto-imported component name |
|
|
185
199
|
| `components` | `boolean` | `true` | Registers the components of `@nxgt/material-vue/components` for use without an import |
|
|
186
200
|
| `imports` | `boolean` | `true` | Auto-imports the composables (`useTheme`, `useIsMobile`, `useIsClient`, `useLocalizedDate`, `useFormatters`, `useNumberFormatter`, `useSearchParam`, …) and `useMaterialTranslation`, `createTypedTranslation`, `useOptionalI18nContext` |
|
|
187
|
-
| `pdf.
|
|
201
|
+
| `pdf.bundle` | `boolean` | `false` | Adds `pdfkitStandardFonts()` to the Nitro build, for a server that renders with `@nxgt/material-vue/pdf` and runs without `node_modules` ([PDF documents](guides/pdf.md#fonts-on-a-fully-inlined-server)). `pdf.bundleStandardFonts` is its deprecated name |
|
|
202
|
+
| `fonts` | `boolean \| { katex?, sans? }` | `{ katex: true, sans: false }` | `katex`: a font is never inlined as a `data:` URI (`font-src 'self'`). `sans: 'inter'`: loads self-hosted Inter and sets `--font-sans` through `css` (a missing `@fontsource-variable/inter` warns once and keeps the system font). `false` turns both off |
|
|
203
|
+
| `csp.zodJitless` | `boolean` | `false` | Puts zod's jitless inline script in every page's `<head>` (`tagPriority: 'critical'`). It is an inline script: a strict CSP needs `nuxt-security` with `nonce: true` (or its script hashes) to allow it. The module sets no CSP itself ([Vite plugin guide](guides/vite-plugin.md#nuxt)) |
|
|
204
|
+
| `styles` | `boolean \| { check? }` | `false` | The `vite build` check that the stylesheet is imported, off by default under Nuxt because the module cannot see how `css` entries are resolved. `true` opts in |
|
|
188
205
|
|
|
189
206
|
The module also:
|
|
190
207
|
|
|
@@ -223,6 +240,7 @@ export default defineConfig({
|
|
|
223
240
|
| `@nxgt/material-vue` | Everything from `/components`, `/composables`, `/i18n`, `/lib`, `/models` and `/types` in one barrel, plus the compiled `style` condition (`dist/material-vue.css`, for apps without Tailwind) |
|
|
224
241
|
| `@nxgt/material-vue/components` | UI components and `*FormField` wrappers ([components](components/README.md)) |
|
|
225
242
|
| `@nxgt/material-vue/map` | Leaflet maps, markers, a location field. **Its own entry, not in the barrel above** ([maps](guides/maps.md)) |
|
|
243
|
+
| `@nxgt/material-vue/backgrounds` | Background presets: `Background` (a superset of the `/components` one: `preset` `gradient` or `grain`, `tone`, `intensity`, `animated`, `grain`, `seed`, `fill`), and the types and tables re-exported from `@nxgt/material-core/backgrounds`; more presets are coming. **Its own entry**, with an opt-in stylesheet ([backgrounds](guides/backgrounds.md)) |
|
|
226
244
|
| `@nxgt/material-vue/pdf` | PDF files from plain data: builders, `usePdf`, `PdfDownloadButton`, `pdfResponse`. **Its own entry, not in the barrel above** ([PDF documents](guides/pdf.md)) |
|
|
227
245
|
| `@nxgt/material-vue/composables` | `useTheme`, `useIsClient`, `useIsMobile`, `useLocalizedDate`, `useSearchParam`, … ([helpers](guides/helpers.md#nxgtmaterial-vuecomposables)) |
|
|
228
246
|
| `@nxgt/material-vue/dnd` | Drag-and-drop primitives ([helpers](guides/helpers.md#nxgtmaterial-vuednd)) |
|
|
@@ -232,6 +250,7 @@ export default defineConfig({
|
|
|
232
250
|
| `@nxgt/material-vue/types` | Shared function types (`PromiseOr`, `FunctionOr`, …) |
|
|
233
251
|
| `@nxgt/material-vue/styles.css` | Source Tailwind v4 tokens. **This** is the stylesheet a Tailwind app imports |
|
|
234
252
|
| `@nxgt/material-vue/map.css` | Leaflet's stylesheet, for apps that use `/map`. Not part of `styles.css` |
|
|
253
|
+
| `@nxgt/material-vue/backgrounds.css` | Every background preset's CSS, for apps that use `/backgrounds`; `@nxgt/material-vue/backgrounds/<preset>.css` loads one. Not part of `styles.css` |
|
|
235
254
|
| `@nxgt/material-vue/nuxt` | The Nuxt module ([above](#nuxt)) |
|
|
236
255
|
| `@nxgt/material-vue/vite` | `pdfkitStandardFonts()`, a Vite plugin for a server that inlines `/pdf` ([PDF documents](guides/pdf.md#fonts-on-a-fully-inlined-server)). Build-time code for `vite.config.ts` |
|
|
237
256
|
|
package/docs/guides/app-shell.md
CHANGED
|
@@ -17,10 +17,7 @@ const items: MenuItem[] = [
|
|
|
17
17
|
</script>
|
|
18
18
|
|
|
19
19
|
<template>
|
|
20
|
-
<ActivityLayout :items="items" avatar-src="/me.png">
|
|
21
|
-
<template #brand>
|
|
22
|
-
<strong>Acme</strong>
|
|
23
|
-
</template>
|
|
20
|
+
<ActivityLayout :items="items" logo="/logo.svg" name="Acme" avatar-src="/me.png">
|
|
24
21
|
<RouterView />
|
|
25
22
|
</ActivityLayout>
|
|
26
23
|
</template>
|
|
@@ -179,7 +176,11 @@ partitionMenu(items); // { content, footer }
|
|
|
179
176
|
| `content-as` | `'main' \| 'div'` | `'main'` | The content region's element: the page's `<main>` landmark, or a `div` when your app renders its own `<main>` |
|
|
180
177
|
| `scroll` | `'document' \| 'container'` | `'document'` | Who scrolls: the page (header and sidebar sticky), or the content region inside a shell one screen high |
|
|
181
178
|
| `class` | `string` | — | Classes for the outer element (`[data-slot=activity-layout]`), which takes the other attributes too |
|
|
182
|
-
|
|
|
179
|
+
| `logo` | `string \| { src, alt? }` | an atom badge | The app's logo, an image URL: alone in the collapsed rail, beside `name` in the phone's menu sheet (see [The brand](#the-brand-logo-and-name)) |
|
|
180
|
+
| `#logo` | slot | — | The logo as a node (an inline SVG), in place of `logo`; it wins when both are given |
|
|
181
|
+
| `name` | `string` | — | The app's name, already translated: the logo's accessible name and tooltip in the rail, shown beside it in the sheet |
|
|
182
|
+
| `#brand` | slot | — | Replaces the sidebar header entirely, in the rail and in the menu sheet; wins over `logo` and `name` |
|
|
183
|
+
| `#header-title` | slot | — | 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) |
|
|
183
184
|
| `#header-actions` | slot | — | In the header, before the avatar: notifications, help, `LanguageSwitcher`, `ThemeToggle` |
|
|
184
185
|
| `#avatar` | slot | the `avatar-src` image | Replaces the avatar entirely |
|
|
185
186
|
| default | slot | — | The routed content, usually `<RouterView />` |
|
|
@@ -214,32 +215,26 @@ the slots.
|
|
|
214
215
|
```vue
|
|
215
216
|
<script setup lang="ts">
|
|
216
217
|
import {
|
|
218
|
+
AccountMenu,
|
|
217
219
|
ActivityLayout,
|
|
218
|
-
Avatar,
|
|
219
|
-
AvatarFallback,
|
|
220
|
-
AvatarImage,
|
|
221
220
|
LanguageSwitcher,
|
|
222
221
|
type MenuItem,
|
|
223
222
|
ThemeToggle,
|
|
224
223
|
} from '@nxgt/material-vue/components';
|
|
225
224
|
|
|
226
|
-
defineProps<{ items: MenuItem[]; user: { name: string; picture?: string } }>();
|
|
225
|
+
defineProps<{ items: MenuItem[]; user: { name: string; email: string; picture?: string } }>();
|
|
226
|
+
const emit = defineEmits<{ 'sign-out': [] }>();
|
|
227
227
|
</script>
|
|
228
228
|
|
|
229
229
|
<template>
|
|
230
|
-
<ActivityLayout :items="items">
|
|
231
|
-
<template #
|
|
232
|
-
<img src="/logo.svg" alt="Acme" class="h-8" />
|
|
233
|
-
</template>
|
|
230
|
+
<ActivityLayout :items="items" logo="/logo.svg" name="Acme">
|
|
231
|
+
<template #header-title>Northwind school</template>
|
|
234
232
|
<template #header-actions>
|
|
235
233
|
<LanguageSwitcher />
|
|
236
234
|
<ThemeToggle />
|
|
237
235
|
</template>
|
|
238
236
|
<template #avatar>
|
|
239
|
-
<
|
|
240
|
-
<AvatarImage :src="user.picture ?? ''" :alt="user.name" />
|
|
241
|
-
<AvatarFallback>{{ user.name.slice(0, 2) }}</AvatarFallback>
|
|
242
|
-
</Avatar>
|
|
237
|
+
<AccountMenu :name="user.name" :secondary="user.email" :src="user.picture" @sign-out="emit('sign-out')" />
|
|
243
238
|
</template>
|
|
244
239
|
<RouterView />
|
|
245
240
|
</ActivityLayout>
|
|
@@ -254,7 +249,92 @@ entry closes it — so does any route change, Back included — and focus goes t
|
|
|
254
249
|
the page's `<main>`, unless the new screen already focused an element inside it
|
|
255
250
|
(an error page's `h1`), which keeps it; closed with Escape, the close button or the overlay,
|
|
256
251
|
focus returns to the menu button. Its entries show their labels, so they have
|
|
257
|
-
no tooltip: none opens over the first entry as focus moves in.
|
|
252
|
+
no tooltip: none opens over the first entry as focus moves in. It shows the
|
|
253
|
+
app's `logo` and `name` (or `#brand`) above the entries, as the expanded
|
|
254
|
+
sidebar does.
|
|
255
|
+
|
|
256
|
+
### The app bar: a title, the actions, the account
|
|
257
|
+
|
|
258
|
+
The header reads, from the start: the phone's menu button, `#header-title`
|
|
259
|
+
(the context the app is in, the active school or organisation), then
|
|
260
|
+
`#header-actions` (language, notifications) and `#avatar` at the end. The
|
|
261
|
+
title takes the room left and ends in an ellipsis when it does not fit, so
|
|
262
|
+
the avatar always stays on screen. It is not a heading: the screen's `h1` is
|
|
263
|
+
`ActivityContent`'s title.
|
|
264
|
+
|
|
265
|
+
`AccountMenu` is the `#avatar` most apps want: an avatar button named
|
|
266
|
+
"Account menu, Ada Lovelace" (`material.account-menu.trigger`) that opens a
|
|
267
|
+
menu aligned to its end, headed by the avatar, the name and `secondary`, then
|
|
268
|
+
the app's `items` and a destructive Sign out
|
|
269
|
+
(`material.account-menu.sign-out`). Choosing Sign out emits `sign-out` and
|
|
270
|
+
keeps the menu open: pass `signing-out` while the request runs, and the item
|
|
271
|
+
shows a spinner and ignores a second selection. Without `src`, or until it
|
|
272
|
+
loads, the avatar shows the initials on `bg-primary`.
|
|
273
|
+
|
|
274
|
+
```vue
|
|
275
|
+
<AccountMenu
|
|
276
|
+
:name="user.name"
|
|
277
|
+
:secondary="user.email"
|
|
278
|
+
:src="user.picture"
|
|
279
|
+
:items="[
|
|
280
|
+
{ label: t('menu.profile'), url: '/profile', icon: 'user' },
|
|
281
|
+
{ label: t('menu.settings'), url: '/settings', icon: 'gear' },
|
|
282
|
+
]"
|
|
283
|
+
:signing-out="pending"
|
|
284
|
+
@sign-out="signOut"
|
|
285
|
+
/>
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Each entry is an `AccountMenuItem`, exported with it:
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
type AccountMenuItem = {
|
|
292
|
+
label: string; // already translated
|
|
293
|
+
url: string; // rendered as a real link through `link-component`
|
|
294
|
+
icon?: IconName; // optional, decorative: the label names the entry
|
|
295
|
+
};
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
`icon` is an `IconName`, a symbol id of the Font Awesome duotone sprite you serve (`user`, `gear`, `arrow-right-from-bracket`; browse the names at <https://fontawesome.com/icons>, and see [getting started](../getting-started.md#icons) for serving the sprite).
|
|
299
|
+
|
|
300
|
+
### The brand: logo and name
|
|
301
|
+
|
|
302
|
+
`logo` (or the `#logo` slot) and `name` put the app's brand in the sidebar
|
|
303
|
+
header, in the shape each place has room for. `logo` is typed `AppLogo`
|
|
304
|
+
(`string | AppLogoImage`, where `AppLogoImage` is `{ src: string; alt?: string }`
|
|
305
|
+
and a string is the image URL), both exported from
|
|
306
|
+
`@nxgt/material-vue/components`:
|
|
307
|
+
|
|
308
|
+
- **The collapsed rail** (desktop, about 70 px wide) shows the **logo alone**.
|
|
309
|
+
`name` is its accessible name (an `<img alt>`, or `role="img"` with an
|
|
310
|
+
`aria-label` around the `#logo` node) and shows as a tooltip on hover, to
|
|
311
|
+
its right.
|
|
312
|
+
- **The expanded sidebar and the phone's menu sheet** show the **logo and the
|
|
313
|
+
name side by side**. The name is on screen there, so the logo is decorative
|
|
314
|
+
(`alt=""` or `aria-hidden`), and no tooltip repeats it.
|
|
315
|
+
|
|
316
|
+
```vue
|
|
317
|
+
<ActivityLayout :items="items" logo="/logo.svg" :name="t('app.name')">…</ActivityLayout>
|
|
318
|
+
<!-- An image with its own alt, used when there is no name: -->
|
|
319
|
+
<ActivityLayout :items="items" :logo="{ src: '/logo.svg', alt: 'Acme' }">…</ActivityLayout>
|
|
320
|
+
<!-- A node, an inline SVG for instance: -->
|
|
321
|
+
<ActivityLayout :items="items" name="Acme">
|
|
322
|
+
<template #logo><AcmeMark class="size-8" /></template>
|
|
323
|
+
…
|
|
324
|
+
</ActivityLayout>
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Without a logo the header keeps its placeholder atom badge (named by `name`
|
|
328
|
+
in the rail when there is one); fill `logo` in a real app. `#brand` is the
|
|
329
|
+
full override: one node rendered as given, collapsed or expanded alike, in the
|
|
330
|
+
rail and in the menu sheet. `AppSidebar` takes the same props and slots, and
|
|
331
|
+
`AppHeader` takes them for its menu sheet.
|
|
332
|
+
|
|
333
|
+
`#brand` is rendered twice, once in the rail and once in the menu sheet (the
|
|
334
|
+
rail stays in the page on a phone, hidden): an `id` inside it appears twice
|
|
335
|
+
while the sheet is open, so leave ids out of it. A link in `#brand` (a home
|
|
336
|
+
link around the logo) is the sheet's first focusable element, so it takes
|
|
337
|
+
the focus when the sheet opens.
|
|
258
338
|
|
|
259
339
|
## Landmarks, headings and scrolling
|
|
260
340
|
|
|
@@ -375,7 +455,7 @@ home included, is a `RouterLink`, so it goes through the router's base: under
|
|
|
375
455
|
`AppSidebar`, `AppHeader` and `AppBreadcrumb` are exported for a layout of your
|
|
376
456
|
own. `AppSidebar` and `AppHeader` take the same `items`; `AppSidebar` also
|
|
377
457
|
takes `expanded` (`v-model:expanded`) and `with-toggle-icon`, and has the
|
|
378
|
-
`#brand` slot
|
|
458
|
+
`#brand` slot, `logo`, `name` and `#logo`; `AppHeader` takes `avatar-src`, `logo`, `name` and `show-menu-icon` (default
|
|
379
459
|
`true`), with `#actions`, `#avatar` and a default slot for the middle of the
|
|
380
460
|
bar. The lower-level `Sidebar`, `TopAppBar`, `NavigationRail` and `Breadcrumb`
|
|
381
461
|
primitives are on the
|