@nxgt/material-vue 0.34.0 → 0.36.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.
Files changed (113) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/README.md +11 -5
  3. package/dist/{Button-DEA5W6O3.js → Button-CurFF4Zt.js} +1 -1
  4. package/dist/{HoverCardTrigger-CjSteFgk.js → HoverCardTrigger-C3r0oc5d.js} +2 -2
  5. package/dist/{camera-C52IuvMH.js → camera-CaQlF4JJ.js} +4 -4
  6. package/dist/components/components.css +1 -0
  7. package/dist/components/print-only.css +44 -0
  8. package/dist/{components-DFEmacXJ.js → components-D56FEfTa.js} +23597 -23041
  9. package/dist/components.js +8 -8
  10. package/dist/composables.js +35 -25
  11. package/dist/dnd.js +1 -1
  12. package/dist/{form-scope-CaYs-o2w.js → form-scope-DOL9MJL3.js} +1 -1
  13. package/dist/{i18n-provider-CiVi05fs.js → i18n-provider-B2q6vp6H.js} +1 -1
  14. package/dist/i18n.js +4 -4
  15. package/dist/{icon-button-CWcOcNHr.js → icon-button-CIUc1bEp.js} +1 -1
  16. package/dist/lib/components/index.d.ts +1 -0
  17. package/dist/lib/components/ui/actions/button/Button.vue.d.ts +1 -1
  18. package/dist/lib/components/ui/actions/button/button.variants.d.ts +1 -1
  19. package/dist/lib/components/ui/actions/copy-button/copy-button.vue.d.ts +1 -1
  20. package/dist/lib/components/ui/actions/copy-button/use-copy-status.d.ts +14 -0
  21. package/dist/lib/components/ui/actions/icon-button/icon-button.variants.d.ts +1 -1
  22. package/dist/lib/components/ui/actions/icon-button/icon-button.vue.d.ts +1 -1
  23. package/dist/lib/components/ui/actions/scroll-to-top/scroll-to-top.vue.d.ts +1 -1
  24. package/dist/lib/components/ui/display/badge/index.d.ts +1 -1
  25. package/dist/lib/components/ui/display/cards/cards.gallery.d.ts +3 -3
  26. package/dist/lib/components/ui/display/chip/chip.utils.d.ts +9 -0
  27. package/dist/lib/components/ui/display/chip/chip.vue.d.ts +29 -16
  28. package/dist/lib/components/ui/display/chip/index.d.ts +1 -0
  29. package/dist/lib/components/ui/display/count-badge/count-badge.vue.d.ts +1 -1
  30. package/dist/lib/components/ui/display/enrolment-secret/enrolment-secret.vue.d.ts +30 -0
  31. package/dist/lib/components/ui/display/enrolment-secret/index.d.ts +3 -0
  32. package/dist/lib/components/ui/display/index.d.ts +2 -0
  33. package/dist/lib/components/ui/display/recovery-codes/index.d.ts +5 -0
  34. package/dist/lib/components/ui/display/recovery-codes/partials/recovery-codes-actions.vue.d.ts +11 -0
  35. package/dist/lib/components/ui/display/recovery-codes/partials/recovery-codes-confirm.vue.d.ts +15 -0
  36. package/dist/lib/components/ui/display/recovery-codes/partials/recovery-codes-list.vue.d.ts +7 -0
  37. package/dist/lib/components/ui/display/recovery-codes/recovery-codes.vue.d.ts +56 -0
  38. package/dist/lib/components/ui/display/recovery-codes/types.d.ts +23 -0
  39. package/dist/lib/components/ui/display/recovery-codes/use-recovery-codes-labels.d.ts +18 -0
  40. package/dist/lib/components/ui/feedback/alert/alert.vue.d.ts +1 -1
  41. package/dist/lib/components/ui/feedback/circular-progress/circular-progress.vue.d.ts +2 -2
  42. package/dist/lib/components/ui/feedback/spinner/spinner.variants.d.ts +1 -1
  43. package/dist/lib/components/ui/feedback/spinner/spinner.vue.d.ts +1 -1
  44. package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
  45. package/dist/lib/components/ui/fields/form-section/form-section-classes.d.ts +30 -0
  46. package/dist/lib/components/ui/fields/form-section/form-section.vue.d.ts +37 -0
  47. package/dist/lib/components/ui/fields/form-section/index.d.ts +3 -0
  48. package/dist/lib/components/ui/fields/index.d.ts +1 -0
  49. package/dist/lib/components/ui/fields/rating-field/rating-field.variants.d.ts +1 -1
  50. package/dist/lib/components/ui/fields/rating-field/rating-field.vue.d.ts +1 -1
  51. package/dist/lib/components/ui/forms/form-scope-schema.d.ts +8 -2
  52. package/dist/lib/components/ui/forms/index.d.ts +1 -0
  53. package/dist/lib/components/ui/forms/use-code-resolver.d.ts +45 -0
  54. package/dist/lib/components/ui/layout/app-shell/activity-content.vue.d.ts +1 -1
  55. package/dist/lib/components/ui/layout/auth-card/auth-card-notice.vue.d.ts +18 -0
  56. package/dist/lib/components/ui/layout/auth-card/auth-card.vue.d.ts +45 -0
  57. package/dist/lib/components/ui/layout/auth-card/index.d.ts +3 -0
  58. package/dist/lib/components/ui/layout/auth-card/types.d.ts +2 -0
  59. package/dist/lib/components/ui/layout/index.d.ts +1 -0
  60. package/dist/lib/components/ui/selection/select-field/select-field.vue.d.ts +2 -0
  61. package/dist/lib/composables/index.d.ts +2 -0
  62. package/dist/lib/composables/use-focus-on-mount.d.ts +23 -0
  63. package/dist/lib/composables/use-submit-once.d.ts +33 -0
  64. package/dist/lib/i18n/resources/en/en.d.ts +21 -0
  65. package/dist/lib/i18n/resources/en/material.json.d.ts +22 -1
  66. package/dist/lib/i18n/resources/fr/fr.d.ts +21 -0
  67. package/dist/lib/i18n/resources/fr/material.json.d.ts +22 -1
  68. package/dist/lib/i18n/resources/resources.d.ts +42 -0
  69. package/dist/lib/i18n/translator.d.ts +1 -1
  70. package/dist/lib/lib/index.d.ts +1 -0
  71. package/dist/lib/nuxt/auto-imports.d.ts +1 -1
  72. package/dist/lib/nuxt/component-names.d.ts +1 -1
  73. package/dist/lib.js +59 -104
  74. package/dist/main.js +17 -17
  75. package/dist/map.js +4 -4
  76. package/dist/material-vue.css +1 -1
  77. package/dist/{pdf-outline-sidebar-C_iJ8gqD.js → pdf-outline-sidebar-Gmdd3RNG.js} +2 -2
  78. package/dist/{pdf-page-DOiJlWQn.js → pdf-page-C3XhLcrp.js} +1 -1
  79. package/dist/{pdf-page-list-BTmUd7Ga.js → pdf-page-list-BTIb5Y-Y.js} +2 -2
  80. package/dist/{pdf-reader-CEMMLJnB.js → pdf-reader-CX2owreo.js} +8 -8
  81. package/dist/{pdf-sidebar-KLd76E5S.js → pdf-sidebar-BmAJ8Q_m.js} +3 -3
  82. package/dist/{pdf-thumbnail-sidebar-B4JkT_LV.js → pdf-thumbnail-sidebar-C04OeZ9M.js} +3 -3
  83. package/dist/{pdf-toolbar-Da6UMt1O.js → pdf-toolbar-zI0_eNXV.js} +4 -4
  84. package/dist/pdf.js +2 -2
  85. package/dist/print-format.utils-keJqahvw.js +187 -0
  86. package/dist/{sortable-provider-Cvpqk8tp.js → sortable-provider-DMu8DL2z.js} +2 -2
  87. package/dist/{stored-language-GDRdeYD8.js → stored-language-dtksFoGO.js} +44 -2
  88. package/dist/{stx-player-B6-PSgzP.js → stx-player-BUo2Vs0j.js} +5 -5
  89. package/dist/{translator-BDFEmlcL.js → translator-DqiGrSH_.js} +1 -1
  90. package/dist/{use-heading-spy-ClhUg50z.js → use-heading-spy-BZ7ctGZ5.js} +58 -48
  91. package/dist/{use-material-translation-CaHS6oZz.js → use-material-translation-DRB6oPdF.js} +3 -3
  92. package/docs/components/README.md +3 -3
  93. package/docs/components/data-display.md +126 -2
  94. package/docs/components/data-table-filter-and-lists.md +2 -1
  95. package/docs/components/form-fields.md +81 -0
  96. package/docs/components/navigation-and-layout.md +64 -0
  97. package/docs/components/selection-and-choice.md +2 -1
  98. package/docs/guides/forms.md +120 -0
  99. package/docs/guides/helpers.md +85 -0
  100. package/docs/guides/i18n.md +17 -0
  101. package/docs/guides/printing.md +27 -0
  102. package/docs/previews/data-display-enrolmentsecret.webp +0 -0
  103. package/docs/previews/data-display-recoverycodes.webp +0 -0
  104. package/docs/previews/form-fields-formsection.webp +0 -0
  105. package/docs/previews/navigation-layout-authcard.webp +0 -0
  106. package/docs/roadmap.md +48 -15
  107. package/docs/troubleshooting.md +92 -0
  108. package/lib/components/components.css +1 -0
  109. package/lib/components/print-only.css +44 -0
  110. package/lib/nuxt/auto-imports.ts +2 -0
  111. package/lib/nuxt/component-names.ts +4 -0
  112. package/package.json +2 -2
  113. package/dist/print-format.utils-DpBtUJvL.js +0 -138
