@nxgt/material-vue 0.42.0 → 0.43.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.
Files changed (162) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/README.md +18 -5
  3. package/dist/{Button-CWmzvv9p.js → Button-CEvi_i4g.js} +4 -4
  4. package/dist/{HoverCardTrigger-C5AbAyHE.js → HoverCardTrigger-B7m5Zvj1.js} +69 -62
  5. package/dist/backgrounds/backgrounds.css +8 -0
  6. package/dist/backgrounds/css/base.css +169 -0
  7. package/dist/backgrounds/css/gradient.css +53 -0
  8. package/dist/backgrounds/css/grain.css +19 -0
  9. package/dist/backgrounds/css/motion.css +30 -0
  10. package/dist/backgrounds.js +111 -0
  11. package/dist/{camera-C-9slBUV.js → camera-DP3Qfhhc.js} +4 -4
  12. package/dist/{chunk-7M6MHVWA-wgE1MsQ9.js → chunk-7M6MHVWA-DzYWbKT9.js} +1 -1
  13. package/dist/{chunk-GNY47TPC-BByCj-tp.js → chunk-GNY47TPC-D4sl5ZSh.js} +1 -1
  14. package/dist/{classDiagram-v2-NBCMYWYE-LDc3yyTu.js → classDiagram-v2-NBCMYWYE-BtmPeDVI.js} +1 -1
  15. package/dist/components/base.css +5 -2
  16. package/dist/components/components.css +5 -0
  17. package/dist/components/theme-tokens.css +21 -0
  18. package/dist/{components-DX4EycMi.js → components-zm5JC2dA.js} +14077 -13577
  19. package/dist/components.js +8 -8
  20. package/dist/composables.js +3 -3
  21. package/dist/{dagre-6A5THRUB-CCigPI-W.js → dagre-6A5THRUB-CmmqjQ01.js} +1 -1
  22. package/dist/{diagram-22UHCM2B-vav-BOss.js → diagram-22UHCM2B-BYL1soMk.js} +1 -1
  23. package/dist/dnd.js +1 -1
  24. package/dist/{erDiagram-OPXOYQCR-BV3UGLoQ.js → erDiagram-OPXOYQCR-DhNOupcl.js} +1 -1
  25. package/dist/{flowDiagram-KWPJA3E3-B3AfwLQk.js → flowDiagram-KWPJA3E3-_S6NO9vf.js} +2 -2
  26. package/dist/{form-scope-CN68PTXc.js → form-scope-XQGlh8m3.js} +1 -1
  27. package/dist/{i18n-provider-DmZSTBnp.js → i18n-provider-tL27-a8y.js} +1 -1
  28. package/dist/i18n.js +26 -9
  29. package/dist/{icon-button-DErh1aJS.js → icon-button-BNA3fXM2.js} +4 -4
  30. package/dist/lib/backgrounds/background/background.vue.d.ts +22 -0
  31. package/dist/lib/backgrounds/background/index.d.ts +2 -0
  32. package/dist/lib/backgrounds/background/partials/background-layers.vue.d.ts +9 -0
  33. package/dist/lib/backgrounds/background/partials/scene-svg.d.ts +10 -0
  34. package/dist/lib/backgrounds/background/types.d.ts +24 -0
  35. package/dist/lib/backgrounds/index.d.ts +13 -0
  36. package/dist/lib/components/ui/actions/button/button-tones.d.ts +3 -3
  37. package/dist/lib/components/ui/actions/responsive-button/responsive-button.vue.d.ts +3 -3
  38. package/dist/lib/components/ui/collections/data-table/data-table-context.d.ts +4 -0
  39. package/dist/lib/components/ui/collections/data-table/data-table.vue.d.ts +8 -0
  40. package/dist/lib/components/ui/collections/data-table/partials/data-table-body.vue.d.ts +8 -0
  41. package/dist/lib/components/ui/collections/data-table/partials/data-table-empty.vue.d.ts +5 -1
  42. package/dist/lib/components/ui/collections/data-table/partials/data-table-toolbar.d.ts +16 -3
  43. package/dist/lib/components/ui/collections/data-table/select-checkbox.d.ts +36 -0
  44. package/dist/lib/components/ui/collections/data-table/types.d.ts +15 -0
  45. package/dist/lib/components/ui/collections/data-table/use-data-table.d.ts +2 -0
  46. package/dist/lib/components/ui/feedback/circular-progress/circular-progress.vue.d.ts +1 -1
  47. package/dist/lib/components/ui/feedback/empty-state/empty-state.vue.d.ts +21 -8
  48. package/dist/lib/components/ui/feedback/empty-state/index.d.ts +1 -1
  49. package/dist/lib/components/ui/feedback/error-state/error-state.vue.d.ts +54 -0
  50. package/dist/lib/components/ui/feedback/error-state/index.d.ts +3 -0
  51. package/dist/lib/components/ui/feedback/error-state/partials/error-state-details.vue.d.ts +7 -0
  52. package/dist/lib/components/ui/feedback/index.d.ts +1 -0
  53. package/dist/lib/components/ui/feedback/state-frame/state-frame.vue.d.ts +36 -0
  54. package/dist/lib/components/ui/feedback/state-frame/state-icon.vue.d.ts +7 -0
  55. package/dist/lib/components/ui/feedback/state-frame/state-illustration.vue.d.ts +8 -0
  56. package/dist/lib/components/ui/feedback/state-frame/state-tones.d.ts +11 -0
  57. package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
  58. package/dist/lib/components/ui/layout/app-shell/use-truncated-title.d.ts +10 -0
  59. package/dist/lib/components/ui/layout/background/background.vue.d.ts +18 -3
  60. package/dist/lib/components/ui/layout/doc/partials/doc-empty.vue.d.ts +7 -4
  61. package/dist/lib/components/ui/media/stx-player/stx-player-viewport.vue.d.ts +2 -2
  62. package/dist/lib/components/ui/overlays/command/CommandSeparator.vue.d.ts +3 -1
  63. package/dist/lib/components/ui/overlays/dialog/in-dialog.d.ts +1 -1
  64. package/dist/lib/components/ui/overlays/dialog/use-escape-forward.d.ts +13 -0
  65. package/dist/lib/components/ui/overlays/dialog/use-modal-content.d.ts +2 -0
  66. package/dist/lib/components/ui/primitives/nonce-provider/nonce-provider.vue.d.ts +4 -1
  67. package/dist/lib/components/ui/selection/autocomplete/autocomplete-keyboard.d.ts +22 -4
  68. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-box.vue.d.ts +6 -2
  69. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-list.vue.d.ts +7 -0
  70. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-active.d.ts +17 -0
  71. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-keydown.d.ts +21 -0
  72. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-open.d.ts +3 -3
  73. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete.d.ts +2 -1
  74. package/dist/lib/components/ui/selection/autocomplete/use-drawer-room.d.ts +11 -0
  75. package/dist/lib/components/ui/selection/autocomplete/use-query-change.d.ts +8 -0
  76. package/dist/lib/components/ui/widgets/charts/chart-container.vue.d.ts +2 -2
  77. package/dist/lib/components/ui/widgets/kanban/partials/kanban-empty.vue.d.ts +1 -0
  78. package/dist/lib/composables/use-background-activity.d.ts +8 -0
  79. package/dist/lib/i18n/resources/en/en.d.ts +24 -0
  80. package/dist/lib/i18n/resources/en/material.json.d.ts +25 -1
  81. package/dist/lib/i18n/resources/fr/fr.d.ts +24 -0
  82. package/dist/lib/i18n/resources/fr/material.json.d.ts +25 -1
  83. package/dist/lib/i18n/resources/resources.d.ts +48 -0
  84. package/dist/lib/i18n/translator.d.ts +1 -1
  85. package/dist/lib/lib/index.d.ts +1 -0
  86. package/dist/lib/lib/is-production.d.ts +11 -0
  87. package/dist/lib/nuxt/component-names.d.ts +1 -1
  88. package/dist/lib/nuxt/module.d.ts +2 -11
  89. package/dist/lib/nuxt/pdf-fonts.d.ts +1 -1
  90. package/dist/lib/nuxt/plugin-options.d.ts +58 -0
  91. package/dist/lib/vite/index.d.ts +4 -8
  92. package/dist/lib.js +52 -51
  93. package/dist/main.js +17 -17
  94. package/dist/map.js +4 -4
  95. package/dist/material-vue.css +1 -1
  96. package/dist/{merge-BzRhCAF6.js → merge-CUkhQBjf.js} +1 -1
  97. package/dist/{mermaid.core-B2ydTQ4X.js → mermaid.core-B70hvAGW.js} +11 -11
  98. package/dist/{mindmap-definition-NLK3R4M7-DCb99I7s.js → mindmap-definition-NLK3R4M7-B59LL3gV.js} +1 -1
  99. package/dist/{pdf-outline-sidebar-BU4o_hBI.js → pdf-outline-sidebar-ChDcGkbS.js} +2 -2
  100. package/dist/{pdf-page-anNe78Lc.js → pdf-page-DFvf3lsq.js} +1 -1
  101. package/dist/{pdf-page-list-CSY76HH6.js → pdf-page-list-K-ncOHkY.js} +2 -2
  102. package/dist/{pdf-reader-BhZNrW7R.js → pdf-reader-BPq6yCdy.js} +15 -15
  103. package/dist/{pdf-sidebar-DcIpG7eh.js → pdf-sidebar-divDHLe7.js} +3 -3
  104. package/dist/{pdf-thumbnail-sidebar-CUC0V4if.js → pdf-thumbnail-sidebar-C8XMXCq0.js} +4 -4
  105. package/dist/{pdf-toolbar-C373S-ul.js → pdf-toolbar-DQdedyLO.js} +4 -4
  106. package/dist/pdf.js +2 -2
  107. package/dist/{print-format.utils-C81c0weJ.js → print-format.utils-DutRk52h.js} +2 -2
  108. package/dist/{requirementDiagram-PLB6GJNP-DnkvMOW6.js → requirementDiagram-PLB6GJNP-BvzdTtRf.js} +1 -1
  109. package/dist/{sortable-provider-CL2UGGn8.js → sortable-provider-CwnKfRFb.js} +2 -2
  110. package/dist/{stateDiagram-v2-GCMORJYK-BCh7NZQn.js → stateDiagram-v2-GCMORJYK-VPagBr-W.js} +1 -1
  111. package/dist/{stored-language-Wtcj2HsU.js → stored-language-DVP9KzRu.js} +50 -2
  112. package/dist/{stx-player-CMmM3F5Z.js → stx-player-NiBlUGm4.js} +12 -12
  113. package/dist/styles.css +4 -0
  114. package/dist/{swimlanesDiagram-TC7HE7FX-D4-k5-iq.js → swimlanesDiagram-TC7HE7FX-BA-757S-.js} +2 -2
  115. package/dist/{translator-D5Dp-dob.js → translator-Dpfm1mAC.js} +171 -159
  116. package/dist/use-background-activity-DdTBbNpX.js +17 -0
  117. package/dist/{use-heading-spy-BIBHtUXN.js → use-heading-spy-jn8f2ioE.js} +13 -13
  118. package/dist/{use-material-translation-COoBTGpB.js → use-material-translation-CHacJbhU.js} +3 -3
  119. package/dist/{usecaseDiagram-POWQR4AR-Bi5we59l.js → usecaseDiagram-POWQR4AR-1f4iixDN.js} +1 -1
  120. package/dist/vite.js +7 -2
  121. package/docs/README.md +3 -0
  122. package/docs/components/buttons-and-actions.md +2 -0
  123. package/docs/components/data-table-filter-and-lists.md +59 -8
  124. package/docs/components/feedback-and-status.md +79 -8
  125. package/docs/components/media-and-files.md +2 -0
  126. package/docs/components/navigation-and-layout.md +13 -5
  127. package/docs/components/overlays-and-menus.md +20 -3
  128. package/docs/components/primitives.md +5 -3
  129. package/docs/components/rich-widgets.md +1 -1
  130. package/docs/components/selection-and-choice.md +14 -4
  131. package/docs/getting-started.md +22 -3
  132. package/docs/guides/app-shell.md +1 -1
  133. package/docs/guides/backgrounds.md +143 -0
  134. package/docs/guides/csp.md +48 -6
  135. package/docs/guides/helpers.md +1 -0
  136. package/docs/guides/i18n.md +51 -1
  137. package/docs/guides/migrate-to-vite-plugin.md +259 -0
  138. package/docs/guides/pdf.md +11 -4
  139. package/docs/guides/ssr.md +1 -1
  140. package/docs/guides/theming.md +52 -4
  141. package/docs/guides/vite-plugin.md +194 -0
  142. package/docs/previews/backgrounds-matrix.webp +0 -0
  143. package/docs/previews/feedback-status-emptystate.webp +0 -0
  144. package/docs/previews/feedback-status-errorstate.webp +0 -0
  145. package/docs/roadmap.md +60 -32
  146. package/docs/troubleshooting.md +251 -7
  147. package/lib/backgrounds/backgrounds.css +8 -0
  148. package/lib/backgrounds/css/base.css +169 -0
  149. package/lib/backgrounds/css/gradient.css +53 -0
  150. package/lib/backgrounds/css/grain.css +19 -0
  151. package/lib/backgrounds/css/motion.css +30 -0
  152. package/lib/components/base.css +5 -2
  153. package/lib/components/components.css +5 -0
  154. package/lib/components/theme-tokens.css +21 -0
  155. package/lib/nuxt/component-names.ts +1 -0
  156. package/lib/nuxt/module.ts +29 -15
  157. package/lib/nuxt/pdf-fonts.ts +1 -1
  158. package/lib/nuxt/plugin-options.ts +92 -0
  159. package/lib/nuxt/runtime/inter.css +6 -0
  160. package/lib/styles.css +4 -0
  161. package/lib/vite/index.ts +19 -2
  162. package/package.json +24 -2
