@nxgt/material-vue 0.35.0 → 0.37.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 (114) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +11 -5
  3. package/dist/{Button-C6GBoxwB.js → Button-CurFF4Zt.js} +1 -1
  4. package/dist/{HoverCardTrigger-BRajrP9V.js → HoverCardTrigger-jCXl6TrX.js} +3 -3
  5. package/dist/{camera-BIK3JMQq.js → camera-DDnR8aYK.js} +4 -4
  6. package/dist/components/components.css +1 -0
  7. package/dist/components/print-only.css +44 -0
  8. package/dist/{components-B3e9Djen.js → components-CCzdGGAe.js} +42239 -41714
  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-DH96Nu9y.js → form-scope-Csf9H-Pg.js} +1 -1
  13. package/dist/{i18n-provider-D0wc5ysD.js → i18n-provider-B2q6vp6H.js} +1 -1
  14. package/dist/i18n.js +4 -4
  15. package/dist/{icon-button-Cep-hCMB.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/count-badge/count-badge.vue.d.ts +1 -1
  27. package/dist/lib/components/ui/display/enrolment-secret/enrolment-secret.vue.d.ts +30 -0
  28. package/dist/lib/components/ui/display/enrolment-secret/index.d.ts +3 -0
  29. package/dist/lib/components/ui/display/index.d.ts +2 -0
  30. package/dist/lib/components/ui/display/recovery-codes/index.d.ts +5 -0
  31. package/dist/lib/components/ui/display/recovery-codes/partials/recovery-codes-actions.vue.d.ts +11 -0
  32. package/dist/lib/components/ui/display/recovery-codes/partials/recovery-codes-confirm.vue.d.ts +15 -0
  33. package/dist/lib/components/ui/display/recovery-codes/partials/recovery-codes-list.vue.d.ts +7 -0
  34. package/dist/lib/components/ui/display/recovery-codes/recovery-codes.vue.d.ts +56 -0
  35. package/dist/lib/components/ui/display/recovery-codes/types.d.ts +23 -0
  36. package/dist/lib/components/ui/display/recovery-codes/use-recovery-codes-labels.d.ts +18 -0
  37. package/dist/lib/components/ui/feedback/alert/alert.vue.d.ts +1 -1
  38. package/dist/lib/components/ui/feedback/circular-progress/circular-progress.vue.d.ts +2 -2
  39. package/dist/lib/components/ui/feedback/spinner/spinner.variants.d.ts +1 -1
  40. package/dist/lib/components/ui/feedback/spinner/spinner.vue.d.ts +1 -1
  41. package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
  42. package/dist/lib/components/ui/fields/form-section/form-section-classes.d.ts +30 -0
  43. package/dist/lib/components/ui/fields/form-section/form-section.vue.d.ts +37 -0
  44. package/dist/lib/components/ui/fields/form-section/index.d.ts +3 -0
  45. package/dist/lib/components/ui/fields/index.d.ts +1 -0
  46. package/dist/lib/components/ui/fields/rating-field/rating-field.variants.d.ts +1 -1
  47. package/dist/lib/components/ui/fields/rating-field/rating-field.vue.d.ts +1 -1
  48. package/dist/lib/components/ui/forms/form-scope-schema.d.ts +8 -2
  49. package/dist/lib/components/ui/forms/index.d.ts +1 -0
  50. package/dist/lib/components/ui/forms/use-code-resolver.d.ts +45 -0
  51. package/dist/lib/components/ui/layout/app-shell/activity-content.vue.d.ts +1 -1
  52. package/dist/lib/components/ui/layout/auth-card/auth-card-notice.vue.d.ts +18 -0
  53. package/dist/lib/components/ui/layout/auth-card/auth-card.vue.d.ts +45 -0
  54. package/dist/lib/components/ui/layout/auth-card/index.d.ts +3 -0
  55. package/dist/lib/components/ui/layout/auth-card/types.d.ts +2 -0
  56. package/dist/lib/components/ui/layout/index.d.ts +1 -0
  57. package/dist/lib/components/ui/selection/autocomplete/autocomplete-items.d.ts +2 -0
  58. package/dist/lib/components/ui/selection/autocomplete/autocomplete-keyboard.d.ts +1 -0
  59. package/dist/lib/components/ui/selection/autocomplete/autocomplete.vue.d.ts +2 -0
  60. package/dist/lib/components/ui/selection/autocomplete/search-types.d.ts +4 -1
  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 +21 -0
  66. package/dist/lib/i18n/resources/fr/fr.d.ts +21 -0
  67. package/dist/lib/i18n/resources/fr/material.json.d.ts +21 -0
  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-Bjz8jMaZ.js → pdf-outline-sidebar-DTb_hLjX.js} +2 -2
  78. package/dist/{pdf-page-BRtEfDpx.js → pdf-page-CCZtaTRB.js} +1 -1
  79. package/dist/{pdf-page-list-DUSFOt6b.js → pdf-page-list-D7ImAK7v.js} +2 -2
  80. package/dist/{pdf-reader-Dhuuu3ud.js → pdf-reader-CsLVHCV6.js} +8 -8
  81. package/dist/{pdf-sidebar-BOsuIcvK.js → pdf-sidebar-DlU0RgZL.js} +3 -3
  82. package/dist/{pdf-thumbnail-sidebar-CEinOvk3.js → pdf-thumbnail-sidebar-CNB7q0Mq.js} +3 -3
  83. package/dist/{pdf-toolbar-ZYITLhtl.js → pdf-toolbar-BtXcyEpd.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-BCawiL69.js → sortable-provider-DMu8DL2z.js} +2 -2
  87. package/dist/{stored-language-CGMzf-kR.js → stored-language-dtksFoGO.js} +42 -0
  88. package/dist/{stx-player-DAhHmRHW.js → stx-player-CqsfQlLp.js} +5 -5
  89. package/dist/{translator-DhrnT0YX.js → translator-DqiGrSH_.js} +1 -1
  90. package/dist/{use-heading-spy-BF4hsZLZ.js → use-heading-spy-BZ7ctGZ5.js} +58 -48
  91. package/dist/{use-material-translation-D30V5LyT.js → use-material-translation-DRB6oPdF.js} +3 -3
  92. package/docs/components/README.md +3 -3
  93. package/docs/components/data-display.md +112 -0
  94. package/docs/components/feedback-and-status.md +2 -0
  95. package/docs/components/form-fields.md +81 -0
  96. package/docs/components/navigation-and-layout.md +64 -0
  97. package/docs/components/overlays-and-menus.md +18 -0
  98. package/docs/components/selection-and-choice.md +12 -4
  99. package/docs/guides/forms.md +120 -0
  100. package/docs/guides/helpers.md +85 -0
  101. package/docs/guides/i18n.md +17 -0
  102. package/docs/guides/printing.md +27 -0
  103. package/docs/previews/data-display-enrolmentsecret.webp +0 -0
  104. package/docs/previews/data-display-recoverycodes.webp +0 -0
  105. package/docs/previews/form-fields-formsection.webp +0 -0
  106. package/docs/previews/navigation-layout-authcard.webp +0 -0
  107. package/docs/roadmap.md +15 -0
  108. package/docs/troubleshooting.md +131 -0
  109. package/lib/components/components.css +1 -0
  110. package/lib/components/print-only.css +44 -0
  111. package/lib/nuxt/auto-imports.ts +2 -0
  112. package/lib/nuxt/component-names.ts +4 -0
  113. package/package.json +2 -2
  114. 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