@@ -366,6 +366,19 @@ fichier.") when it cannot show it, `material.file-preview.download`
366
366
  "Seuls les premiers {size} de ce fichier sont affichés.") under a text cut
367
367
  by the size cap; the text or Markdown it shows scrolls in a region named by
368
368
  `material.file-preview.content` ("Contents of {name}", "Contenu de {name}").
369
+ `EnrolmentSecret` reads `material.enrolment-secret.qr-label` ("QR code to
370
+ scan with your authenticator app"), `open-app` ("Open in your authenticator
371
+ app", "Ouvrir dans votre application d'authentification"), `manual` ("Or
372
+ type this key in the app:"), `secret-label` ("Setup key", "Clé de
373
+ configuration"), `copy` ("Copy the key") and `copied` ("Key copied", "Clé
374
+ copiée"); each has a prop that overrides it. `RecoveryCodes` reads
375
+ `material.recovery-codes.title` and `list` ("Recovery codes", "Codes de
376
+ récupération"), `warning` ("Each code works once. Keep them somewhere safe:
377
+ they are shown only now."), `copy`, `copied` and `copy-failed` ("Copy codes",
378
+ "Codes copied", "Copy failed"), `download`, `print`, `saved` ("I have saved
379
+ these codes", "J'ai enregistré ces codes"), `saved-required` ("Tick the box
380
+ once your codes are safe") and `done`; `title`, `warning` and `labels`
381
+ override them. `AuthCard` has no copy of its own.
369
382
  `material.slider.value` / `minimum` / `maximum`
370
383
  ("Value", "Minimum", "Maximum") are there for `@nxgt/material`'s editable
371
384
  `Slider` readout inputs (this package's readout is text), so the bundles hold
@@ -480,6 +493,10 @@ z.config({
480
493
 
481
494
  Examples and per-field messages are in [forms](forms.md#validation-messages).
482
495
 
496
+ Your own rules' messages — a refinement carrying an error code in
497
+ `params.code` — are worded by your translator, in the provider's language,
498
+ with `useCodeResolver`: see [forms](forms.md#wording-errors-by-code).
499
+
483
500
  ## Server rendering
484
501
 
485
502
  On the server there is no `localStorage`, so without help the server renders
@@ -389,3 +389,30 @@ kebab-case (`is-header`, `text-align`, `qr-data-url`), and replace `Document`
389
389
  and `Page` with a `div` styled `tw(commonPrintStyles.page)`. `PrintDocument`,
390
390
  `PrintDataTable`, `PrintKeyValue` and `PrintMetric` have none: use `/pdf`'s
391
391
  `page`, `dataTable`, `keyValueOf` and `metric`.
392
+
393
+ ## Printing one element of the page
394
+
395
+ `RecoveryCodes`' "Print" button prints its card and nothing else, with
396
+ `window.print()`, no inline style and no new window. The same helper prints
397
+ any element of yours: `printOnly(element, name?)` from
398
+ `@nxgt/material-vue/lib` marks `<html data-nxgt-print="<name>">` and
399
+ the element (`data-nxgt-print-target`), opens the print dialog, and removes
400
+ both once printing is over (on `afterprint`, or as soon as `print()` returns
401
+ where it blocks). The rules that hide the rest of the page are in the
402
+ package's `styles.css` (`print-only.css`), under `@media print`: everything but the element,
403
+ what holds it and what it holds is taken out of the flow, and the element
404
+ starts at the top left of the first sheet.
405
+
406
+ ```ts
407
+ import { printOnly } from '@nxgt/material-vue/lib';
408
+
409
+ const stop = printOnly(document.getElementById('ticket') as HTMLElement, 'ticket');
410
+ // When the element unmounts before the dialog closed (a browser whose
411
+ // print() returns at once and never fires afterprint):
412
+ stop();
413
+ ```
414
+
415
+ Hide the controls inside the element with Tailwind's `print:hidden`. If the
416
+ rest of the page still prints, an `!important` print rule of the app's wins
417
+ over the package's: see
418
+ [troubleshooting](../troubleshooting.md#recoverycodes-print-button-prints-the-whole-page).
package/docs/roadmap.md CHANGED
@@ -6,21 +6,6 @@ version they shipped in, and planned entries may name the major they target.
6
6
 
7
7
  ## Now
8
8
 
9
- - **A strict Content Security Policy** — a page that sends `script-src` with
10
- a nonce and no `'unsafe-eval'`, and `style-src-elem` with a nonce and no
11
- `'unsafe-inline'`, runs every component with no violation report: a
12
- `NonceProvider` forwards the nonce to each component, the CSS the
13
- dependencies inject moves into the stylesheet, zod becomes external with
14
- schemas built lazily, and the KaTeX fonts ship as files. Done so far:
15
- KaTeX's fonts are files beside the compiled stylesheet instead of 1.4 MB of
16
- `data:` URIs inside it, so `font-src 'self'` needs no `data:`;
17
- `NonceProvider` takes the nonce to reka's `Select`, `Combobox`,
18
- `ScrollArea` and `Splitter`, `InputOTP` and `RichTextEditor`;
19
- `ImageCropper` is measured and injects no `<style>`, and `InputOTP`'s rules
20
- are in the stylesheet too; zod is no longer bundled and no schema is built
21
- at import, so the app's `z.config({ jitless: true })` keeps zod from
22
- probing `new Function`. Today the
23
- [Content Security Policy guide](guides/csp.md) lists what is left to relax.
24
9
  - **Empty and error states that look designed** — `EmptyState` and a new
25
10
  `ErrorState` in three sizes (`default`, `compact`, `inline`), with in-house
26
11
  inline SVG illustrations or a slot for your own. `ErrorState` is a
@@ -32,6 +17,30 @@ version they shipped in, and planned entries may name the major they target.
32
17
 
33
18
  ## Next
34
19
 
20
+ - **One Vite plugin for the whole setup** — `nxgtMaterial()` from
21
+ `@nxgt/material-vue/vite` is the one call that wires the library
22
+ into a Vite app; it is implemented once in `@nxgt/material-core/vite`, and
23
+ `@nxgt/material/vite` is the same plugin.
24
+ Every option is on by default and each can be turned off or refined; the
25
+ helpers that exist today stay exported.
26
+ - `csp`: a nonce placeholder through `html.cspNonce` and
27
+ `applyCspNonce(html, nonce)` to fill it; zod's jitless mode set by an
28
+ inline nonced script in `index.html` (`ZOD_JITLESS_SCRIPT` for a server
29
+ that renders the page itself); and the recommended policy sent by the dev
30
+ server as Report-Only.
31
+ - `fonts`: the KaTeX fonts emitted as files, and `sans: 'inter'` for a
32
+ self-hosted Inter.
33
+ - `styles`: a build that fails with the exact fix when the stylesheet import
34
+ or the `@source` line is missing.
35
+ - `pdf`: pdfkit's standard fonts handled automatically when
36
+ `@react-pdf/renderer` is installed, and `bundle: true` (the Nuxt module's
37
+ `pdf.bundleStandardFonts` today) for a server that has no
38
+ `node_modules`.
39
+ - Nuxt: the Nuxt module takes the same options.
40
+ - On the server, `@nxgt/material-core` gains `createCspNonce()` (Web Crypto)
41
+ and `cspPolicy({ ... })`, which returns `{ directives, header }` as the
42
+ [Content Security Policy guide](guides/csp.md) describes; `NonceProvider`'s
43
+ `nonce` becomes optional on the client. Ships as minors.
35
44
  - **One media player, video and audio** — `MediaPlayer`, `MediaPlayerMini`,
36
45
  `useMediaPlayer` and `AudioPlayer`, built on the native `<video>` / `<audio>`.
37
46
  `StxPlayer` and `VideoPlayer` are removed in `@nxgt/material-vue` 1.0, with a
@@ -113,6 +122,8 @@ version they shipped in, and planned entries may name the major they target.
113
122
 
114
123
  ## Not planned
115
124
 
125
+ - **A Next.js plugin** — the plugin targets Vite only. No supported consumer
126
+ builds with Next.js, and `withNxgtPrint` stays the config helper for it.
116
127
  - **The `Combobox` primitive family** — `ComboboxFormField` covers forms; the
117
128
  low-level `Combobox` parts stay in `@nxgt/material` only.
118
129
  - **The React library's state and form stacks** — widgets keep their state in
@@ -151,6 +162,28 @@ version they shipped in, and planned entries may name the major they target.
151
162
 
152
163
  ## Shipped
153
164
 
165
+ - **Signed-out pages and two-step verification** — `AuthCard`, the frame of a
166
+ sign-in page (one `main`, the card's title its `h1`, a status or alert
167
+ notice, safe-area padding); `EnrolmentSecret`, a TOTP enrolment's QR code,
168
+ app link and key grouped by four; `RecoveryCodes`, one-time codes to copy,
169
+ download or print alone, with an "I have saved these codes" confirmation
170
+ (`v-model:saved`) an app's router guard can follow. 0.36.0.
171
+ - **Focus after a change, one submit at a time, errors worded by code** —
172
+ `useFocusOnMount` focuses the heading of what replaced the content the user
173
+ acted on; `useSubmitOnce` drops a double click or a second Enter until the
174
+ request ends; `useCodeResolver` words a zod schema's errors in the page's
175
+ language, a refinement's `params.code` in the app's own words. 0.36.0.
176
+ - **Long forms in titled sections** — `FormSection`: a heading (an `h2`, or
177
+ the level `title-as` names), a description and actions, its fields in the form
178
+ grid's columns and one column on a phone, a rule between sections, and a
179
+ group a screen reader announces by its title. 0.36.0.
180
+ - **A strict Content Security Policy** — a page that sends `script-src` with a
181
+ nonce and no `'unsafe-eval'`, and `style-src-elem` with a nonce and no
182
+ `'unsafe-inline'`, runs every component with no violation report:
183
+ `NonceProvider` forwards the nonce to each component, the CSS the
184
+ dependencies inject is in the stylesheet, zod is external and its schemas are
185
+ built on first use, and the KaTeX fonts are files. The
186
+ [Content Security Policy guide](guides/csp.md) has the policy. 0.33.0.
154
187
  - **An `Autocomplete` that searches a server** — `on-search`, `debounce-ms`, `min-query-length`, `filter`, `loading`, `search-error`: a
155
188
  debounced search with an `AbortSignal` whose stale responses are dropped,
156
189
  a polite "Loading…" / "N results" announcement, and local matching that
@@ -102,6 +102,7 @@ words in your error.
102
102
 
103
103
  **Printing**
104
104
 
105
+ - [`RecoveryCodes`' Print button prints the whole page](#recoverycodes-print-button-prints-the-whole-page)
105
106
  - [The table header and the total row print blank](#the-table-header-and-the-total-row-print-blank)
106
107
  - [A `tw()` class has no effect](#a-tw-class-has-no-effect)
107
108
  - [An amount in yen shows no decimals, or one in dinars shows three](#an-amount-in-yen-shows-no-decimals-or-one-in-dinars-shows-three)
@@ -140,6 +141,8 @@ words in your error.
140
141
  - [A test asserting the old Zod string length text fails](#a-test-asserting-the-old-zod-string-length-text-fails)
141
142
  - [A calendar's weekday row is in English on a French page](#a-calendars-weekday-row-is-in-english-on-a-french-page)
142
143
  - [An accessibility audit reports low contrast on a calendar's weekday row or its days](#an-accessibility-audit-reports-low-contrast-on-a-calendars-weekday-row-or-its-days)
144
+ - [Errors keep their old words after a language switch](#errors-keep-their-old-words-after-a-language-switch)
145
+ - [`useCodeResolver` re-validates or resets the form on every render](#usecoderesolver-re-validates-or-resets-the-form-on-every-render)
143
146
 
144
147
  **Runtime**
145
148
 
@@ -154,6 +157,7 @@ words in your error.
154
157
  - [An `Icon` is named "Icon {name}" although you passed `aria-label`](#an-icon-is-named-icon-name-although-you-passed-aria-label)
155
158
  - [An end-to-end test no longer finds the `Close` or `Zoom in` button](#an-end-to-end-test-no-longer-finds-the-close-or-zoom-in-button)
156
159
  - [A test no longer finds a pagination, breadcrumb or other built-in label by its English name](#a-test-no-longer-finds-a-pagination-breadcrumb-or-other-built-in-label-by-its-english-name)
160
+ - [The selection count reads in the wrong order after overriding `material.table.pagination.rows-selected`](#the-selection-count-reads-in-the-wrong-order-after-overriding-materialtablepaginationrows-selected)
157
161
  - [A test no longer finds a chip's remove button by the exact name `Remove`, or a `LocationPreview` by its place name](#a-test-no-longer-finds-a-chips-remove-button-by-the-exact-name-remove-or-a-locationpreview-by-its-place-name)
158
162
  - [A test no longer finds an icon badge by role `button`, or an `Icon` named "Icon {name}" inside a button](#a-test-no-longer-finds-an-icon-badge-by-role-button-or-an-icon-named-icon-name-inside-a-button)
159
163
  - [A button named "Icon plus Add" is now named "Add"](#a-button-named-icon-plus-add-is-now-named-add)
@@ -1959,6 +1963,30 @@ const field = useTemplateRef<ComponentExposed<typeof Autocomplete>>('field');
1959
1963
 
1960
1964
  ## Printing
1961
1965
 
1966
+ ### `RecoveryCodes`' Print button prints the whole page
1967
+
1968
+ **When:** "Print" on `RecoveryCodes` prints the rest of the page with the
1969
+ codes, or the codes away from the top of the sheet.
1970
+ **Why:** the button only marks the page (`<html data-nxgt-print>`) and the
1971
+ card (`data-nxgt-print-target`); hiding the rest is a `@media print` rule in
1972
+ the package's stylesheet (`print-only.css`). A page that does not load
1973
+ `@nxgt/material-vue/styles.css` (or
1974
+ `dist/material-vue.css`) has no such rule, and an app's own
1975
+ print rule that is `!important` (or a `position` on an ancestor set with
1976
+ `!important`) wins over it.
1977
+ **Fix:** load the stylesheet the setup guide names, and keep your print
1978
+ rules off the marked page:
1979
+
1980
+ ```css
1981
+ /* your app's print rules, everywhere but while one element is printed */
1982
+ @media print {
1983
+ html:not([data-nxgt-print]) .page-header { position: static !important; }
1984
+ }
1985
+ ```
1986
+
1987
+ To print an element of your own the same way, call `printOnly(element)`
1988
+ from `@nxgt/material-vue/lib`; the same rules apply to it.
1989
+
1962
1990
  ### The table header and the total row print blank
1963
1991
 
1964
1992
  **When:** on paper or in a PDF, the table header and the grand-total row are
@@ -2531,6 +2559,45 @@ reka hides the weekday row from assistive tech (`<thead aria-hidden>`); in a
2531
2559
  test, find a day by its full date instead
2532
2560
  (`getByRole('gridcell', { name: 'jeudi 1 octobre 2026' })`).
2533
2561
 
2562
+ ### Errors keep their old words after a language switch
2563
+
2564
+ **When:** a form validated with `useCodeResolver` shows an error, the user
2565
+ switches the language, and the error stays in the old one (a code's words
2566
+ or a zod message alike).
2567
+ **Why:** the typed schema is a computed that follows the language, and
2568
+ vee-validate re-validates when its `validationSchema` ref changes — but only
2569
+ when it receives the ref. Passing `validationSchema.value` hands it a plain
2570
+ object, which keeps the language of the first validation.
2571
+ **Fix:** pass the computed itself:
2572
+
2573
+ ```ts
2574
+ const validationSchema = useCodeResolver(schema, { translate });
2575
+ const { handleSubmit } = useForm({ validationSchema }); // not validationSchema.value
2576
+ ```
2577
+
2578
+ Storybook: `Guides/Forms/useCodeResolver`. See
2579
+ [forms](guides/forms.md#wording-errors-by-code).
2580
+
2581
+ ### `useCodeResolver` re-validates or resets the form on every render
2582
+
2583
+ **When:** the schema, `translate` or `codeOf` is created inside a computed
2584
+ or a render function, so `useCodeResolver` is called again with a new one.
2585
+ **Why:** each call makes a new typed schema; vee-validate re-validates every
2586
+ time `validationSchema` changes.
2587
+ **Fix:** call `useCodeResolver` once in `setup`, with a schema, a translator
2588
+ and a `codeOf` defined at module scope (a `language` ref or getter is what
2589
+ should change):
2590
+
2591
+ ```ts
2592
+ import { type CodeIssue, defaultCodeOf, isEmptyRequired, useCodeResolver } from '@nxgt/material-vue/components';
2593
+
2594
+ const schema = z.object({ username: z.string().min(3) });
2595
+ const codeOf = (issue: CodeIssue) =>
2596
+ isEmptyRequired(issue) ? 'errors.required' : defaultCodeOf(issue);
2597
+
2598
+ const validationSchema = useCodeResolver(schema, { translate, codeOf });
2599
+ ```
2600
+
2534
2601
  ## Runtime
2535
2602
 
2536
2603
  ### A test asserting the old Zod string length text fails
@@ -2827,6 +2894,31 @@ await page.getByRole('button', { name: /^(Next|Suivant)$/ }).click();
2827
2894
  <PaginationNext aria-label="Next results" />
2828
2895
  ```
2829
2896
 
2897
+ ### The selection count reads in the wrong order after overriding `material.table.pagination.rows-selected`
2898
+
2899
+ **When:** after upgrading, the data table's footer reads "of 128 3 rows
2900
+ selected" (or the French "sur 128 3 lignes sélectionnées") because your
2901
+ `I18nProvider` overrides `material.table.pagination.rows-selected` with the old
2902
+ text.
2903
+ **Why:** the message now leads with the count and ends with the total, so
2904
+ "3 rows selected of 128" and "No rows selected of 128" read as a sentence, and
2905
+ the plural covers zero. The old form, `{count} of {total} {count, plural, …}`,
2906
+ put the count and the total first. An override keeps its own text, so it keeps
2907
+ the old order. The same release hides "Page n of m" when the table has no rows.
2908
+ **Fix:** adopt the new ICU form in your override, or delete the override:
2909
+
2910
+ ```json
2911
+ {
2912
+ "material": {
2913
+ "table": {
2914
+ "pagination": {
2915
+ "rows-selected": "{count, plural, =0 {No rows selected} one {# row selected} other {# rows selected}} of {total}"
2916
+ }
2917
+ }
2918
+ }
2919
+ }
2920
+ ```
2921
+
2830
2922
  ### A test no longer finds a chip's remove button by the exact name `Remove`, or a `LocationPreview` by its place name
2831
2923
 
2832
2924
  **When:** after an update (0.22.0 and later),
@@ -21,6 +21,7 @@
21
21
  @import "./ui/actions/button-group/button-group.css";
22
22
  @import "./ui/primitives/label/label.css";
23
23
  @import "./ui/feedback/toast/toast.css";
24
+ @import "./print-only.css";
24
25
  @import "./ui/fields/contacts/contact-item.css";
25
26
  @import "./ui/fields/opening-hours/opening-hours-item.css";
26
27
  @import "./ui/fields/attributes/partials/attribute-form.css";
@@ -0,0 +1,44 @@
1
+ /* Printing one element of the page (`printOnly`, from `/lib` or
2
+ * @nxgt/material-core/print-only; RecoveryCodes' Print button): while
3
+ * <html data-nxgt-print> is set, print media shows the element marked
4
+ * data-nxgt-print-target and nothing else. No inline style: only these rules,
5
+ * the same in @nxgt/material and @nxgt/material-vue.
6
+ *
7
+ * Everything that neither is, holds nor sits inside the target is taken out
8
+ * of the flow, so the page prints no blank sheets after it; where :has() is
9
+ * not supported, that rule is dropped and `visibility` alone hides the rest.
10
+ * The target's ancestors stop positioning it, so it starts at the top left of
11
+ * the first page. Unlayered, these rules beat the components' Tailwind
12
+ * utilities (layered) with no `!important`. */
13
+ @media print {
14
+ html[data-nxgt-print]
15
+ body
16
+ *:not(
17
+ [data-nxgt-print-target],
18
+ [data-nxgt-print-target] *,
19
+ :has([data-nxgt-print-target])
20
+ ) {
21
+ display: none;
22
+ }
23
+
24
+ html[data-nxgt-print] body * {
25
+ visibility: hidden;
26
+ }
27
+
28
+ html[data-nxgt-print] :has([data-nxgt-print-target]) {
29
+ position: static;
30
+ transform: none;
31
+ }
32
+
33
+ html[data-nxgt-print] [data-nxgt-print-target],
34
+ html[data-nxgt-print] [data-nxgt-print-target] * {
35
+ visibility: visible;
36
+ }
37
+
38
+ html[data-nxgt-print] [data-nxgt-print-target] {
39
+ position: absolute;
40
+ inset: 0 0 auto 0;
41
+ margin: 0;
42
+ box-shadow: none;
43
+ }
44
+ }
@@ -11,6 +11,8 @@ export const COMPOSABLE_IMPORTS = [
11
11
  'useOptions',
12
12
  'useSearchParam',
13
13
  'useAllowedTransitions',
14
+ 'useFocusOnMount',
15
+ 'useSubmitOnce',
14
16
  'useCspNonce',
15
17
  'resolveTheme',
16
18
  'applyResolvedTheme',
@@ -34,6 +34,7 @@ export const COMPONENT_NAMES = [
34
34
  'AttributesField',
35
35
  'AttributesFormField',
36
36
  'AttributesList',
37
+ 'AuthCard',
37
38
  'Autocomplete',
38
39
  'AutocompleteFormField',
39
40
  'Avatar',
@@ -234,6 +235,7 @@ export const COMPONENT_NAMES = [
234
235
  'EmojiList',
235
236
  'EmojiPicker',
236
237
  'EmptyState',
238
+ 'EnrolmentSecret',
237
239
  'EntityHeader',
238
240
  'EventCalendar',
239
241
  'EventCard',
@@ -266,6 +268,7 @@ export const COMPONENT_NAMES = [
266
268
  'FilterProvider',
267
269
  'FilterSheet',
268
270
  'FilterTrigger',
271
+ 'FormSection',
269
272
  'FunnelCard',
270
273
  'FunnelChart',
271
274
  'Gallery',
@@ -431,6 +434,7 @@ export const COMPONENT_NAMES = [
431
434
  'RatingFormField',
432
435
  'RatioCard',
433
436
  'ReceiptCard',
437
+ 'RecoveryCodes',
434
438
  'RelativeTime',
435
439
  'ResizableHandle',
436
440
  'ResizablePanel',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/material-vue",
3
- "version": "0.34.0",
3
+ "version": "0.36.0",
4
4
  "license": "MIT",
5
5
  "bugs": {
6
6
  "email": "stevegires@gmail.com"
@@ -124,7 +124,7 @@
124
124
  "docs:previews": "vitest run --project previews"
125
125
  },
126
126
  "dependencies": {
127
- "@nxgt/material-core": "^0.9.0",
127
+ "@nxgt/material-core": "^0.10.0",
128
128
  "@floating-ui/dom": "^1.8.0",
129
129
  "@fortawesome/fontawesome-common-types": "7.3.1",
130
130
  "@lucide/vue": "^1.45.0",
@@ -1,138 +0,0 @@
1
- import { t as e } from "./_baseIsEqual-BUD8VQJE.js";
2
- import { n as t } from "./number.utils-Bpo9tUaJ.js";
3
- import { defineComponent as n, getCurrentInstance as r, h as i, inject as a, provide as o, useId as s } from "vue";
4
- import { defineStore as c } from "pinia";
5
- import { createFormatters as l, formatAmount as u, formatDate as d } from "@nxgt/material-core/print-format";
6
- //#region ../../node_modules/lodash-es/isEqual.js
7
- function f(t, n) {
8
- return e(t, n);
9
- }
10
- //#endregion
11
- //#region ../../node_modules/lodash-es/isNil.js
12
- function p(e) {
13
- return e == null;
14
- }
15
- //#endregion
16
- //#region lib/lib/string.utils.ts
17
- function m(e) {
18
- return e.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
19
- }
20
- function h(e) {
21
- return e ? e.toLowerCase().replace(/_/g, "-") : "";
22
- }
23
- function g(e, t = ", ") {
24
- return e.filter((e) => !!e).join(t);
25
- }
26
- function _(e, t) {
27
- let n = t.trim();
28
- if (!n) return [{
29
- text: e,
30
- match: !1
31
- }];
32
- let r = n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), i = new RegExp(r, "ig"), a = [], o = 0;
33
- for (let t of e.matchAll(i)) {
34
- let n = t.index ?? 0;
35
- n > o && a.push({
36
- text: e.slice(o, n),
37
- match: !1
38
- }), a.push({
39
- text: t[0],
40
- match: !0
41
- }), o = n + t[0].length;
42
- }
43
- return o < e.length && a.push({
44
- text: e.slice(o),
45
- match: !1
46
- }), a.length > 0 ? a : [{
47
- text: e,
48
- match: !1
49
- }];
50
- }
51
- var v = {
52
- enumToKebabCase: h,
53
- joinTruthyParts: g,
54
- slugify: m,
55
- splitMatch: _
56
- }, y = 0;
57
- function b(e) {
58
- return r() ? `${e}:${s()}` : (y += 1, `${e}:${y}`);
59
- }
60
- function x(e, t) {
61
- return (n) => c(b(e), {
62
- state: () => ({
63
- ...t.state(),
64
- ...n
65
- }),
66
- actions: { ...t.actions ?? {} },
67
- getters: { ...t.getters ?? {} }
68
- })();
69
- }
70
- function S(e, t) {
71
- return t ? {
72
- ...e,
73
- ...t
74
- } : e;
75
- }
76
- //#endregion
77
- //#region lib/lib/storage.utils.ts
78
- function C(e, t) {
79
- try {
80
- let n = localStorage.getItem(e);
81
- if (n) return typeof t == "string" ? n : JSON.parse(n);
82
- } catch (t) {
83
- console.warn(`Failed to restore state for key "${e}":`, t);
84
- }
85
- return t;
86
- }
87
- function w(e, t) {
88
- try {
89
- localStorage.setItem(e, typeof t == "string" ? t : JSON.stringify(t));
90
- } catch (t) {
91
- console.warn(`Failed to store state for key "${e}":`, t);
92
- }
93
- }
94
- function T(e) {
95
- try {
96
- localStorage.removeItem(e);
97
- } catch (t) {
98
- console.warn(`Failed to remove state for key "${e}":`, t);
99
- }
100
- }
101
- var E = {
102
- safeGetItem: C,
103
- safeSetItem: w,
104
- safeRemoveItem: T
105
- }, D = {
106
- colorFromVariable(e, t = 1) {
107
- return `rgb(from var(--${e}) r g b / ${t})`;
108
- },
109
- cssSize(e) {
110
- if (!(e == null || e === "")) return typeof e == "number" ? `${e}px` : e;
111
- }
112
- };
113
- //#endregion
114
- //#region lib/lib/url.utils.ts
115
- function O(e) {
116
- return new URL(e.url).searchParams;
117
- }
118
- function k(e, t) {
119
- return O(e).get(t);
120
- }
121
- function A(e) {
122
- return /^(https?:)?\/\//.test(e) || e.startsWith("mailto:");
123
- }
124
- //#endregion
125
- //#region lib/lib/client-only.utils.ts
126
- var j = Symbol("inside-client-only");
127
- function M(e) {
128
- return n({
129
- name: "ClientOnly",
130
- inheritAttrs: !1,
131
- setup(n, { attrs: r, slots: s }) {
132
- let c = a(j, !1), l = t();
133
- return o(j, !0), () => !c && !l.value ? null : i(e, { ...r }, s);
134
- }
135
- });
136
- }
137
- //#endregion
138
- export { f as S, h as _, A as a, _ as b, D as c, T as d, w as f, v as g, S as h, M as i, E as l, b as m, u as n, k as o, x as p, d as r, O as s, l as t, C as u, g as v, p as x, m as y };