@@ -0,0 +1,17 @@
1
+ import { onBeforeUnmount as e, onMounted as t, ref as n, watch as r } from "vue";
2
+ import { observeBackgroundActivity as i } from "@nxgt/material-core/backgrounds";
3
+ //#region lib/composables/use-background-activity.ts
4
+ function a(a, o) {
5
+ let s = n(!1), c, l = () => {
6
+ c?.(), c = void 0, s.value = !1;
7
+ }, u = () => {
8
+ l(), o() && a.value && (c = i(a.value, (e) => {
9
+ s.value = e;
10
+ }));
11
+ };
12
+ return t(() => {
13
+ u(), r(o, u);
14
+ }), e(l), s;
15
+ }
16
+ //#endregion
17
+ export { a as t };
@@ -1,29 +1,29 @@
1
- import { a as e } from "./i18n-provider-DmZSTBnp.js";
1
+ import { a as e } from "./i18n-provider-tL27-a8y.js";
2
2
  import { c as t, d as n, l as r, n as i, s as a, u as o } from "./number.utils-C1eB3GDr.js";
3
3
  import { computed as s, getCurrentInstance as c, inject as l, onMounted as u, ref as d, shallowRef as f, toValue as p, watch as m } from "vue";
4
4
  import { injectConfigProviderContext as h } from "reka-ui";