@@ -162,6 +162,21 @@ version they shipped in, and planned entries may name the major they target.
162
162
 
163
163
  ## Shipped
164
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.
165
180
  - **A strict Content Security Policy** — a page that sends `script-src` with a
166
181
  nonce and no `'unsafe-eval'`, and `style-src-elem` with a nonce and no
167
182
  `'unsafe-inline'`, runs every component with no violation report:
@@ -26,6 +26,7 @@ words in your error.
26
26
  - [Dialogs, toasts or buttons no longer animate](#dialogs-toasts-or-buttons-no-longer-animate)
27
27
  - [A Content-Security-Policy reports a `data:` image on every form with a required field](#a-content-security-policy-reports-a-data-image-on-every-form-with-a-required-field)
28
28
  - [A Button's label overflows a card on a phone](#a-buttons-label-overflows-a-card-on-a-phone)
29
+ - [A dialog or sheet scrolls, or clips a child, after upgrading](#a-dialog-or-sheet-scrolls-or-clips-a-child-after-upgrading)
29
30
  - [`JsBarcode is not a function`](#jsbarcode-is-not-a-function)
30
31
  - [A `LinkButton` selected as a `button` stops matching](#a-linkbutton-selected-as-a-button-stops-matching)
31
32
  - [A `Toc`, `Pager`, `SeeAlso` or `ScrollProgress` inside `Doc` selected by `data-slot` stops matching](#a-toc-pager-seealso-or-scrollprogress-inside-doc-selected-by-data-slot-stops-matching)
@@ -40,6 +41,8 @@ words in your error.
40
41
  - [An `Autocomplete` lists more options than before, or "ecole" finds "École"](#an-autocomplete-lists-more-options-than-before-or-ecole-finds-école)
41
42
  - [`@query-change` runs more often after upgrading](#query-change-runs-more-often-after-upgrading)
42
43
  - [An older search's results replace the newer ones in an `Autocomplete`](#an-older-searchs-results-replace-the-newer-ones-in-an-autocomplete)
44
+ - [An `Autocomplete` option does nothing on click or Enter](#an-autocomplete-option-does-nothing-on-click-or-enter)
45
+ - ["The search failed. Try again." no longer shows in an `Autocomplete`](#the-search-failed-try-again-no-longer-shows-in-an-autocomplete)
43
46
  - [A test no longer finds `aria-invalid` on an `OtpField` slot](#a-test-no-longer-finds-aria-invalid-on-an-otpfield-slot)
44
47
  - [A test no longer finds the breadcrumb's home button](#a-test-no-longer-finds-the-breadcrumbs-home-button)
45
48
  - [A test no longer finds a sidebar entry by the `button` role](#a-test-no-longer-finds-a-sidebar-entry-by-the-button-role)
@@ -102,6 +105,7 @@ words in your error.
102
105
 
103
106
  **Printing**
104
107
 
108
+ - [`RecoveryCodes`' Print button prints the whole page](#recoverycodes-print-button-prints-the-whole-page)
105
109
  - [The table header and the total row print blank](#the-table-header-and-the-total-row-print-blank)
106
110
  - [A `tw()` class has no effect](#a-tw-class-has-no-effect)
107
111
  - [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 +144,8 @@ words in your error.
140
144
  - [A test asserting the old Zod string length text fails](#a-test-asserting-the-old-zod-string-length-text-fails)
141
145
  - [A calendar's weekday row is in English on a French page](#a-calendars-weekday-row-is-in-english-on-a-french-page)
142
146
  - [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)
147
+ - [Errors keep their old words after a language switch](#errors-keep-their-old-words-after-a-language-switch)
148
+ - [`useCodeResolver` re-validates or resets the form on every render](#usecoderesolver-re-validates-or-resets-the-form-on-every-render)
143
149
 
144
150
  **Runtime**
145
151
 
@@ -658,6 +664,47 @@ an older query, even from an API that ignores the signal:
658
664
  />
659
665
  ```
660
666
 
667
+ ### An `Autocomplete` option does nothing on click or Enter
668
+
669
+ **When:** one option of an `Autocomplete` is greyed out and clicking it, or
670
+ pressing Enter on it, changes nothing, with no error.
671
+ **Why:** that option has `disabled: true` (in the local `options` or in what
672
+ `on-search` returns). It is listed and announced with `aria-disabled`, and never
673
+ picked, in `single` and `multiple` mode; Enter picks the first enabled option, and Tab skips a disabled one.
674
+ **Fix:** drop `disabled` from the option, or build it only for the options that
675
+ may be chosen:
676
+
677
+ ```vue
678
+ <Autocomplete
679
+ v-model="value"
680
+ :options="[
681
+ { value: 'a', label: 'Alpha', disabled: true }, // listed, never picked
682
+ { value: 'b', label: 'Beta' },
683
+ ]"
684
+ />
685
+ ```
686
+
687
+ ### "The search failed. Try again." no longer shows in an `Autocomplete`
688
+
689
+ **When:** after upgrading, an `Autocomplete` whose `on-search` rejects shows
690
+ your own text, or nothing generic, where it used to say "The search failed. Try
691
+ again."
692
+ **Why:** `search-error` now takes precedence over the generic text with `on-search`
693
+ too, in the list and in the live announcement. The generic text only shows
694
+ while `search-error` is unset.
695
+ **Fix:** nothing, if the reason is what you want. To get the generic text back,
696
+ leave `search-error` unset or empty; to show a reason, keep it from `@search-error` and pass it
697
+ back:
698
+
699
+ ```vue
700
+ <Autocomplete
701
+ v-model="value"
702
+ :on-search="search"
703
+ :search-error="reason"
704
+ @search-error="reason = 'Directory unreachable'"
705
+ />
706
+ ```
707
+
661
708
  ### A test no longer finds `aria-invalid` on an `OtpField` slot
662
709
 
663
710
  **When:** `[data-slot="input-otp-slot"][aria-invalid]` (or
@@ -1179,6 +1226,27 @@ your own through `class`. With `as-child`, put the label in a `<span>`:
1179
1226
  </template>
1180
1227
  ```
1181
1228
 
1229
+ ### A dialog or sheet scrolls, or clips a child, after upgrading
1230
+
1231
+ **When:** after upgrading, a tall `DialogContent`, `AlertDialogContent` or
1232
+ `SheetContent` shows a scrollbar, or a child that stuck out of it (a popover, a
1233
+ focus ring, a wide table) is clipped.
1234
+ **Why:** these contents are now capped to the viewport (`max-h-[calc(100dvh-2rem)]`,
1235
+ `100vh` where `dvh` is unsupported; a top or bottom sheet is capped at `100dvh`)
1236
+ and carry `overflow-y-auto`, so a long body scrolls inside them instead of
1237
+ leaving the title off-screen. Setting `overflow-y` makes `overflow-x` clip or
1238
+ scroll too.
1239
+ **Fix:** to keep the old behaviour on one dialog, remove the cap and the scroll
1240
+ with `class="max-h-none overflow-visible"`; to change the cap, pass your own
1241
+ `max-h-*` (it wins on browsers with `dvh` support):
1242
+
1243
+ ```vue
1244
+ <DialogContent class="max-h-none overflow-visible">
1245
+ ```
1246
+
1247
+ For a dialog that should grow taller than the screen and scroll inside the
1248
+ overlay instead of capping, use `DialogScrollContent`.
1249
+
1182
1250
  ## Feedback
1183
1251
 
1184
1252
  ### A toast with an action never goes away
@@ -1960,6 +2028,30 @@ const field = useTemplateRef<ComponentExposed<typeof Autocomplete>>('field');
1960
2028
 
1961
2029
  ## Printing
1962
2030
 
2031
+ ### `RecoveryCodes`' Print button prints the whole page
2032
+
2033
+ **When:** "Print" on `RecoveryCodes` prints the rest of the page with the
2034
+ codes, or the codes away from the top of the sheet.
2035
+ **Why:** the button only marks the page (`<html data-nxgt-print>`) and the
2036
+ card (`data-nxgt-print-target`); hiding the rest is a `@media print` rule in
2037
+ the package's stylesheet (`print-only.css`). A page that does not load
2038
+ `@nxgt/material-vue/styles.css` (or
2039
+ `dist/material-vue.css`) has no such rule, and an app's own
2040
+ print rule that is `!important` (or a `position` on an ancestor set with
2041
+ `!important`) wins over it.
2042
+ **Fix:** load the stylesheet the setup guide names, and keep your print
2043
+ rules off the marked page:
2044
+
2045
+ ```css
2046
+ /* your app's print rules, everywhere but while one element is printed */
2047
+ @media print {
2048
+ html:not([data-nxgt-print]) .page-header { position: static !important; }
2049
+ }
2050
+ ```
2051
+
2052
+ To print an element of your own the same way, call `printOnly(element)`
2053
+ from `@nxgt/material-vue/lib`; the same rules apply to it.
2054
+
1963
2055
  ### The table header and the total row print blank
1964
2056
 
1965
2057
  **When:** on paper or in a PDF, the table header and the grand-total row are
@@ -2532,6 +2624,45 @@ reka hides the weekday row from assistive tech (`<thead aria-hidden>`); in a
2532
2624
  test, find a day by its full date instead
2533
2625
  (`getByRole('gridcell', { name: 'jeudi 1 octobre 2026' })`).
2534
2626
 
2627
+ ### Errors keep their old words after a language switch
2628
+
2629
+ **When:** a form validated with `useCodeResolver` shows an error, the user
2630
+ switches the language, and the error stays in the old one (a code's words
2631
+ or a zod message alike).
2632
+ **Why:** the typed schema is a computed that follows the language, and
2633
+ vee-validate re-validates when its `validationSchema` ref changes — but only
2634
+ when it receives the ref. Passing `validationSchema.value` hands it a plain
2635
+ object, which keeps the language of the first validation.
2636
+ **Fix:** pass the computed itself:
2637
+
2638
+ ```ts
2639
+ const validationSchema = useCodeResolver(schema, { translate });
2640
+ const { handleSubmit } = useForm({ validationSchema }); // not validationSchema.value
2641
+ ```
2642
+
2643
+ Storybook: `Guides/Forms/useCodeResolver`. See
2644
+ [forms](guides/forms.md#wording-errors-by-code).
2645
+
2646
+ ### `useCodeResolver` re-validates or resets the form on every render
2647
+
2648
+ **When:** the schema, `translate` or `codeOf` is created inside a computed
2649
+ or a render function, so `useCodeResolver` is called again with a new one.
2650
+ **Why:** each call makes a new typed schema; vee-validate re-validates every
2651
+ time `validationSchema` changes.
2652
+ **Fix:** call `useCodeResolver` once in `setup`, with a schema, a translator
2653
+ and a `codeOf` defined at module scope (a `language` ref or getter is what
2654
+ should change):
2655
+
2656
+ ```ts
2657
+ import { type CodeIssue, defaultCodeOf, isEmptyRequired, useCodeResolver } from '@nxgt/material-vue/components';
2658
+
2659
+ const schema = z.object({ username: z.string().min(3) });
2660
+ const codeOf = (issue: CodeIssue) =>
2661
+ isEmptyRequired(issue) ? 'errors.required' : defaultCodeOf(issue);
2662
+
2663
+ const validationSchema = useCodeResolver(schema, { translate, codeOf });
2664
+ ```
2665
+
2535
2666
  ## Runtime
2536
2667
 
2537
2668
  ### A test asserting the old Zod string length text fails
@@ -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.35.0",
3
+ "version": "0.37.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.1",
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 };