5
5
  import { useIntersectionObserver as g, useMediaQuery as _, useSSRWidth as v, useStorage as y } from "@vueuse/core";
6
+ //#region lib/composables/use-ssr-media-query.ts
7
+ var b = 1024;
8
+ function x(e) {
9
+ return c() ? _(e, { ssrWidth: v() ?? b }) : _(e);
10
+ }
11
+ //#endregion
6
12
  //#region lib/composables/resolve-theme.ts
7
- function b(e, t) {
13
+ function S(e, t) {
8
14
  let n = e === "light" || e === "dark" ? e : "system";
9
15
  return {
10
16
  theme: n === "system" ? t ? "dark" : "light" : n,
11
17
  preference: n
12
18
  };
13
19
  }
14
- function x(e) {
15
- typeof document > "u" || (document.documentElement.classList.toggle("dark", e === "dark"), document.documentElement.style.colorScheme = e);
16
- }
17
- //#endregion
18
- //#region lib/composables/use-ssr-media-query.ts
19
- var S = 1024;
20
20
  function C(e) {
21
- return c() ? _(e, { ssrWidth: v() ?? S }) : _(e);
21
+ typeof document > "u" || (document.documentElement.classList.toggle("dark", e === "dark"), document.documentElement.style.colorScheme = e);
22
22
  }
23
23
  //#endregion
24
24
  //#region lib/composables/use-theme.ts
25
25
  function w() {
26
- let e = C("(prefers-color-scheme: dark)"), t = y("theme", null, void 0, { initOnMounted: !0 }), n = s(() => b(t.value, !!e.value));
26
+ let e = x("(prefers-color-scheme: dark)"), t = y("theme", null, void 0, { initOnMounted: !0 }), n = s(() => S(t.value, !!e.value));
27
27
  return {
28
28
  get theme() {
29
29
  return n.value.theme;
@@ -32,7 +32,7 @@ function w() {
32
32
  return n.value.preference;
33
33
  },
34
34
  updateTheme: (n) => {
35
- n === "system" ? t.value = null : t.value = n, x(b(n === "system" ? void 0 : n, !!e.value).theme);
35
+ n === "system" ? t.value = null : t.value = n, C(S(n === "system" ? void 0 : n, !!e.value).theme);
36
36
  }
37
37
  };
38
38
  }
@@ -40,7 +40,7 @@ function w() {
40
40
  //#region lib/composables/use-mobile.ts
41
41
  var T = 768;
42
42
  function E() {
43
- return C(`(max-width: ${T - 1}px)`);
43
+ return x(`(max-width: ${T - 1}px)`);
44
44
  }
45
45
  //#endregion
46
46
  //#region ../../node_modules/date-fns/locale/fr/_lib/formatDistance.js
@@ -495,4 +495,4 @@ function q(e, t, n) {
495
495
  });
496
496
  }
497
497
  //#endregion
498
- export { V as a, w as c, b as d, W as i, C as l, U as n, B as o, H as r, E as s, q as t, x as u };
498
+ export { V as a, w as c, x as d, W as i, C as l, U as n, B as o, H as r, E as s, q as t, S as u };
@@ -1,6 +1,6 @@
1
- import "./stored-language-Wtcj2HsU.js";
2
- import { a as e } from "./i18n-provider-DmZSTBnp.js";
3
- import { i as t, r as n } from "./translator-D5Dp-dob.js";
1
+ import "./stored-language-DVP9KzRu.js";
2
+ import { a as e } from "./i18n-provider-tL27-a8y.js";
3
+ import { i as t, r as n } from "./translator-Dpfm1mAC.js";
4
4
  //#region lib/i18n/translate-material.ts
5
5
  function r(e, r, i, a) {
6
6
  if (e) for (let t of /* @__PURE__ */ new Set([e, e.split("-")[0]])) {
@@ -12,7 +12,7 @@ import "./chunk-Z7XXMR3K-DdnrI6vA.js";
12
12
  import { t as ee } from "./chunk-XXDRQBXY-B9ckANit.js";
13
13
  import { t as te } from "./chunk-WEXAMYUT-DufdZ2tS.js";
14
14
  import "./chunk-UA2S7LBM-DN55-gzN.js";
15
- import { r as ne, t as re } from "./chunk-GNY47TPC-BByCj-tp.js";
15
+ import { r as ne, t as re } from "./chunk-GNY47TPC-D4sl5ZSh.js";
16
16
  //#region ../../node_modules/chevrotain/node_modules/lodash-es/_freeGlobal.js
17
17
  var ie = typeof global == "object" && global && global.Object === Object && global, ae = typeof self == "object" && self && self.Object === Object && self, b = ie || ae || Function("return this")(), oe = b.Symbol, se = Object.prototype, ce = se.hasOwnProperty, le = se.toString, ue = oe ? oe.toStringTag : void 0;
18
18
  function de(e) {
package/dist/vite.js CHANGED
@@ -1,2 +1,7 @@
1
- import { cspAssetsInlineLimit as e, pdfkitStandardFonts as t } from "@nxgt/material-core/vite";
2
- export { e as cspAssetsInlineLimit, t as pdfkitStandardFonts };
1
+ import { CSP_NONCE_PLACEHOLDER as e, ZOD_JITLESS_SCRIPT as t, cspAssetsInlineLimit as n, nxgtMaterial as r, pdfkitStandardFonts as i } from "@nxgt/material-core/vite";
2
+ //#region lib/vite/index.ts
3
+ function a(e) {
4
+ return r(e, "@nxgt/material-vue");
5
+ }
6
+ //#endregion
7
+ export { e as CSP_NONCE_PLACEHOLDER, t as ZOD_JITLESS_SCRIPT, n as cspAssetsInlineLimit, a as nxgtMaterial, i as pdfkitStandardFonts };
package/docs/README.md CHANGED
@@ -14,8 +14,11 @@ detail: every option, the second example, and the traps.
14
14
  | [Printing](guides/printing.md) | You lay out a printable document (invoice, report) with the `Print*` components |
15
15
  | [PDF documents](guides/pdf.md) | You produce a PDF file from data with `@nxgt/material-vue/pdf`: in the browser (`usePdf`, `PdfDownloadButton`) or from a Nuxt server route (`pdfResponse`), fonts on an inlined server |
16
16
  | [Maps](guides/maps.md) | You render Leaflet maps or a location field with `@nxgt/material-vue/map` |
17
+ | [Backgrounds](guides/backgrounds.md) | You want a gradient or patterned backdrop: tones, intensity, the opt-in `backgrounds.css`, custom colours, motion and CSP |
17
18
  | [SSR](guides/ssr.md) | Your app renders on a server — Nuxt or a Vite SSR server — and bundles this package |
18
19
  | [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 |
20
+ | [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 the Nuxt module, and each option's opt-out |
21
+ | [Migrating to the Vite plugin](guides/migrate-to-vite-plugin.md) | Your app has its own CSP header, nonce, `zod-config`, Inter import or PDF bundling, and you move it to `nxgtMaterial()`: search → replace pairs, each with how to verify |
19
22
  | [Helpers](guides/helpers.md) | You need a utility, composable, Pinia store factory or type from `/lib`, `/composables`, `/i18n`, `/models`, `/types` or `/dnd` |
20
23
  | [Troubleshooting](troubleshooting.md) | Something failed and you have an error message or a symptom |
21
24
  | [Roadmap](roadmap.md) | You want to know what is planned, what shipped, and what will not be done |
@@ -298,6 +298,8 @@ replace that name, so pass one only when it should differ from the label.
298
298
  own, so it is not announced as "collapsed". Used as a popover, drawer or menu
299
299
  trigger (`as-child`), it carries that trigger's `aria-expanded`.
300
300
 
301
+ A `tooltip` always opens below 768 px or with `expanded={false}`. On a desktop width, or with `expanded`, one that repeats the visible label is not opened, and one that adds to it ("Create order (Ctrl+N)") still opens.
302
+
301
303
  **Storybook:** `Buttons & actions/ResponsiveButton` · **React:** `ResponsiveButton`
302
304
 
303
305
  ---
@@ -80,7 +80,8 @@ table enables, for typing your own column defs.
80
80
  | `data` | `T[]` | — | rows; sorting, filtering and pagination run client-side |
81
81
  | `#expanded="{ row }"` / `renderExpandedRow` | slot / `(row) => VNode` | — | makes rows expandable; pair with the `expand` column |
82
82
  | `showViewOptions` | `boolean` | — | column visibility menu above the table |
83
- | `#actions="{ table }"` / `actions` | slot / `(table) => VNode` | — | toolbar content with access to the table (selected rows…) |
83
+ | `#actions="{ table }"` / `actions` | slot / `(table) => VNode` | — | toolbar content with access to the table (selected rows…); disabled while `loading` |
84
+ | `#filters="{ table }"` / `filters` | slot / `(table) => VNode` | — | a search or filter controls, on the toolbar's start side; unlike `actions` they stay enabled while `loading`, so a search typed during a reload keeps its focus and text |
84
85
  | `onLoadMore` / `isLoadingMore` | `() => void` / `boolean` | — | adds a "load more" button under the rows |
85
86
  | `options` | `{ showPage?: boolean; showSelected?: boolean }` | both shown | parts of the footer |
86
87
  | `showHeader` | `boolean` | shown | header row (column names, sorting, "Select all") at every width; `false` hides it |
@@ -89,10 +90,13 @@ table enables, for typing your own column defs.
89
90
  | `loading` | `boolean` | `false` | skeleton rows, or the current rows dimmed; `aria-busy`; controls disabled |
90
91
  | `loadingRows` | `number` | the page size | how many skeleton rows |
91
92
  | `density` | `'comfortable' \| 'compact'` | `'comfortable'` | `compact` tightens the cell padding, the row and header heights and the gap between rows, the selection column included; it is the table's `data-density` attribute, styled with classes (no inline style). The toolbar band and the empty state keep their size; the print variant is unchanged |
92
- | `#empty="{ filtered }"` | slot | icon and "No data available" | content of the empty row, spanning every visible column |
93
+ | `#empty="{ filtered }"` | slot | a compact `EmptyState`: "No data available", or "No results" when filtered | content of the empty row, spanning every visible column |
94
+ | `#empty-filtered` | slot | none (falls back to `#empty`, then the default) | content of the empty row when rows exist but the filters hide every one; wins over `#empty` then |
93
95
  | `manual` | `boolean` | `false` | `data` is one server page: no client filtering, sorting or paging; the footer is the server footer ([server-side data](#server-side-data)) |
94
96
  | `v-model:sorting` | `SortingState` | held inside | controlled sorting; `update:sorting` gets the new state |
95
97
  | `getRowId` | `(row, index) => string` | the index | the row's id, the key of the row selection |
98
+ | `enableRowSelection` | `boolean \| (row) => boolean` | `true` | which rows can be selected (`row` is the data). A row that cannot has a disabled checkbox; "Select all" skips it and its checked and indeterminate states count only selectable rows. A locked row already in the selection can be cleared by its own box (and only cleared) |
99
+ | `rowSelectionDisabledReason` | `(row) => string \| undefined` | — | why a row cannot be selected: its checkbox is `aria-disabled` but stays focusable, shows the reason in a tooltip on hover and focus, and has it as its `aria-describedby`; without a reason it is a plain `disabled` box |
96
100
  | `v-model:row-selection` | `RowSelectionState` | held inside | controlled selection, by id; may hold rows of other pages |
97
101
  | `pagination` | `{ totalCount?, offset?, previous?, next? }` | — | the server footer's range ("41–60 of 133") and Previous / Next: an href (a link) or a callback (a button) |
98
102
  | `#footer="{ table }"` / `footer` | slot / `false \| (table) => VNode` | the pagination | replaces the footer; `false` removes it |
@@ -141,13 +145,60 @@ arrives. Sorting (`DataTableColumnHeader`), the `select` column's "Select
141
145
  all", the toolbar's native controls (it
142
146
  is a disabled `<fieldset>`, so every input or button you render there, a
143
147
  filter or an export, is disabled
144
- too) and pagination are disabled meanwhile.
145
-
146
- When no row is left after filtering, the empty state is one `TableEmpty`
147
- cell spanning every visible column: today's icon and "No data available" by
148
- default, or the `#empty` slot, told whether rows exist but none matches the
149
- filters (`filtered`); content that renders nothing gets the default.
148
+ too) and pagination are disabled meanwhile. The toolbar's `actions` are in that fieldset; a search or filter you want to keep typing in while the list reloads goes in `#filters` (or `filters`) instead, which sits beside it and stays enabled (its focus and text survive a reload).
149
+
150
+ When no row is left, the empty state is one `TableEmpty` cell spanning every
151
+ visible column. What it holds, first match wins:
152
+
153
+ 1. the `#empty-filtered` slot, when rows exist but the filters hide every one
154
+ (`filtered`);
155
+ 2. the `#empty` slot, told `{ filtered }`;
156
+ 3. the default, a compact `EmptyState`: "No data available"
157
+ (`material.table.labels.no-data`) for a table with no data, or, when
158
+ filtered, "No results" and "Try a different search or clear the filters."
159
+ (`material.table.labels.no-results`, `no-results-description`) announced by the table's
160
+ status.
161
+
162
+ A slot that renders nothing falls through to the next step; there is no blank
163
+ option, the default shows. When the filters hide every row, the table's hidden
164
+ status announces "No results. Try a different search or clear the filters."
165
+ whatever the visible content is. `#empty-filtered`
166
+ is not used for a table with no data at all, nor with `manual` (server-side
167
+ data): the table cannot tell a filtered page from an empty one there, so it
168
+ reads `filtered` as `false`; branch on your own filter state in `#empty`.
150
169
  `DataTableEmptyState` types the slot props.
170
+ `filtered` is computed from the core row model (`getCoreRowModel().rows.length > 0`), as in React.
171
+
172
+ ```vue
173
+ <DataTable :data="people" :columns="columns">
174
+ <template #empty>No people yet.</template>
175
+ <template #empty-filtered>No person matches these filters.</template>
176
+ </DataTable>
177
+ ```
178
+
179
+ ### Locked rows and a search that stays usable
180
+
181
+ `enableRowSelection` leaves rows out of the selection, `rowSelectionDisabledReason` says why, and `#filters` keeps a search enabled while the list reloads (`#actions` is disabled meanwhile):
182
+
183
+ ```vue
184
+ <DataTable
185
+ v-model:row-selection="selection"
186
+ :columns="columns"
187
+ :data="proposals"
188
+ :loading="isFetching"
189
+ :enable-row-selection="(proposal) => proposal.status !== 'applied'"
190
+ :row-selection-disabled-reason="() => 'Already applied'"
191
+ >
192
+ <template #filters>
193
+ <SearchField v-model="q" label="Search" />
194
+ </template>
195
+ <template #actions="{ table }">
196
+ <Button>Apply to {{ table.getSelectedRowModel().rows.length }}</Button>
197
+ </template>
198
+ </DataTable>
199
+ ```
200
+
201
+ A locked row's checkbox is `aria-disabled` but focusable, so Tab reaches it and the tooltip says why; "Select all" skips it.
151
202
 
152
203
  ### Phones and narrow screens
153
204
 
@@ -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) | Icon, title, description and action, with presets |
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
  ![EmptyState](../previews/feedback-status-emptystate.webp)
414
415
 
415
- Icon in a tinted well, title, description and action; not-found,
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 variant="search">
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
- | `variant` | `'not-found' \| 'unauthorized' \| 'generic' \| 'empty' \| 'offline' \| 'search' \| 'maintenance'` | `'generic'` | preset icon, well colour, title and description |
437
- | `#icon` slot | — | the preset's | replaces the icon |
438
- | `title` / `description` | `string` (or `#title` / `#description`) | the preset's (translated) | replace the preset text; see below |
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 preset's 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.
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
+ ![ErrorState](../previews/feedback-status-errorstate.webp)
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
  ![Progress](../previews/feedback-status-progress.webp)
@@ -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 |
@@ -78,7 +78,7 @@ which work alone; `findActiveMenuItem`, `isMenuItemActive` and
78
78
  | `avatarSrc` | `string` | — | avatar image in the header |
79
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
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 |
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) |
82
82
  | `#header-actions`, `#avatar` | slots | — | header, before the avatar; `#avatar` replaces it |
83
83
  | `title`, `subtitle`, `#actions` | `string`, `string`, slot | — | `ActivityContent`'s header card |
84
84
  | `#header` / `hideHeader` | slot / `boolean` | — / `false` | `ActivityContent`: replace or drop the header card |
@@ -780,7 +780,7 @@ Parts: `Doc` (the shell), `DocPage` (header, content, see also and pager);
780
780
  the pieces it composes — `DocTopbar`, `DocSidebar`, `DocNav`, `DocArticle`,
781
781
  `DocToc`, `DocPager`, `DocSearch`, `DocSeeAlso`, `DocChildCards`,
782
782
  `DocProgress`, `DocSkipLink`, `DocHeader`, `DocCopyPage`, `DocCopyMarkdown`,
783
- `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).
784
784
  `useDoc` / `provideDoc` / `useDocContext` expose the state for a shell of
785
785
  your own, built from those parts:
786
786
 
@@ -878,9 +878,17 @@ import { Background, Card } from '@nxgt/material-vue/components';
878
878
  </template>
879
879
  ```
880
880
 
881
- No props but `class`. It fills the viewport; pass a `class` such as
882
- `h-96 w-full` to use it as a band. `ActivityLayout` and `ThreadLayout` sit on
883
- it.
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).
884
892
 
885
893
  **Storybook:** `Navigation & layout/Background` · **React:** `Background`
886
894
 
@@ -39,7 +39,16 @@ default) leave the page as it is.
39
39
 
40
40
  ![Dialog](../previews/overlays-menus-dialog.webp)
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 are English, not translated — pass your own. It does not bind a
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`, `error` is `text-error-text`; `default` inherits (a `class` colour still wins) |
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 (none at the root) |
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 empty state | what an empty chart shows |
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 Tab
340
- skips it, and is never picked, by click or by Enter (which picks the first
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`. The keyboard works as before.
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:
@@ -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: { bundleStandardFonts: true }, // a server with no node_modules that renders /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.bundleStandardFonts` | `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)) |
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