@nxgt/material 2.6.1 → 2.7.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 (73) hide show
  1. package/CHANGELOG.md +1781 -0
  2. package/README.md +25 -3
  3. package/dist/{_Uint8Array-CDlDeQmw.js → _Uint8Array-JRUmWcRr.js} +1 -1
  4. package/dist/{_overRest-CWIWQwDJ.js → _overRest-DvC5rm5s.js} +1 -1
  5. package/dist/{async-slice-Qoen0Mqm.js → async-slice-OftEOtMA.js} +2 -2
  6. package/dist/{blockDiagram-BEXU5L5S-_6EVgQ2-.js → blockDiagram-BEXU5L5S-BM-gkWAO.js} +2 -2
  7. package/dist/{chunk-3FUC2YCW-CRQHl-Jx.js → chunk-3FUC2YCW-Cf18L00V.js} +2 -2
  8. package/dist/{chunk-7M6MHVWA-DqXpf6CM.js → chunk-7M6MHVWA-C3x6E3yT.js} +1 -1
  9. package/dist/{chunk-GNY47TPC-BgMsLbCS.js → chunk-GNY47TPC-D1E6DHDV.js} +4 -4
  10. package/dist/{chunk-NETBCI7D-CsyH43bj.js → chunk-NETBCI7D-DJUuN4EY.js} +1 -1
  11. package/dist/{chunk-Z7XXMR3K-CoCjTVVx.js → chunk-Z7XXMR3K-DMTBxZxM.js} +1 -1
  12. package/dist/{classDiagram-v2-NBCMYWYE-j4vk04t-.js → classDiagram-v2-NBCMYWYE-BDaxYvDy.js} +2 -2
  13. package/dist/{components-C3wIUrWw.js → components-DrStgM4J.js} +23243 -23085
  14. package/dist/components.js +5 -5
  15. package/dist/{dagre-6A5THRUB-DXq_YBNL.js → dagre-6A5THRUB-DZF0nICj.js} +11 -11
  16. package/dist/{diagram-22UHCM2B-He8NDldw.js → diagram-22UHCM2B-CVZYpeev.js} +2 -2
  17. package/dist/{diagram-3UASUU5V-CeILva4U.js → diagram-3UASUU5V-D7MmBJbM.js} +1 -1
  18. package/dist/dnd.js +1 -1
  19. package/dist/{elk-276RUBZZ-DFn4kTey.js → elk-276RUBZZ-DI4eicFj.js} +3 -3
  20. package/dist/{erDiagram-OPXOYQCR-BzVUcUwh.js → erDiagram-OPXOYQCR-CUbpVns3.js} +2 -2
  21. package/dist/{field-blur-BlrE9fOD.js → field-blur-DWQtxiAe.js} +1 -1
  22. package/dist/{flowDiagram-KWPJA3E3-B4pJCj2Q.js → flowDiagram-KWPJA3E3-DZxumFfu.js} +3 -3
  23. package/dist/{ganttDiagram-FUAMR5RP-B4A4UFFi.js → ganttDiagram-FUAMR5RP-D3vmspam.js} +1 -1
  24. package/dist/{graphlib-CQ3hLS1N.js → graphlib-DLJn7qS3.js} +6 -6
  25. package/dist/hooks.js +2 -2
  26. package/dist/{i18n-provider-CbYD2KxP.js → i18n-provider-DBFRz5uH.js} +1 -1
  27. package/dist/i18n.js +6 -6
  28. package/dist/{isArrayLikeObject-5rnYwEK7.js → isArrayLikeObject-CO5xeuaJ.js} +3 -3
  29. package/dist/{isEmpty-DChvniYe.js → isEmpty-DOC38Ces.js} +3 -3
  30. package/dist/{journeyDiagram-ZHPQQLJL-C91SPPkY.js → journeyDiagram-ZHPQQLJL-Dxvl3oeN.js} +1 -1
  31. package/dist/lib/components/ui/collections/data-table/data-table-server-footer.d.ts +17 -0
  32. package/dist/lib/components/ui/collections/data-table/data-table.d.ts +4 -30
  33. package/dist/lib/components/ui/collections/data-table/index.d.ts +1 -0
  34. package/dist/lib/components/ui/collections/data-table/partials/data-table-body.d.ts +5 -6
  35. package/dist/lib/components/ui/collections/data-table/partials/data-table-link.d.ts +4 -0
  36. package/dist/lib/components/ui/collections/data-table/partials/data-table-page-control.d.ts +13 -0
  37. package/dist/lib/components/ui/collections/data-table/partials/data-table-row.d.ts +16 -0
  38. package/dist/lib/components/ui/collections/data-table/types.d.ts +87 -0
  39. package/dist/lib/components/ui/collections/data-table/use-data-table.d.ts +16 -3
  40. package/dist/lib/i18n/resources/en/en.d.ts +5 -0
  41. package/dist/lib/i18n/resources/en/material.json.d.ts +6 -1
  42. package/dist/lib/i18n/resources/fr/fr.d.ts +5 -0
  43. package/dist/lib/i18n/resources/fr/material.json.d.ts +6 -1
  44. package/dist/lib/i18n/resources/resources.d.ts +10 -0
  45. package/dist/lib/i18n/translator.d.ts +1 -1
  46. package/dist/lib.js +3 -3
  47. package/dist/main.js +15 -15
  48. package/dist/map.js +2 -2
  49. package/dist/material.css +1 -1
  50. package/dist/{merge-_Awk4ywA.js → merge-CuDTNgpm.js} +4 -4
  51. package/dist/{mermaid.core-DciYB1MV.js → mermaid.core-O7mtb1-p.js} +21 -21
  52. package/dist/{mindmap-definition-NLK3R4M7-Brf_2jIE.js → mindmap-definition-NLK3R4M7-CO0E97EZ.js} +2 -2
  53. package/dist/{pieDiagram-5QR66LMP-MPh-EWf-.js → pieDiagram-5QR66LMP-BXxVfv5K.js} +1 -1
  54. package/dist/print.js +2 -2
  55. package/dist/{quadrantDiagram-O4NWA36T-CwDeE9KB.js → quadrantDiagram-O4NWA36T-BbidZDM8.js} +1 -1
  56. package/dist/{requirementDiagram-PLB6GJNP-CCP3YtvB.js → requirementDiagram-PLB6GJNP-wFvgSWJP.js} +2 -2
  57. package/dist/{sankeyDiagram-IPEJSGJF-vgVchd2a.js → sankeyDiagram-IPEJSGJF-Cka5t09q.js} +1 -1
  58. package/dist/{sortable-provider-CzSnl6It.js → sortable-provider-C5VYhY8u.js} +1 -1
  59. package/dist/{stateDiagram-v2-GCMORJYK-T-oIXh-8.js → stateDiagram-v2-GCMORJYK-EMa7q3ju.js} +2 -2
  60. package/dist/{stored-language-DmUUhRb5.js → stored-language-B0o4cBh8.js} +12 -2
  61. package/dist/{swimlanes-2SLR337P-tW3_wBWO.js → swimlanes-2SLR337P-BtZ7Di5u.js} +3 -3
  62. package/dist/{swimlanesDiagram-TC7HE7FX-B1lWzHp1.js → swimlanesDiagram-TC7HE7FX-CROh8GTF.js} +3 -3
  63. package/dist/{timeline-definition-EJHVYXUP-SdA6wt2-.js → timeline-definition-EJHVYXUP-2Z6UABw8.js} +1 -1
  64. package/dist/{translate-material-DuY12xca.js → translate-material-BCwQrt2G.js} +2 -2
  65. package/dist/{translator-CaIYjyFP.js → translator-CwpmpI24.js} +1 -1
  66. package/dist/{use-heading-spy-Crdz4OH5.js → use-heading-spy-BtBJiU63.js} +2 -2
  67. package/dist/{use-material-translation-Od0QsFPA.js → use-material-translation-DG_7z3Q7.js} +2 -2
  68. package/dist/{usecaseDiagram-POWQR4AR-B3rBcR4E.js → usecaseDiagram-POWQR4AR-DT56cp_B.js} +2 -2
  69. package/dist/{xychartDiagram-PMCCYNJV-stQWLgCx.js → xychartDiagram-PMCCYNJV-B2aZlrno.js} +1 -1
  70. package/docs/components/data-table-filter-and-lists.md +160 -5
  71. package/docs/getting-started.md +3 -2
  72. package/docs/roadmap.md +6 -0
  73. package/package.json +3 -2
package/CHANGELOG.md ADDED
@@ -0,0 +1,1781 @@
1
+ # @nxgt/material
2
+
3
+ ## 2.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#431](https://github.com/softistx/nxgt-material/pull/431) [`bd58a6f`](https://github.com/softistx/nxgt-material/commit/bd58a6f67bdc25d4b4d0d7d88c52784869ad1529) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `DataTable` renders server-side data: one page from a loader, with the query in the URL. Everything is opt-in; a table that sets none of the new props renders and behaves as before.
8
+
9
+ - `manual`: `data` is one server page, rendered as given — no client paging, sorting or filtering — and the footer becomes `DataTableServerFooter` (exported).
10
+ - `pagination={{ totalCount, offset, previous, next }}`: the server footer's range ("41–60 of 133", `material.table.pagination.range`) and Previous / Next, each an href (a real link, `rel="prev"` / `rel="next"`, that works with no JavaScript) or a callback (a button); disabled when absent and while `loading`.
11
+ - Controlled sorting: `sorting` + `onSortingChange(sorting)` in React, `v-model:sorting` in Vue. The headers show the sorting they are given; the callback receives the new `SortingState`, not an updater.
12
+ - `getRowId(row, index)` and controlled selection by id: `rowSelection` + `onRowSelectionChange(selection)` in React, `v-model:row-selection` in Vue. Ids of other pages are kept, and the server footer counts them all.
13
+ - `getRowHref(row)`: the first data cell becomes a link to the row's page, and a click anywhere else on the row (but on a control, in a portal, or at the end of a text selection) follows it. `onRowClick(row, event)` (`@row-click` in Vue) makes the row focusable and activatable with Enter.
14
+ - `linkComponent`: the link rendered for row and page links, given `href` (wrap React Router's `Link` or vue-router's `RouterLink`, which take `to`). Default: a plain `<a>`.
15
+ - `footer`: a render function (React, Vue) or the `#footer` slot (Vue) replaces the footer; `footer={false}` removes it.
16
+ - New `material.table.pagination.*` strings, in English and French: `label`, `range`, `range-open`, `total`, `selected`.
17
+
18
+ `@nxgt/material-core` gains `/row-activation` (`isRowActivation`, `followRowLink`, `ROW_CONTROL_SELECTOR`, `ROW_LINK_SELECTOR`, `RowClick`): the clickable-row rule both libraries' `DataTable` use.
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies [[`bd58a6f`](https://github.com/softistx/nxgt-material/commit/bd58a6f67bdc25d4b4d0d7d88c52784869ad1529)]:
23
+ - @nxgt/material-core@0.4.0
24
+
25
+ ## 2.6.2
26
+
27
+ ### Patch Changes
28
+
29
+ - [#429](https://github.com/softistx/nxgt-material/pull/429) [`89c1e07`](https://github.com/softistx/nxgt-material/commit/89c1e078eec9ab7368882b6a90f53ebe9ed7951b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Docs: the README has a "Using with Claude Code" section. The
30
+ `softistx/nxgt-material` repository is now a Claude Code plugin marketplace
31
+ for apps that use the libraries — `nxgt-material` for React apps,
32
+ `nxgt-material-vue` for Vue and Nuxt apps — each with a skill that reads the
33
+ installed package's docs, a screen-builder agent, an upgrade-assistant agent
34
+ and an advisory hook that names the component a file hand-rolls. Install
35
+ with `/plugin marketplace add softistx/nxgt-material`, then
36
+ `/plugin install nxgt-material@nxgt-material` (or
37
+ `nxgt-material-vue@nxgt-material`); the repository is private, so this needs
38
+ GitHub access. Nothing in the package itself changed.
39
+
40
+ - [#429](https://github.com/softistx/nxgt-material/pull/429) [`13c3eda`](https://github.com/softistx/nxgt-material/commit/13c3edad01f08656ca196aca7c37a21e8c003466) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The package ships its `CHANGELOG.md` (now in `files`), so the notes of every
41
+ version are in `node_modules/<package>/CHANGELOG.md` next to the docs, for
42
+ reading offline and for the upgrade agents of the Claude Code plugins.
43
+ The getting-started pages of `@nxgt/material` and `@nxgt/material-vue`
44
+ list it among the tarball's contents.
45
+ - Updated dependencies [[`13c3eda`](https://github.com/softistx/nxgt-material/commit/13c3edad01f08656ca196aca7c37a21e8c003466)]:
46
+ - @nxgt/material-core@0.3.2
47
+
48
+ ## 2.6.1
49
+
50
+ ### Patch Changes
51
+
52
+ - [#427](https://github.com/softistx/nxgt-material/pull/427) [`d4e953f`](https://github.com/softistx/nxgt-material/commit/d4e953fd4fc147b366c5d9d70ebfb1aa3a3c0ba7) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Docs: the roadmap lists what 2.6.0 shipped (`TreeSelectField`, `DateRangeField` presets and time, `FilePreview`) under Shipped. Troubleshooting has two new entries, for what a test may now see: `TimePicker`'s AM / PM moves the time into that half once (it could add 12 hours twice) and a click on the pressed half changes nothing; a `Tree` no longer moves focus into itself when it renders. The `FilePreview` entry quotes the HTTP error `onError` receives, `Could not read …: HTTP 404`.
53
+ - Updated dependencies [[`d4e953f`](https://github.com/softistx/nxgt-material/commit/d4e953fd4fc147b366c5d9d70ebfb1aa3a3c0ba7)]:
54
+ - @nxgt/material-core@0.3.1
55
+
56
+ ## 2.6.0
57
+
58
+ ### Minor Changes
59
+
60
+ - [#424](https://github.com/softistx/nxgt-material/pull/424) [`d90a68b`](https://github.com/softistx/nxgt-material/commit/d90a68b8f16759b44929effc426b6c3964e92a4c) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `DateRangeField` (and `DateRangeFormField`) take `presets` and `withTime`.
61
+ `presets` lists one-click ranges beside the calendar: built-in ids
62
+ (`DATE_RANGE_PRESET_IDS`: `today`, `yesterday`, `last-7-days`,
63
+ `last-30-days`, `this-month`, `last-month`, `this-year`) and
64
+ `{ label, range }` of your own, where `range` may be `(now) => range`. A
65
+ preset is a toggle button in a group named "Presets", pressed when its days
66
+ are the value's, and disabled when its range starts before `minDate`, ends
67
+ after `maxDate`, either end is one of `disabledDays`, or (a preset of your
68
+ own) its end comes before its start. Picking one closes the popover, except
69
+ with `withTime`, where OK closes it. `withTime` adds a start and an end `TimePicker` under the
70
+ calendar, each disabled until its end has a day, and an OK button; the
71
+ popover then stays open until OK, the trigger shows each end's time, a day picked on the calendar keeps the
72
+ time its end had (a new start 00:00, a new end 23:59, as the built-in
73
+ presets end, so the range covers its last day), and an end set before the
74
+ start on the same day becomes the start time. The built-in presets use the
75
+ current date (there is no `now` prop).
76
+ `DATE_RANGE_PRESET_IDS` and the types `DateRangePreset`, `DateRangePresetId`,
77
+ `DateRangePresetOption` and `DayRange` are exported from
78
+ `@nxgt/material/components` (from `@nxgt/material-core/date-range`).
79
+
80
+ New `material.*` keys: `material.date-range-field.presets`,
81
+ `material.date-range-field.preset.today`, `.yesterday`, `.last-7-days`,
82
+ `.last-30-days`, `.this-month`, `.last-month`, `.this-year`,
83
+ `material.date-range-field.start-time`, `material.date-range-field.end-time`,
84
+ `material.time-picker.hours` and `material.time-picker.minutes`.
85
+
86
+ `TimePicker` fix: AM / PM now follows the `date` it is given, so a date
87
+ changed from outside shows its own half of the day; switching AM / PM always
88
+ moves the time into that half (it could add 12 hours twice before), and a
89
+ click on the pressed half changes nothing. Its hours and minutes inputs are
90
+ named "Hours" and "Minutes".
91
+
92
+ - [#426](https://github.com/softistx/nxgt-material/pull/426) [`812c101`](https://github.com/softistx/nxgt-material/commit/812c10115278480852852084ddf06b016239e6d4) Thanks [@SteveGT96](https://github.com/SteveGT96)! - New `FilePreview` (Media & files): one file previewed by its MIME type, else
93
+ its extension. `file` is a `File`, a `Blob` or a URL, with optional `type`
94
+ and `name` overriding what the file says. An image is an `<img>` whose alt
95
+ text is the file name; a PDF opens in `PdfReader`, imported the first time a
96
+ PDF is previewed, and falls back to the file card when the viewer cannot be
97
+ loaded; a video or audio file gets the browser's `<video controls>` /
98
+ `<audio controls>`; a text, JSON, CSV or Markdown file is read up to
99
+ `maxTextBytes` (256 KB by default) and shown in a scrolling `<pre>`, JSON in
100
+ `JsonViewer` and Markdown rendered, with "Showing the first … of this file."
101
+ when it was cut; anything else is a `FileCard` with its size and type and a
102
+ Download link. A text is decoded with the `charset` of the response's
103
+ `Content-Type` when the browser supports it, else as UTF-8. A URL must be
104
+ `http(s):`, relative, `blob:`, or a `data:` URL of an image, video, audio, plain text, CSV, Markdown, JSON or PDF: any
105
+ other (`javascript:`, `data:text/html`, and also a `data:` URL of XML, TSV or
106
+ YAML) is never linked, loaded or fetched, and shows the error state with no
107
+ Download link and a `FilePreviewUnsafeUrlError` ("Refused to preview a URL with an unsafe scheme") for `onError`. A PDF the reader fails to open stays `ready`: `PdfReader` shows
108
+ its own error. The scrolling text and Markdown are regions named "Contents
109
+ of {name}", and "Loading preview…" is announced from a live region that
110
+ stays mounted. A `File` or `Blob` is shown through an object URL, revoked on
111
+ change and on unmount. On the server, and in the first client render, it
112
+ renders an empty placeholder (`data-state="pending"`): no object URL, no
113
+ fetch, nothing to mismatch on hydration. Then `loading`, `ready` or `error`
114
+ ("This file could not be previewed.", with a Download link), and `onError`
115
+ receives the error. Also exported from `@nxgt/material/components`:
116
+ `fileKind`, `FilePreviewHttpError`, `FilePreviewUnsafeUrlError`,
117
+ `TEXT_PREVIEW_MAX_BYTES` and the types
118
+ `FileKind`, `FilePreviewSource`, `FilePreviewProps` and `FilePreviewStatus`
119
+ (from `@nxgt/material-core/file-preview`).
120
+
121
+ New `material.*` keys: `material.file-preview.label`,
122
+ `material.file-preview.untitled`, `material.file-preview.loading`,
123
+ `material.file-preview.error`, `material.file-preview.download`,
124
+ `material.file-preview.truncated` and `material.file-preview.content`.
125
+
126
+ `useImagePreview` (the upload field's, image field's, file list's and chat
127
+ chip's image thumbnail) no longer hands out a revoked URL for the render
128
+ between a file change and its effect: neither the previous file's, nor, when
129
+ a file is cleared and the same one picked again, that file's old one.
130
+
131
+ - [#418](https://github.com/softistx/nxgt-material/pull/418) [`01d96ca`](https://github.com/softistx/nxgt-material/commit/01d96ca739bb9565c841c7944294df24eb378e07) Thanks [@SteveGT96](https://github.com/SteveGT96)! - **`TreeSelectField`** and **`TreeSelectFormField`**, in both libraries under the same names: a form field that opens the `Tree` in a popover.
132
+
133
+ - `items` takes `Tree`'s nodes. The value is the picked node's id (`string | null`), or the ids with `multiple` (`string[]`): `value` / `onValueChange` in `@nxgt/material`, `v-model` in `@nxgt/material-vue`. The field is controlled only: it holds no value of its own.
134
+ - Single: a pick closes the popover and the trigger shows the node's label. `multiple`: checkboxes in the tree, and each pick is a dismissible `Chip` beside the trigger, its remove button named "Remove {label}".
135
+ - `searchable` adds a filter above the tree; each match stays under its ancestors, which open.
136
+ - Removing a chip hands focus to the next chip, else the previous one, else the trigger, so a keyboard user is not dropped to the page (nor the form field touched).
137
+ - The trigger is a `combobox` named by the label, with `aria-expanded` and `aria-controls`, and the popover is named by the label too; the tree keeps its own keys, and Escape closes the popover and returns focus to the trigger. `id`, `aria-describedby` and `data-*` attributes go on the trigger. It renders on the server.
138
+ - `TreeSelectFormField` binds it to react-hook-form (`control`, `name`, `multiple`) or vee-validate (`name`, `multiple`), touched when focus leaves the trigger and its popover.
139
+ - New copy under `material.tree-select-field.*` (`placeholder`, `search`, `empty`, `selected`), in English and French.
140
+
141
+ **`Tree`**: when its nodes change (a filter) or a branch folds and the row that had the keyboard is no longer shown, the first visible row takes `tabindex="0"`, so Tab still reaches the tree. `useTree` returns that row as `tabbableId`, in both libraries. In `@nxgt/material`, mounting a `Tree` no longer moves focus into it: focus follows the keyboard only, as in `@nxgt/material-vue`.
142
+
143
+ ### Patch Changes
144
+
145
+ - [#425](https://github.com/softistx/nxgt-material/pull/425) [`8092210`](https://github.com/softistx/nxgt-material/commit/80922105439dcac15b9564864c8945e5edd64aa8) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `DateRangeField` docs: `onValueChange` after the first click, and the emitted
146
+ range, now say what react-day-picker 10 does — the first day picked is both
147
+ `from` and `to` until the second. The `WithTime` story picks two days and
148
+ checks they start at 12:00 AM and end at 11:59 PM.
149
+
150
+ - [#419](https://github.com/softistx/nxgt-material/pull/419) [`02efc3e`](https://github.com/softistx/nxgt-material/commit/02efc3e57a60b533596fd644ebbfc856186664f8) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Docs: the i18n guide names the pagination keys `material.pagination.previous`,
151
+ `next`, `page` and `more` (`@nxgt/material`); `InlineEdit` says where focus
152
+ goes when the editor closes, and its Vue props table lists `label` and
153
+ `name`; the Vue `DocLink` paragraph says that without `@navigate` it and the
154
+ sidebar are plain anchors that load the page.
155
+ The Vue i18n guide also names `material.slider.value`, `minimum` and
156
+ `maximum`, which label the slider readout.
157
+
158
+ - [#419](https://github.com/softistx/nxgt-material/pull/419) [`99878c9`](https://github.com/softistx/nxgt-material/commit/99878c9bdb0d4c33fd6a777970c4f3735bd5ef3b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader`: opening on an `initialPage` no longer reports another page on a
159
+ slow device. The placement scroll could land after the scroll spy's hold had
160
+ run out, and near the end of the document it read as the last page. While
161
+ the view stays where the opening page's pin put it (that page at the top,
162
+ or the page area at its end with that page not above the top), a scroll is the reader's own and is not
163
+ reported; a scroll that moves the view elsewhere — the user's, find in page,
164
+ a screen reader's cursor — lets go of the pin and is reported. A wheel,
165
+ touch, pointer or key in the page area also ends the hold on the reader's
166
+ own scroll, so the scrolling after it is reported at once. In
167
+ `@nxgt/material-core/pdf-viewer`, `pinToPage` takes an optional `onRelease`,
168
+ called once when the pin lets go, and `pinHolds(node)` tells whether the view
169
+ is still where the pin puts it.
170
+
171
+ - [#421](https://github.com/softistx/nxgt-material/pull/421) [`0cf0b4f`](https://github.com/softistx/nxgt-material/commit/0cf0b4f3d5fd5e7f9ff33b5c8ba80174d22f563b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The roadmap lists what 2.5.0 shipped under Shipped.
172
+
173
+ - [#419](https://github.com/softistx/nxgt-material/pull/419) [`2a41ae1`](https://github.com/softistx/nxgt-material/commit/2a41ae13664b425c6c7d37b921e95914eabb2552) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `Slider`: a single value's readout input, which had no name, is named as its
174
+ thumb is — your `aria-labelledby`, else your `aria-label`, else the label —
175
+ and "Value" (`material.slider.value`, en and fr) when nothing names the
176
+ slider. `NumberField` takes an `aria-labelledby`, for an input that is a
177
+ part of a larger field. If you override the bundles or ship another
178
+ language, translate the new key.
179
+
180
+ - [#420](https://github.com/softistx/nxgt-material/pull/420) [`f4ca69b`](https://github.com/softistx/nxgt-material/commit/f4ca69ba5dd7b80f92cdd5374cf6fd01f155784d) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `@nxgt/material-vue` takes the remaining options of `@nxgt/material`, under the same names. Nothing renders differently for code that does not use them; the one change in behaviour, `toAttributeFormSchema`'s error paths, is described after this list:
181
+
182
+ - `Autocomplete` and `AutocompleteFormField` are generic: `options` takes your own option type, read by `get-option-value` / `get-option-label`, and the `#option`, `#selected-leading` and `#actions` slots (React's `renderOption`, `renderSelectedLeading`, `actions`) receive it. `free-solo-label` names the free-solo button.
183
+ - `FilesFormField`: a free-solo autocomplete of file URLs bound to vee-validate, with image thumbnails (`image`, `image-link-builder`).
184
+ - `ConfirmationDialog` `form-options`: a Zod 4 schema (or any Standard Schema, or a vee-validate typed schema) and its `defaultValues`; the `#form` slot renders the fields, confirm waits for a valid form, and `@confirm` receives the parsed values. The form's code (and vee-validate) loads only for a dialog with `form-options`; if it cannot load, after one retry, the dialog says so (`material.confirm-dialog.form-failed`, also added to `@nxgt/material`'s bundles so the keys stay the same).
185
+ - `DateField` and `DateRangeField` `disabled-days`: React's day rules (a day, a list, a range, before / after, days of the week, a function, or a list of those). `DayMatcher` and `DisabledDays` are exported.
186
+ - `SliderField` `ticks`, `leading` and `trailing` (props, or `#leading` / `#trailing` slots).
187
+ - `Avatar` `status` and `status-pulse`: a presence dot named Online, Away, Busy, Offline or Active.
188
+ - `QRCode` `#logo` slot (the default slot still works).
189
+ - `AccordionCard` `#header`, `#title` and `#content` slots.
190
+ - `SelectField` `#option` slot.
191
+ - `CodeEditor` `options`: Monaco's own construction options, passed through (`CodeEditorOptions` is exported). Monaco stays client-only.
192
+ - `Tabs` `variant="line"` (`TabsVariant` is exported).
193
+
194
+ `Autocomplete`, `AutocompleteFormField`, `ConfirmationDialog` and `FilesFormField` are generic components now (`<script setup generic>`): templates infer the option and schema types, but `InstanceType<typeof Autocomplete>` no longer type-checks; use `ComponentExposed` from `vue-component-type-helpers` instead if you need the instance type.
195
+
196
+ `toAttributeFormSchema` (exported from `@nxgt/material-vue`) now goes through the same Standard Schema adapter as `ConfirmationDialog`: an option's error is reported at `options[0]` rather than `options.0`, and each field has one entry holding all its messages.
197
+
198
+ `@nxgt/material`'s README and component pages name these Vue counterparts, and its `SliderField` reads its tick positions from `@nxgt/material-core/slider-ticks`, the same code as before.
199
+ - Updated dependencies [[`d90a68b`](https://github.com/softistx/nxgt-material/commit/d90a68b8f16759b44929effc426b6c3964e92a4c), [`812c101`](https://github.com/softistx/nxgt-material/commit/812c10115278480852852084ddf06b016239e6d4), [`f4ca69b`](https://github.com/softistx/nxgt-material/commit/f4ca69ba5dd7b80f92cdd5374cf6fd01f155784d), [`99878c9`](https://github.com/softistx/nxgt-material/commit/99878c9bdb0d4c33fd6a777970c4f3735bd5ef3b)]:
200
+ - @nxgt/material-core@0.3.0
201
+
202
+ ## 2.5.0
203
+
204
+ ### Minor Changes
205
+
206
+ - [#417](https://github.com/softistx/nxgt-material/pull/417) [`920e6d5`](https://github.com/softistx/nxgt-material/commit/920e6d5f176ba3050ecb8f0ae390ce9d915cf4f7) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `DataTable`: a `loading` prop. With no row to show, the body is skeleton rows
207
+ (`Skeleton`), one cell per visible column, as many as the page size or
208
+ `loadingRows`; with rows already shown, they stay in place, dimmed and
209
+ `inert`, so a refresh does not move the page. The table is `aria-busy` and a
210
+ visually hidden status says "Loading data…" (`material.table.loading`, en and
211
+ fr: translate it if you override the bundles or ship another language).
212
+ Sorting (`DataTableColumnHeader`), the `select` column's "Select all", the
213
+ toolbar's native controls and pagination (`DataTablePagination`'s new
214
+ `disabled`) are disabled meanwhile; the toolbar is now a `<fieldset>`.
215
+
216
+ An `empty` prop (a node, or `({ filtered }) => node`) replaces the "No data
217
+ available" row, inside a `TableEmpty` spanning every visible column;
218
+ `filtered` is `true` when rows exist but none matches the column filters. The
219
+ default empty row is a `TableEmpty` too, and spans the visible columns rather
220
+ than every defined one, as an expanded row now does. An `empty` that renders
221
+ nothing gets the default. New types: `DataTableEmpty`, `DataTableEmptyState`.
222
+
223
+ The table body's `max-h-[300px] overflow-auto` is gone: a table row group is
224
+ neither clipped nor scrolled, so it never had an effect (the wrapper's
225
+ `max-h-[400px]` still scrolls the table).
226
+
227
+ - [#414](https://github.com/softistx/nxgt-material/pull/414) [`79887f0`](https://github.com/softistx/nxgt-material/commit/79887f0554ff018c71969d623d06adc56ac1ebd2) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `@nxgt/material` gains components and helpers `@nxgt/material-vue` already had, under the same public names unless noted:
228
+
229
+ - **`Camera`** is exported, with `useCamera`, `useCameraContext`, `capturePhotoFromVideo`, `isCameraSupported`, `requestCameraPermissions`, `getCameraDevices` and their types. `createCameraStore` is React's counterpart to Vue's `useCameraStore`. `Camera` renders in the browser only, after mount, so it is safe in an SSR page, and each `Camera` keeps its own state.
230
+ - **`NativeSelect`**, `NativeSelectOption` and `NativeSelectOptGroup`: the browser's own `<select>`, styled; an invalid one uses the `error` colour.
231
+ - **`InputOTP`**, `InputOTPGroup`, `InputOTPSlot` and `InputOTPSeparator`, the parts `OtpField` is built from. `InputOTPSeparator` now has `role="separator"` and takes children.
232
+ - **`PaginationFirst`** and **`PaginationLast`** (double chevrons), **`TableEmpty`**, **`DialogScrollContent`**, and the **`ToastAction`** type.
233
+ - **`Button` `size`**: `xs`, `sm`, `default`, `lg`, and the square `icon-xs`, `icon-sm`, `icon` and `icon-lg`. A button without `size` renders as before.
234
+ - **`RadioGroupField`, `SliderField` and `SwitchField`**: aliases of `RadioGroup`, `Slider` and `Switch` (with `*FieldProps`), so code reads the same in both libraries. Nothing is renamed or deprecated; the full naming alignment is planned for 3.0.
235
+
236
+ `@nxgt/material-vue`:
237
+
238
+ - An `icon*` `Button` holding an icon is square again: the base `has-[>svg]:px-3` padding no longer survives the icon sizes.
239
+ - An invalid `NativeSelect` uses the library's `error` colour instead of `destructive`.
240
+ - `PaginationFirst` and `PaginationLast` show double chevrons, so they no longer look like Previous and Next when their text is hidden on a narrow screen.
241
+ - Its README and component pages name the new React counterparts.
242
+
243
+ ### Patch Changes
244
+
245
+ - [#413](https://github.com/softistx/nxgt-material/pull/413) [`5933ad4`](https://github.com/softistx/nxgt-material/commit/5933ad4d63473935a7a7fcf84113940af763314d) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `Alert`: a long word or URL in the title or description wraps inside the
246
+ alert instead of overflowing it, and the icon keeps its size. The text
247
+ column now shrinks (`min-w-0`) and breaks long words, as `Banner`'s text
248
+ column does, and the icon sits in a `shrink-0` wrapper.
249
+
250
+ - [#416](https://github.com/softistx/nxgt-material/pull/416) [`7656bd3`](https://github.com/softistx/nxgt-material/commit/7656bd39810ae873685444177a1265ac3d546022) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `Button`: an `Icon` inside a button that has text (or an `aria-label` /
251
+ `aria-labelledby`) is now `aria-hidden`, so the button is announced "Add"
252
+ instead of "Icon plus Add". A `Button` holding only an `Icon` still reads it,
253
+ and so does one whose only other text is `aria-hidden` (a decorative count).
254
+ If a test of yours found such a button by "Icon {name} …", select it by its
255
+ text — see "A button named "Icon plus Add" is now named "Add"" in
256
+ `docs/troubleshooting.md`.
257
+
258
+ - [#416](https://github.com/softistx/nxgt-material/pull/416) [`5f5cd1a`](https://github.com/softistx/nxgt-material/commit/5f5cd1a9fddd01af0b5d13673d8c2b614ee7609f) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Fixes found by new interaction tests for `NumberField`, `ColorField` and
259
+ `InlineEdit`:
260
+
261
+ - `NumberField`: without an `id`, its `label` named nothing — the input had
262
+ no accessible name and `getByLabelText` could not find it. The input now
263
+ gets a generated id (React's `useId`) when you pass none.
264
+ - `InlineEdit`: closing the editor with Enter, Escape, Save or Cancel put
265
+ focus on `<body>`; it now returns to the field. Clicking elsewhere still
266
+ saves and leaves focus where you clicked.
267
+
268
+ - [#416](https://github.com/softistx/nxgt-material/pull/416) [`1c60b25`](https://github.com/softistx/nxgt-material/commit/1c60b25281900c4a34a9b57d742b3d213031b170) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Long content no longer breaks `ListTile`, `StatCard`, `Banner` or `Stepper`.
269
+
270
+ - `ListTile`: a long title or an unbroken URL wraps inside the tile instead
271
+ of scrolling sideways, and the `leading` / `trailing` pieces keep their
272
+ size (a trailing chevron was squeezed to a sliver).
273
+ - `StatCard`: a long label, value or hint wraps inside the card instead of
274
+ running out of it; the icon and the delta keep their size.
275
+ - `Banner`: a long title or description wraps instead of scrolling sideways;
276
+ the icon keeps its size.
277
+ - `Stepper`: a long step title or label truncates with an ellipsis instead of
278
+ scrolling inside its step.
279
+
280
+ - [#416](https://github.com/softistx/nxgt-material/pull/416) [`c3f5881`](https://github.com/softistx/nxgt-material/commit/c3f58819181579a26bcf00dea37438903f33fc4c) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PaginationPrevious` / `PaginationNext`: with a `label` that is not a string
281
+ (an element), the link's `aria-label` fell back to English "Previous" /
282
+ "Next" whatever the language. It now uses the translated
283
+ `material.pagination.previous` / `next`, as `@nxgt/material-vue` does. The
284
+ new `PaginationFirst` / `PaginationLast` use `material.pagination.first` /
285
+ `last` the same way.
286
+
287
+ - [#413](https://github.com/softistx/nxgt-material/pull/413) [`9894685`](https://github.com/softistx/nxgt-material/commit/9894685ef7a805c9af7cf01f2edb4bc36174a9af) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `Filter`: a `number-range` field is now a `role="group"` named by its label
288
+ (and described by its description and error), so two range filters no longer
289
+ read as "Min", "Max", "Min", "Max"; the inputs keep "Min" and "Max" as their
290
+ part names, as `DurationField` does. A labelled range `Slider` (the
291
+ `slider-range` filter included) is a group named by its label too, and its
292
+ readout inputs, which had no name, are named "Minimum" and "Maximum"
293
+ (`material.slider.minimum` / `maximum`, en and fr). If you override the
294
+ bundles or ship another language, translate the two new keys.
295
+
296
+ - [#411](https://github.com/softistx/nxgt-material/pull/411) [`c346b2b`](https://github.com/softistx/nxgt-material/commit/c346b2be2acae80990ae3b8e937b652db30c240b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The roadmap (`docs/roadmap.md`) lists the upcoming component work: the fixes
297
+ in progress, the exports shared with `@nxgt/material-vue`, and the
298
+ `DateRangeField` presets, `TreeSelectField`, `DataTable` loading and empty
299
+ states and `FilePreview` to follow. Range filters in a `Filter` schema, which
300
+ already render, are listed as shipped.
301
+
302
+ - [#413](https://github.com/softistx/nxgt-material/pull/413) [`95576c4`](https://github.com/softistx/nxgt-material/commit/95576c44ae76dac1b21e556b21b39443b1359b0b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `Spinner` (`variant="1"`): `color="light"` is white again and `color="default"`
303
+ follows the foreground colour. The variant listed `default` twice, so
304
+ `default` rendered white and `light` had no colour, which made a spinner on a
305
+ dark surface (the player's buffering indicator) look static. A filled
306
+ `Button` with `spinnerVariant="1"` gets a white loading spinner again too.
307
+
308
+ - [#416](https://github.com/softistx/nxgt-material/pull/416) [`8265d9a`](https://github.com/softistx/nxgt-material/commit/8265d9af7aaf9715e1a96d56de2bf62324c21f06) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `Stepper`: step ids are unique per stepper, and the steps say their state to
309
+ assistive tech.
310
+
311
+ - Each step's `id` was a static `step-N`, so two steppers on one page
312
+ produced duplicate ids. It is now prefixed with React's `useId`.
313
+ **Note:** if a test or stylesheet of yours targets `#step-N`, select the
314
+ step by `data-pw` instead (`step-N`, or `<name>-step-N`), which did not
315
+ change — see "A `Stepper` step selected by `#step-N` stops matching" in
316
+ `docs/troubleshooting.md`.
317
+ - The steps are an `<ol>` of `<li>`; the current step's button carries
318
+ `aria-current="step"`; a completed or failed step adds visually hidden text
319
+ (`material.stepper.completed`, `material.stepper.error`) so a screen reader
320
+ does not depend on the icon.
321
+ - A long step title no longer widens its column or pushes the next step: the
322
+ columns are `minmax(0, 1fr)` and the title truncates with an ellipsis.
323
+
324
+ - [#415](https://github.com/softistx/nxgt-material/pull/415) [`19c92f3`](https://github.com/softistx/nxgt-material/commit/19c92f348664ed7d49810006748a494fbeed5b60) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `@nxgt/material-vue` gains the exports `@nxgt/material` already had, under the
325
+ same names:
326
+
327
+ - `Checkbox` takes `label` and `helper-text` (or the `#label` and
328
+ `#helper-text` slots) and then renders the box in a labelled row. Without
329
+ them it is the same bare Reka UI box as before. `CheckboxFormField` is its
330
+ vee-validate wrapper: an unset value shows as indeterminate, and an
331
+ indeterminate value is written as `null`.
332
+ - `InlineEditFormField` (the value changes on save) and `CodeEditorFormField`
333
+ (Monaco still loads in the browser only).
334
+ - `PaginationBar` (`v-model:page`, `page-count`, `sibling-count`), the
335
+ `paginationItems` window it renders, and `PaginationLink`.
336
+ - `CopyValue`, a value in a monospace chip with its copy button.
337
+ - `MermaidDiagram`, one mermaid chart rendered in the browser only.
338
+ - The `Doc` parts `DocArticle`, `DocNav`, `DocLink`, `DefaultDocLink` and
339
+ `DocCopyPage`, with the `DocArticleProps`, `DocNavProps` and `DocLinkProps`
340
+ types, to build a documentation shell of your own.
341
+
342
+ Fixed in `Doc`: a parent's `@navigate` runs once per click (it ran twice);
343
+ without a navigate handler the links are plain anchors again, instead of
344
+ links that went nowhere; and the shell follows later `pathname`, `items`,
345
+ `toc`, `previous` and `next` changes, so the active entry tracks the route.
346
+
347
+ The Nuxt module registers the new components. In `@nxgt/material`, the README
348
+ and component pages name these Vue equivalents and list what is still
349
+ React-only.
350
+
351
+ ## 2.4.2
352
+
353
+ ### Patch Changes
354
+
355
+ - [#409](https://github.com/softistx/nxgt-material/pull/409) [`a31ce5f`](https://github.com/softistx/nxgt-material/commit/a31ce5f1db0a6feae418c49d14b845321957cbbc) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `ActionCard` (`variant="sm"`): the check indicator now stays at the right
356
+ edge of the card. It sat right after the title and description, so it moved
357
+ with their length; the text now fills the row, as in `@nxgt/material-vue`. With
358
+ `variant="md"` and no `icon`, the indicator is at the right edge too.
359
+
360
+ ## 2.4.1
361
+
362
+ ### Patch Changes
363
+
364
+ - [#406](https://github.com/softistx/nxgt-material/pull/406) [`b890457`](https://github.com/softistx/nxgt-material/commit/b8904573f18dc6d0cdd16f62146183ff0b7aef18) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Internal: `formatFileSize` and the pdfkit font resolution behind
365
+ `withNxgtPrint` (`@nxgt/material/next`) now come from `@nxgt/material-core`
366
+ (`/format-file-size`, `/pdfkit-fonts`), shared with `@nxgt/material-vue`,
367
+ instead of a copy in this package. No API change: `formatFileSize` is still
368
+ exported by name, and `withNxgtPrint` adds the same globs, warns at most once
369
+ with the same `[@nxgt/material/next]` prefix (that one warning now lists
370
+ every resolution problem, joined by `; `, instead of only the first), and
371
+ still skips fonts on
372
+ another Windows drive with a warning. `@nxgt/material/next` now imports that
373
+ core subpath besides `node:*` built-ins; your install already has
374
+ `@nxgt/material-core`.
375
+ - Updated dependencies [[`bf43a14`](https://github.com/softistx/nxgt-material/commit/bf43a14abb147e6dcd2bcac5f1e0e359b0aae6a2), [`b890457`](https://github.com/softistx/nxgt-material/commit/b8904573f18dc6d0cdd16f62146183ff0b7aef18)]:
376
+ - @nxgt/material-core@0.2.0
377
+
378
+ ## 2.4.0
379
+
380
+ ### Minor Changes
381
+
382
+ - [#404](https://github.com/softistx/nxgt-material/pull/404) [`dcbd54a`](https://github.com/softistx/nxgt-material/commit/dcbd54a34ef86a6a517870b854fec28f6cfffc14) Thanks [@SteveGT96](https://github.com/SteveGT96)! - New subpath `@nxgt/material/next`: `withNxgtPrint(nextConfig)` makes a
383
+ Next.js config ready for route handlers that import `@nxgt/material/print`.
384
+
385
+ ```ts
386
+ // next.config.ts
387
+ import { withNxgtPrint } from '@nxgt/material/next';
388
+
389
+ export default withNxgtPrint({ output: 'standalone' });
390
+ ```
391
+
392
+ It adds `'@nxgt/material'` to `serverExternalPackages` (route handlers are
393
+ bundled with React's `react-server` build, which has no `createContext`), and
394
+ pdfkit's `.cjs` fonts, with the chunk they `require`, to
395
+ `outputFileTracingIncludes` for every route, so `.next/standalone/server.js`
396
+ run alone can render PDFs. pdfkit is resolved through `@react-pdf/renderer`
397
+ and `@react-pdf/font` to its real directory, so the glob follows the
398
+ installation (checked end to end under npm and pnpm). Both lists are merged, never overwritten.
399
+ Without `@react-pdf/renderer` no fonts are added; any other resolution failure
400
+ prints one warning. The function form of `next.config` is accepted too, and
401
+ `{ dir }` sets the project directory (default `process.cwd()`).
402
+
403
+ It runs in Node at config time, imports only `node:*` built-ins, and adds no
404
+ dependency: `next` is not a peer, the config is typed structurally and the
405
+ type you pass in is the type you get back.
406
+
407
+ ### Patch Changes
408
+
409
+ - [#403](https://github.com/softistx/nxgt-material/pull/403) [`6a4938e`](https://github.com/softistx/nxgt-material/commit/6a4938e8624fbb5802a9835bc510e441d5fee4de) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The French UI now says « Enregistrer » for the save action everywhere:
410
+ `material.buttons.save` read « Sauvegarder » while every component's own `save`
411
+ key already read « Enregistrer ». Consumers who assert on the French label in
412
+ their own tests will now see « Enregistrer ».
413
+
414
+ - [#401](https://github.com/softistx/nxgt-material/pull/401) [`69d5de5`](https://github.com/softistx/nxgt-material/commit/69d5de5cc8891d7bfc2e22747cba8516085cf952) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The printing guide and troubleshooting page now cover Next.js. A route
415
+ handler that imports `@nxgt/material/print` needs
416
+ `serverExternalPackages: ['@nxgt/material']`, or `next build` stops with
417
+ `TypeError: (0 , t.createContext) is not a function` (route handlers are
418
+ bundled with React's `react-server` build). With `output: 'standalone'`, add
419
+ `outputFileTracingIncludes: { '/**': ['./node_modules/pdfkit/js/standard-fonts/**/*.cjs'] }`:
420
+ the tracer copies only the `.mjs` fonts, and a webpack build's standalone
421
+ server answered every PDF request with
422
+ `Cannot find module '…/node_modules/pdfkit/js/standard-fonts/Helvetica.cjs'`.
423
+ `withNxgtPrint` (above) sets both for you; the manual config stays documented
424
+ as the fallback.
425
+
426
+ - [#402](https://github.com/softistx/nxgt-material/pull/402) [`6fe042b`](https://github.com/softistx/nxgt-material/commit/6fe042b212896c58bdfd831cc663fa2806d7d657) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `NetworkUploadField`, its size-limit messages ("File exceeds maximum size of
427
+ …", the drop zone's "Max 2.0 MB") and the chat composer's attachment chip now
428
+ format sizes with the exported `formatFileSize`, as `FileList` and `FileCard`
429
+ already did. A negative, `NaN` or infinite size now shows nothing instead of
430
+ `NaN undefined` or `Infinity GB`; every other size reads as before.
431
+
432
+ ## 2.3.1
433
+
434
+ ### Patch Changes
435
+
436
+ - [#398](https://github.com/softistx/nxgt-material/pull/398) [`b4e5770`](https://github.com/softistx/nxgt-material/commit/b4e5770922ad4582a1ad2dcca439f7c8c999a0c7) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Docs: the roadmap's Shipped list now has 2.3.0 — the framework-free code comes from `@nxgt/material-core`, with the same imports as before.
437
+ - Updated dependencies [[`1abb908`](https://github.com/softistx/nxgt-material/commit/1abb90800ec213d8ec98abd7df5dda9f65c14204), [`b4e5770`](https://github.com/softistx/nxgt-material/commit/b4e5770922ad4582a1ad2dcca439f7c8c999a0c7)]:
438
+ - @nxgt/material-core@0.1.1
439
+
440
+ ## 2.3.0
441
+
442
+ ### Minor Changes
443
+
444
+ - [#391](https://github.com/softistx/nxgt-material/pull/391) [`deaf0c4`](https://github.com/softistx/nxgt-material/commit/deaf0c49cbc2268fd20e9e2272104dbd27e4ce7f) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `@nxgt/material` now depends on a new package, `@nxgt/material-core`, which
445
+ your install adds on its own: there is nothing to change in your
446
+ `package.json` or your imports.
447
+
448
+ `@nxgt/material-core` holds the code `@nxgt/material` and
449
+ `@nxgt/material-vue` used to ship as two identical copies: the PDF document
450
+ model and its renderer, the print formatters (`formatAmount`, `formatDate`,
451
+ `createFormatters`), the column and value helpers of `/print`, the PDF file
452
+ helpers behind `PdfReader` and `usePdfFile`, the page geometry, scroll spy
453
+ and text search inside `PdfReader`, the decimals of `CurrencyField`, the rule
454
+ that keeps a field from blurring while focus is in its own popover, and
455
+ `pdfkitStandardFonts()`.
456
+ `@nxgt/material/print`, `/lib`, `/vite` and the other entries export exactly
457
+ the same names as before, with the same types, and now import that code from
458
+ `@nxgt/material-core` instead of bundling it.
459
+
460
+ One case to check: a server bundled into a single file and run **without**
461
+ `node_modules` must bundle `@nxgt/material-core` too, like any other
462
+ dependency of `@nxgt/material` — a bundler that inlines everything already
463
+ does. A server that keeps `node_modules` loads it from there. With Vite, list both:
464
+ `ssr: { noExternal: ['@nxgt/material', '@nxgt/material-core'] }`. Without it
465
+ the server fails at boot with `Cannot find package '@nxgt/material-core'`
466
+ ([troubleshooting](https://cdn.jsdelivr.net/npm/@nxgt/material@2/docs/troubleshooting.md#cannot-find-package-nxgtmaterial-core)).
467
+
468
+ ### Patch Changes
469
+
470
+ - [#387](https://github.com/softistx/nxgt-material/pull/387) [`673ef71`](https://github.com/softistx/nxgt-material/commit/673ef712137e4b9158bdc857c754922952d6d259) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The rich-text editor now uses tiptap 3.31.3 (from 3.29.2), the version
471
+ `@nxgt/material-vue` already ships. The tiptap packages are regular
472
+ `dependencies` of `@nxgt/material`, not peers, so nothing changes in your own
473
+ `package.json`: their ranges are now `^3.31.3`, and your install picks it up.
474
+ The editor code is bundled, so the upgrade comes with the package either way.
475
+
476
+ What you may notice in `RichTextEditor`:
477
+
478
+ - **Security fixes.** ProseMirror's view is now 1.42.3, which fixes an XSS
479
+ where pasting crafted HTML could run script
480
+ ([GHSA-c8x8-7fp4-3x9w](https://github.com/advisories/GHSA-c8x8-7fp4-3x9w)).
481
+ Tiptap also stops untrusted HTML attributes from reaching an object's
482
+ prototype through `mergeAttributes`, and crafted Markdown attributes can no
483
+ longer spin the CPU.
484
+ - **Lists.** Tab at the start of a paragraph right after a list now moves the
485
+ paragraph into the list's last item.
486
+ - **Fixes.** The custom node views (callout, gallery, action card, radio
487
+ group, accordion, checkbox group, select card and select chip) no longer
488
+ freeze on iOS and Android and render faster in long documents; a YouTube
489
+ `/live/` link embeds, and a pasted iframe without `src` no longer crashes
490
+ the editor; unsetting one text style inside a blockquote keeps the others.
491
+
492
+ Code highlighting uses `highlight.js` 11.12.0 in `CodeBlock` (and so in
493
+ Markdown code fences and `ChatMarkdown`) and `highlight.js` 11.11.2 under
494
+ `lowlight` in the editor's code blocks (both were 11.11.1). Mermaid and nivo charts are
495
+ unchanged: only the internal `d3-format` and `d3-time-format` imports of the
496
+ bundled `d3-scale` moved to the versions Mermaid already used, and no rendered label is expected
497
+ to change.
498
+
499
+ If your app installs `@tiptap/*` itself below 3.31.3, a second copy is
500
+ nested under `@nxgt/material`. It works at runtime (the editor is bundled),
501
+ but an `onUpdate` or `onCreate` handler typed with your own `Editor` can
502
+ fail to type-check with "Types have separate declarations of a private
503
+ property": raise your tiptap packages to `^3.31.3`, or leave the handler's
504
+ argument unannotated. See [troubleshooting](https://cdn.jsdelivr.net/npm/@nxgt/material@2/docs/troubleshooting.md#types-have-separate-declarations-of-a-private-property-on-a-richtexteditor-onupdate-or-oncreate-handler), which gains that entry,
505
+ and a line in `RichTextEditor`'s component docs.
506
+
507
+ - Updated dependencies [[`deaf0c4`](https://github.com/softistx/nxgt-material/commit/deaf0c49cbc2268fd20e9e2272104dbd27e4ce7f)]:
508
+ - @nxgt/material-core@0.1.0
509
+
510
+ ## 2.2.0
511
+
512
+ ### Minor Changes
513
+
514
+ - [#374](https://github.com/softistx/nxgt-material/pull/374) [`638b8d1`](https://github.com/softistx/nxgt-material/commit/638b8d152de495f9b66d2d9d53e919ade329b69e) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `CurrencyField` / `CurrencyFormField`: new `fractionDigits` option. The amount
515
+ is shown with the currency's own decimals by default (ISO 4217: `JPY` 0,
516
+ `EUR` 2, `KWD` 3), the rule `formatAmount` follows in this release too; it
517
+ always showed two before. Pass `fractionDigits={2}` to keep two decimals whatever the
518
+ currency; it is clamped to 0–20 (the range every `Intl.NumberFormat`
519
+ accepts), and `NaN` or `Infinity` counts as not given. The emitted number is
520
+ unchanged.
521
+
522
+ The helpers guide now says which formatter to use: `useFormatters()`'s
523
+ `formatAmount` for money, `useNumberFormatter()` for the other
524
+ `Intl.NumberFormat` styles.
525
+
526
+ - [#315](https://github.com/softistx/nxgt-material/pull/315) [`994e63b`](https://github.com/softistx/nxgt-material/commit/994e63b1d1c8561aeb1e3cb2cb7be8d229132bd7) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `ScrollArea` and `ScrollBar` are exported from `@nxgt/material/components` (and `@nxgt/material`)
527
+
528
+ The themed scroll container that `Filter`, `SortableList`, `PdfReader`,
529
+ `StxPlayer`'s queue, the notifications list and `EventCalendar` already use
530
+ inside is now public, as it is in `@nxgt/material-vue`. Give it a height and it
531
+ scrolls whatever overflows, with a thin scrollbar that matches the theme
532
+ (`type` controls when the bar shows; `hover` by default).
533
+
534
+ ```tsx
535
+ import { ScrollArea } from '@nxgt/material/components';
536
+
537
+ <ScrollArea className="h-72 w-48 rounded-md border">{children}</ScrollArea>;
538
+ ```
539
+
540
+ - [#301](https://github.com/softistx/nxgt-material/pull/301) [`91b3d80`](https://github.com/softistx/nxgt-material/commit/91b3d8019dd8a9507fea1ba2507f0f9efca4e788) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Form wrappers honour your `disabled` and `helperText`, and composite fields show their errors.
541
+
542
+ - `TextFormField`, `AutocompleteFormField` (and so `CountryFormField`), `CheckboxFormField`, `CheckboxGroupFormField`, `SwitchFormField`, `CheckCardFormField`, `DateFormField`, `DateRangeFormField`, `UploadFormField`, `NetworkUploadFormField`, `S3UploadFormField`, `FilesFormField`, `CodeEditorFormField`, `RichTextEditorFormField`, `LocationFormField` and `InputGroupFormField` used to overwrite the `disabled` and `helperText` you passed with the form state. Every wrapper now follows one rule: the field is disabled when you pass `disabled` **or** the form disables it, and your `helperText` shows until there is an error. `RichTextEditorFormField` keeps your `editable={false}`. `SwitchFormField` now also passes `error`, so its message is red.
543
+ - Wrappers that already honoured `disabled` used `props.disabled ?? field.disabled`, so `disabled={false}` re-enabled a field that `useForm({ disabled })` had disabled. The form now wins.
544
+ - `ContactsFormField`, `OpeningHoursFormField` and `PostalAddressFormField` passed only their own `disabled` and ignored `useForm({ disabled })`; they follow the same rule now.
545
+ - `ContactsFormField` and `OpeningHoursFormField` showed only `errors.<name>.root`; an array-level rule such as `z.array().min(1)` (on `errors.<name>.message`) was never displayed. Both are shown now, and a nested `name` works.
546
+ - `AttributesFormField` shows the field's error, and follows `form.reset()` / `form.setValue()` instead of reading its value only on mount. `AttributesField` gains `error` and `helperText` props and follows a change in the value of its `attributes` prop (a new array with the same content, as `attributes={list.map(...)}` gives on each render, is not a change); `AttributesFormField` gains `helperText`.
547
+ - `FilesFormField`: the image thumbnail's class `ronded` is now `rounded`.
548
+
549
+ - [#302](https://github.com/softistx/nxgt-material/pull/302) [`6a022fc`](https://github.com/softistx/nxgt-material/commit/6a022fc637a3e0746c1509361748c00611f25ea5) Thanks [@SteveGT96](https://github.com/SteveGT96)! - i18n fallbacks are formatted, and Zod messages are English in English
550
+
551
+ - Components rendered without an `I18nProvider` no longer show raw ICU
552
+ placeholders: the Stepper reads `Step 01` (not `Step {step}`), the
553
+ JsonViewer `3 items` (not `{count} items`), the Kanban WIP badge `3/5`, and
554
+ so on. `useMaterialTranslation().t(key, fallback, values)` now formats the
555
+ fallback with `values`, with or without a provider, and on a server. A
556
+ fallback that cannot be formatted (a stray `{`) is shown as written, with a
557
+ `[@nxgt/material] Could not format the fallback` warning in development
558
+ only. Plain fallbacks are not parsed at all.
559
+ - `zodLocaleError()`'s `invalid_type` messages were French in the English
560
+ bundle. They are English now (`This value must be a number.`), and a type
561
+ with no message of its own gets `This value has the wrong type.` instead of
562
+ its raw key.
563
+ - `zodLocaleError()` no longer throws on a server or when storage is blocked:
564
+ it writes English. It takes optional `{ storageKey, language, translate }`
565
+ so it can read the `storageKey` you gave `I18nProvider`, follow a language
566
+ you pass, and format through your own merged translator so your
567
+ `material.zod.*` overrides apply. Calling it with no argument behaves as
568
+ before.
569
+
570
+ - [#323](https://github.com/softistx/nxgt-material/pull/323) [`2a1c366`](https://github.com/softistx/nxgt-material/commit/2a1c3664d32c47e20a19b05edeae4d140e03cd40) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The rich-text editor, carousel, camera, autocomplete, QR code and barcode follow `I18nProvider`
571
+
572
+ 121 keys join the `material` bundles, in English and French, with the same
573
+ names and the same French as `@nxgt/material-vue`:
574
+
575
+ - `material.rich-text-editor.*` (111): every toolbar tooltip, the table menu,
576
+ the table, callout, accordion, action-card, gallery, selector, checkbox and
577
+ radio group dialogs and forms, the colour picker's `Clear`, the slash menu's
578
+ `No matching commands`, and the `words` / `characters` counter. The
579
+ dialogs' `Save` and the colour picker's `OK` use the existing
580
+ `material.buttons.save` / `material.buttons.ok`.
581
+ - `material.carousel.previous` / `next` (the arrows' screen-reader text),
582
+ `material.camera.*` (capture button, captured image `alt`, and a new
583
+ start/stop tooltip on the stream toggle, which had no accessible name),
584
+ `material.autocomplete.add` (`Add "{query}"`) and `empty`.
585
+ - `material.qrcode.label` and `material.barecode.label`: `QRCode` and
586
+ `Barecode` now render with `role="img"` and an accessible name. `Barecode`
587
+ wraps its drawing in an `inline-flex` `<span data-slot="barecode">` sized to
588
+ the barcode (`max-w-full`), because react-barcode forwards only `id` and
589
+ `className`; its `aria-label` goes on that wrapper, and a `className` still
590
+ reaches the barcode element. `QRCode`'s `title` names the code through
591
+ `aria-label` only and no longer renders an `<svg><title>`, which announced
592
+ the text twice.
593
+
594
+ Without a provider the English is what it was. Anything you pass still wins
595
+ over the translation: `CarouselPrevious`/`CarouselNext` children,
596
+ `Autocomplete`'s `freeSoloLabel` and `empty`, the editor's
597
+ `options.labels.save` / `cancel`, `QRCode`'s `title` or `aria-label`, and
598
+ `Barecode`'s `aria-label`.
599
+
600
+ - [#331](https://github.com/softistx/nxgt-material/pull/331) [`0dca729`](https://github.com/softistx/nxgt-material/commit/0dca72985506ae1630433fe9896c8f42118c8087) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The calendar, data table, filter, spinner, scroll-to-top, network upload, map marker and the editor's slash menu, fonts and table alignment follow `I18nProvider`
601
+
602
+ 82 keys join the `material` bundles, in English and French, with the same
603
+ names and the same French as `@nxgt/material-vue`:
604
+
605
+ - `material.rich-text-editor.*` (47): the slash menu's group, item titles and
606
+ descriptions (`slash-menu.*`), the font menu (`font-default`, `font-sans`,
607
+ `font-serif`, `font-mono`) and the table-caption alignment choices
608
+ (`table-align-left` / `-center` / `-right`).
609
+ - `material.filter.*` (19): `min`, `max`, and the presets menu and dialog
610
+ (`presets.*`).
611
+ - `material.calendar.*` (9): the previous/next month and year tooltips, the
612
+ year view's paging, `Go to Today`, and the caption button's label.
613
+ - `material.table.labels.select-all` / `select-row`,
614
+ `material.upload.retry` / `remove`, `material.spinner.loading`,
615
+ `material['scroll-to-top'].label`, `material.map.you-are-here`.
616
+
617
+ The slash menu filters on the translated title and description and on the
618
+ English ones, accent-insensitively; `RichTextEditor` hands the menu its own
619
+ translator, so there is nothing to wire.
620
+
621
+ Without a provider the English is unchanged. An explicit label still wins:
622
+ `Spinner`'s `aria-label` now reaches the `variant="4"` SVG, and
623
+ `LocationMarker`'s `tooltip` prop is no longer overwritten by `You are here`.
624
+
625
+ - [#358](https://github.com/softistx/nxgt-material/pull/358) [`6281850`](https://github.com/softistx/nxgt-material/commit/62818500e16295ef5845587b60f062b6f2f605e3) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PrintDataTable`, `PrintKeyValue` and `PrintMetric`: printed tables and figures from column definitions
626
+
627
+ - **`PrintDataTable`** (`@nxgt/material/print`) prints `rows` through
628
+ `columns` from `definePrintColumns<T>()`: a `key` or an `accessor`, a
629
+ `label`, a `width` in points or percent or a `flex` share, an `align`
630
+ (right by default for figures), a `format` (`'amount'`, `'date'`,
631
+ `'number'`, `'percent'` or your own function), a `currency` per column or
632
+ per row, and a `total` (`'sum'` or your own). The header repeats on each
633
+ page the table crosses, rows never split (`keepRowsWhole`), rows stripe
634
+ (`striped`), a totals row closes the table, and an empty table shows one
635
+ "No rows" row (`emptyText`, or `hideWhenEmpty`). Amounts and dates follow
636
+ the `PrintDocument`'s locale.
637
+ - **`PrintKeyValue`**: label and value pairs in one or two columns, from
638
+ `items` or from `data` with the same column definitions as `fields`; empty
639
+ values are skipped unless `keepEmpty`.
640
+ - **`PrintMetric`** / **`PrintMetrics`**: a figure with its label, hint and
641
+ change (`+4%`), the printed `StatCard`, lined up in a row.
642
+ - **`resolvePrintTable`**, **`resolvePrintFields`**, **`toPrintValue`** and
643
+ the `PrintValue` helpers (`formatPrintValue`) are exported from `/print`
644
+ too, for your own blocks.
645
+ - New i18n keys `material.print.table.total` and `material.print.table.empty`,
646
+ in English and French.
647
+
648
+ - [#358](https://github.com/softistx/nxgt-material/pull/358) [`4221b82`](https://github.com/softistx/nxgt-material/commit/4221b8213d7743023395f352da1d63090c7b4734) Thanks [@SteveGT96](https://github.com/SteveGT96)! - One call to a PDF file, and the document model, from `@nxgt/material/print`
649
+
650
+ - **`renderPdfBlob(doc)`**, **`renderPdfBuffer(doc)`** (a `Uint8Array`, in
651
+ the browser or on Node/Bun) and **`renderPdfStream(doc)`** (a Web
652
+ `ReadableStream`, Node 18+ or Bun).
653
+ - **`downloadPdf(doc | blob, { fileName })`** saves the file
654
+ (`document.pdf` by default; `.pdf` is added), and **`openPdf(doc | blob)`**
655
+ opens it in a new tab — the tab opens before rendering so popup blockers
656
+ let it through, and a blocked tab falls back to a download.
657
+ - **`pdfResponse(doc, { fileName, disposition, headers })`**: a Web
658
+ `Response` with the PDF's type, length and an RFC 6266 file name, for a
659
+ React Router loader, a Next route handler, Hono or `Bun.serve`;
660
+ **`contentDisposition(disposition, fileName)`** builds that header for a
661
+ response of your own (Express, a Node stream).
662
+ - **`usePdfFile(doc)`**: `{ blob, url, loading, error, refresh }`, the
663
+ object URL revoked when replaced or unmounted.
664
+ - `doc` is a react-pdf element (such as a `<PrintDocument>`) **or a document
665
+ model**: plain data built with the new builders (`pdfDocument`,
666
+ `definePdfDocument`, `page`, `heading`, `text`, `table`, `keyValue`,
667
+ `totals`, `metric`, `badge`, `header`, `dataTable` and `keyValueOf` from
668
+ the same column definitions as `PrintDataTable`, …), which can be built on a server
669
+ and sent as JSON. A `definePdfDocument` template annotates its data
670
+ parameter, and its other parameters may take defaults
671
+ (`(invoice: Invoice, language = 'en') => …`). A model is rendered with the package's translations
672
+ (`language`) and `@nxgt/material` as its creator. The model is
673
+ `@experimental`: it may still change in a minor release.
674
+
675
+ - [#358](https://github.com/softistx/nxgt-material/pull/358) [`90aa776`](https://github.com/softistx/nxgt-material/commit/90aa7768373274a0298e7155a9a7010be33ac72f) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PrintDocument`: a PDF page shell with page numbers; `PrintHeader` takes a logo image
676
+
677
+ - **`PrintDocument`** (`@nxgt/material/print`) replaces the `Document` +
678
+ `Page` you wrote by hand: `size`, `orientation`, `margins`, the PDF
679
+ metadata (`title`, `author`, `subject`, `keywords`, `creator`, `producer`),
680
+ a `header` and a `footer` repeated on every page, `pageNumbers` ("Page 2 of
681
+ 5", translated) and a `watermark`. A `footer` may be a function of
682
+ `{ pageNumber, totalPages }` returning text. Its `language` defaults to the
683
+ `I18nProvider` around the document, then `'en'`.
684
+ - **`translateMaterial(language, key, fallback, values)`** (`@nxgt/material/i18n`):
685
+ the package's own strings for a language given as a value, with no
686
+ provider and no hook, for a PDF's page-number callback or a server.
687
+ `useMaterialTranslation().t` now goes through it, so a regional language
688
+ such as `'fr-CA'` reads the French strings instead of falling back to
689
+ English.
690
+ - New i18n keys `material.print.page-of` and `material.print.page`, in
691
+ English and French.
692
+ - **`PrintHeader`**: a `logo` image (URL or data URL, or
693
+ `{ src, width, height }`), a `subtitle` and a `referencePrefix` (`'#'` by
694
+ default). **Changed:** with no `logo` and no `logoText` it no longer draws
695
+ a grey "LOGO" placeholder, and with no `qrDataUrl` no empty QR box; the
696
+ title stays centred. Pass `logoText="LOGO"` to keep the old box.
697
+ - French amounts print correctly (also inside mixed text such as
698
+ `{amount} TTC`, and in a `PrintDocument` footer function) in `PrintSection`, `PrintInfoRow`,
699
+ `PrintTableCell`, `PrintTotalRow`, `PrintCardField` and `PrintBadge`: the
700
+ narrow no-break space `Intl` puts in `1 234,50 €` used to print as a slash
701
+ (`1/234,50 €`) in the standard fonts.
702
+ - **Peer floor raised:** `@react-pdf/renderer` is now `^4.9`, the version
703
+ the page shell and `pdfkitStandardFonts()` are built and tested against
704
+ (pdfkit 0.20). Upgrade with
705
+ `bun add @react-pdf/renderer@^4.9` (or `npm i @react-pdf/renderer@^4.9`).
706
+
707
+ - [#350](https://github.com/softistx/nxgt-material/pull/350) [`d043779`](https://github.com/softistx/nxgt-material/commit/d0437794584c9f4e49a2bc7f88299da5cedaac32) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader` opens any source: `file` takes a `Blob` (a generated PDF, a
708
+ `File`), an `ArrayBuffer` or a `Uint8Array`, a `URL`, or
709
+ `{ url, httpHeaders, withCredentials }` for a URL that needs a token, as well
710
+ as a URL string. The new `PdfSource` and `PdfUrlSource` types are exported.
711
+
712
+ - Bytes are copied: pdf.js empties the buffer it is given, and yours stays
713
+ usable after the reader opened it.
714
+ - The document reloads only when it changes: a URL with the same headers, even
715
+ in a new object on each render, keeps the view; a Blob or a buffer is
716
+ compared by identity.
717
+ - Download and print fetch a URL source with its headers, and save a Blob or
718
+ bytes as they are. When that fetch fails, a URL with headers is not opened
719
+ in a new tab, which would load it without them. With no `fileName`, a
720
+ `File`'s name or a URL segment, it is named "document.pdf", translated
721
+ (`material.pdf-reader.default-file-name`).
722
+ - The headers are part of what identifies the document: new headers (a
723
+ renewed token) load it again, from `initialPage`.
724
+
725
+ - [#332](https://github.com/softistx/nxgt-material/pull/332) [`bb0ede2`](https://github.com/softistx/nxgt-material/commit/bb0ede263ca373fd94c552c1aa8ba51f57409fd3) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader`: Download now finishes in Firefox and Safari too. The link is
726
+ attached before its click and the object URL is revoked a minute later, not
727
+ right away. A new `fileName` prop names the saved file, and a URL document is
728
+ named after its last path segment instead of always `document.pdf` (`.pdf` is
729
+ added when missing). A URL document is saved from the bytes already loaded,
730
+ so a cross-origin one downloads instead of navigating. Print uses a
731
+ same-origin copy too, and opens a new tab instead of throwing `SecurityError`
732
+ when the browser refuses.
733
+
734
+ - [#373](https://github.com/softistx/nxgt-material/pull/373) [`be623e1`](https://github.com/softistx/nxgt-material/commit/be623e1c75d9326b3d93251585322d81cb094b2c) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader` takes `externalLinkTarget` (`'_blank' | '_self' | '_parent' |
735
+ '_top'`, default `'_blank'`): a link to a website inside the PDF now opens in a
736
+ new tab, whatever the PDF asks; pass `'_self'` for the previous behaviour.
737
+ `rel` stays `noopener noreferrer nofollow`.
738
+
739
+ `usePdfSource`, `toPdfjsSource` and `toPdfjsParams` are exported beside
740
+ `usePdfReader` (with the `PdfjsSourceParams` / `PdfjsUrlParams` types), so a
741
+ layout of your own opens bytes without detaching your buffer.
742
+
743
+ `Tooltip` and `TooltipContent` take `container`, where the tooltip is
744
+ portalled.
745
+
746
+ - [#350](https://github.com/softistx/nxgt-material/pull/350) [`e7bc71a`](https://github.com/softistx/nxgt-material/commit/e7bc71a5205fba09333b954c53be86700dbea173) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader`'s zoom menu is a `DropdownMenu`: it opens from the keyboard (Enter
747
+ or Space, the arrows move, Escape closes and gives focus back), its button is
748
+ named after the current zoom ("Zoom level 150%",
749
+ `material.pdf-reader.zoom-menu`), and in fullscreen it opens inside the reader.
750
+ Keys pressed in the menu no longer turn pages. An outline entry without a title
751
+ reads "Untitled" in the reader's language (`material.pdf-reader.untitled`).
752
+
753
+ A link inside the PDF to another page goes to it through the reader, so it
754
+ works for a page not drawn yet and the page number follows; it used to do
755
+ nothing unless that page was already drawn.
756
+
757
+ `DropdownMenuContent` takes `container`, the element the menu is portalled into
758
+ (`document.body` by default): pass `document.fullscreenElement` for a menu
759
+ opened in fullscreen.
760
+
761
+ - [#350](https://github.com/softistx/nxgt-material/pull/350) [`d717ad4`](https://github.com/softistx/nxgt-material/commit/d717ad4ba7264eb91158bb4279b082a58fc88644) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `formatAmount` and `formatDate` take a locale and a time zone, and
762
+ `useFormatters()` / `createFormatters()` bind them.
763
+
764
+ - `formatAmount(value, currency?, { locale?, fractionDigits? })` and
765
+ `formatDate(value, { locale?, timeZone?, dateStyle?, timeStyle? })`. Without a
766
+ `locale` they still use the runtime's (a deterministic default is planned for
767
+ 3.0). `formatDate` also accepts a `Date` or an epoch number.
768
+ - `useFormatters({ locale?, timeZone? })` from `@nxgt/material/hooks` binds both
769
+ to the `I18nProvider` language (`en` without a provider), so a server render
770
+ and the browser print the same string (for a date with a time, pass
771
+ `timeZone`).
772
+ - `createFormatters({ locale?, timeZone? })` from `@nxgt/material/lib` (and
773
+ `/print`) binds them outside React: a server route, a PDF document.
774
+
775
+ **Changed output.** Amounts use their currency's own decimals (ISO 4217):
776
+ `formatAmount(1234.5, 'JPY')` is now `¥1,235` (was `¥1,234.50`), KWD, BHD and
777
+ OMR show three decimals. EUR, USD, GBP and the other two-decimal currencies do
778
+ not change, nor does an amount without a currency. To keep two decimals, pass
779
+ `{ fractionDigits: 2 }`. `fractionDigits` is clamped to 0–20, as in
780
+ `CurrencyField`, and `NaN` or `Infinity` counts as not given.
781
+
782
+ **Fixed.** A date-only string (`'2026-03-01'`) keeps its calendar day in every
783
+ time zone; west of UTC it used to show the day before.
784
+
785
+ - [#303](https://github.com/softistx/nxgt-material/pull/303) [`1ccaed6`](https://github.com/softistx/nxgt-material/commit/1ccaed698045037832e261e85ed949c7e1da8fea) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Declaration files always import `react` by name, and `CircularProgressProps` is exported
786
+
787
+ - A locally packed or linked build could ship declaration files importing
788
+ `'../../../node_modules/react'`. That path does
789
+ not exist in a consumer's install, so `ReactNode` and `ComponentProps` were
790
+ typed `any`, and `ConfirmationDialog` demanded `variant` and `color`.
791
+ Versions installed from npm were not affected; locally packed or linked
792
+ builds were.
793
+ - `CircularProgressProps` is exported from `@nxgt/material/components` (and
794
+ `@nxgt/material`), like the other components' props types.
795
+
796
+ - [#306](https://github.com/softistx/nxgt-material/pull/306) [`6899daf`](https://github.com/softistx/nxgt-material/commit/6899daf0cc4965f02bd6f496d2108749b026ff2b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Fix a batch of visual and API rough edges:
797
+
798
+ - **Calendar** (and the calendars in `DateField` / `DateRangeField`): weeks with two-digit days no longer push the last column out of the 312px calendar.
799
+ - **CodeBlock**: `scheme="dark"` now gives a dark code area too — the global `pre` / `code` styles no longer paint a light background over it. Line numbers line up with their lines.
800
+ - **ScrollToTop**: `variant` and `color` now reach the button.
801
+ - **ButtonGroup**: a `tonal` button marked `data-state="active"` now shows its label (primary foreground on the primary fill).
802
+ - **AvatarGroup**: fallback initials are sized to the avatar and no longer run under the next one. To make room, the overlap is shallower: `sm` / `md` / `lg` now overlap by 2 / 4 / 6px (was 8 / 10 / 12px).
803
+ - **HydrateLoading**: new `loadingIcon` prop; the misspelled `lodadingIcon` still works and is deprecated.
804
+ - **MenubarItem**: accepts `variant="error"` like `DropdownMenuItem` and `ContextMenuItem`; `"destructive"` still works and is deprecated. Both now use the `error` colour, and both render `data-variant="error"` — CSS or tests selecting `[data-variant=destructive]` on a menubar item no longer match; select `[data-variant=error]`.
805
+ - **useTheme**: every caller shares one store, so components that read `theme` (CodeBlock, CodeEditor, CodeDiff, JsonViewer) follow a toggle straight away. SSR-safe; the stored format is unchanged; a choice that storage refuses to save is kept for the session. `ThemeToggle` no longer repaints `<html>` with the server's `system` guess while hydrating.
806
+ - **filterMentions**: new optional `{ match: 'prefix' | 'substring' }` (default `'substring'`, unchanged).
807
+ - **RichTextEditor**: `options.mentions.suggestions` accepts `MentionItem[]` (the type `MentionList` and `ChatComposer` take) as well as `string[]`.
808
+ - Print: the `PrintHeader` QR box is centred again (a class typo dropped `justify-center`).
809
+
810
+ ### Patch Changes
811
+
812
+ - [#355](https://github.com/softistx/nxgt-material/pull/355) [`c79a91e`](https://github.com/softistx/nxgt-material/commit/c79a91e4f9d138c3eed9b1fb0218bd87ce202c61) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `NetworkUploadField` / `S3UploadField`: the field is a `group` named by its
813
+ label, and once a file hides the drop zone (or it is disabled: `maxFiles`
814
+ reached, or `disabled`) the group carries the error (the `helperText`, or a
815
+ hidden "This value is not valid."), which nothing said before. `ImageField`: a
816
+ failed upload with no message of its own is described as not valid, not only
817
+ outlined in red.
818
+
819
+ `AppBreadcrumb`: each crumb is `<li><a>…</a></li>` (the `<li>` sat inside the
820
+ `<a>`), and the home crumb is a link named "Home" rather than a `<button>`
821
+ inside a link.
822
+
823
+ `SwitchFormField` and `CheckboxFormField` no longer use the field's `name` as
824
+ the control's `id`, which collided between two forms on one page; without an
825
+ `id` of yours one is generated.
826
+
827
+ `UploadField` and `TimeField` are tinted only while they hold a value, like an
828
+ `<input>`: `inputDefaultClassNames`' `not-placeholder-shown:` tint always
829
+ matched these `div`s. The `Switch` row uses the same untinted base.
830
+
831
+ - [#324](https://github.com/softistx/nxgt-material/pull/324) [`ce245a1`](https://github.com/softistx/nxgt-material/commit/ce245a110fb5db1f0b8d5317b9d3975054be69b5) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `ResponsiveButton` (and `ResponsiveLinkButton`, `LoadMoreButton`, the
832
+ `EntityHeader` actions built on it) no longer sets `aria-expanded`: it said
833
+ whether the label was showing, so screen readers announced a button that
834
+ controls nothing as "collapsed". Used as a popover, drawer or menu trigger (the
835
+ `Filter` trigger), it still carries the trigger's own `aria-expanded`.
836
+
837
+ `Switch`: the row's text is now the switch's `<label>`, so a labelled switch
838
+ has an accessible name (even without an `id`) and clicking the text toggles
839
+ it. With a `placeholder`, the `label` above the row is the name
840
+ (`aria-labelledby`) and the row text is the description (`aria-describedby`),
841
+ as in `@nxgt/material-vue`; an `aria-label` / `aria-labelledby` you pass
842
+ wins. The row is tinted from the switch's real state (`checked`, or
843
+ `defaultChecked` and each toggle) instead of `value`, and is no longer tinted
844
+ while off. `className` applies when there is no field
845
+ around the switch, on the row or the bare switch.
846
+
847
+ The row carries `data-checked="true"` / `"false"`, from the same state.
848
+ `data-filed` (misspelt) read `value`, so a form-bound switch always showed
849
+ `"false"`; it now follows the switch's state too, and is deprecated: it is
850
+ removed in 3.0.
851
+
852
+ - [#374](https://github.com/softistx/nxgt-material/pull/374) [`6c62121`](https://github.com/softistx/nxgt-material/commit/6c621219870379e618a570f7be70312d93f7b70c) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Form wrappers: leaving a field straight into another field's freshly opened
853
+ overlay — a `SelectFormField` list opened by pressing the mouse on its
854
+ trigger, which takes focus without the trigger ever having it — marks the
855
+ field touched as it is left. It used to count that overlay as the field's own
856
+ and mark the field touched only when the list handed focus back to its
857
+ trigger. An overlay is the field's when one of its controls names it
858
+ (`aria-controls`), or when it opened since focus entered and no other control
859
+ names it.
860
+
861
+ - [#379](https://github.com/softistx/nxgt-material/pull/379) [`ffee913`](https://github.com/softistx/nxgt-material/commit/ffee91378bfd6b9483484841f1908ebd80b6ee1e) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Bug reports have an address: `bugs.email` in `package.json`, and the
862
+ "Reporting a bug" section of the troubleshooting page, linked from the README.
863
+
864
+ - [#368](https://github.com/softistx/nxgt-material/pull/368) [`2930292`](https://github.com/softistx/nxgt-material/commit/293029293c3bca705596a0466517a5878e39909f) Thanks [@SteveGT96](https://github.com/SteveGT96)! - A dismissible `Chip` whose `label` is a node (`<b>Paris</b>`) names its ✕
865
+ "Remove Paris" after the text it shows, as a text label already did; a label
866
+ with no text keeps "Remove". The same rule as `@nxgt/material-vue`'s `Chip`,
867
+ which names it after its default slot when one is given.
868
+
869
+ - [#333](https://github.com/softistx/nxgt-material/pull/333) [`12763f1`](https://github.com/softistx/nxgt-material/commit/12763f1585f16205553ec67e96822d29a720427d) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `DialogClose`, `DrawerClose` and `SheetClose` (and `SheetContent`'s built-in
870
+ close button) are named "Close" when they show only their icon: screen readers
871
+ announced an unnamed button. The map's zoom buttons are named "Zoom in" /
872
+ "Zoom out". All follow `I18nProvider` (`material.buttons.close`,
873
+ `material.map.zoom-in`, `material.map.zoom-out`); text children or an
874
+ `aria-label` you pass win.
875
+
876
+ - [#305](https://github.com/softistx/nxgt-material/pull/305) [`2239896`](https://github.com/softistx/nxgt-material/commit/2239896218f73cd8cef24a50db482848c32be582) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `ComboboxField` (and `TimezoneField`, which is built on it) now shows the selected option's label in the input instead of its raw value, filters the options as you type, and shows "No results" only when nothing matches.
877
+
878
+ The config-driven `FormField` now stores the picked `File` for `type: 'upload'` (it stored a `FileList` and then crashed rendering it), writes the value `onUpload` resolved for `type: 'network-upload'` (it never reached the form), keeps a config's `helperText` until there is an error message to show in its place, and is disabled by `useForm({ disabled })` like every other wrapper. `{ type: 'switch' }` type-checks as a `FieldConfig` without a cast. `UploadFormField` no longer crashes after a file is picked in single mode. `location` stays in `FieldType` but has no config: use `LocationFormField` from `@nxgt/material/map`.
879
+
880
+ `LocationField` (`@nxgt/material/map`) shows an empty field and no preview when its value is `null`, instead of the coordinates of a default city; pressing OK on an empty field commits where the pin was last dragged to (or its start), even when the parent does not pass `value` back. Its placeholder, dialog title and button are translated (`material.location-field.*`, `en` and `fr`), and the trailing button has an accessible name.
881
+
882
+ - [#378](https://github.com/softistx/nxgt-material/pull/378) [`84610c2`](https://github.com/softistx/nxgt-material/commit/84610c2e02ced1fc9051231d355c1d3ea6b0faee) Thanks [@SteveGT96](https://github.com/SteveGT96)! - A Tailwind app that imports `@nxgt/material/styles.css` now gets the CSS the
883
+ components need, which reached only the compiled stylesheet before: KaTeX
884
+ (`Markdown` maths), the code theme (`CodeBlock`), highlight.js
885
+ (`RichTextEditor` code blocks), react-easy-crop (`ImageCropper`), and the
886
+ stylesheets of `ButtonGroup`, `BottomAppBar`, `ContactItem`,
887
+ `OpeningHoursItem`, `AttributeForm` and `AttributeValuesForm`, which were CSS
888
+ modules and are global `nxgt-*` classes now. The compiled stylesheet styles
889
+ the same components the same way.
890
+
891
+ - [#299](https://github.com/softistx/nxgt-material/pull/299) [`611e008`](https://github.com/softistx/nxgt-material/commit/611e0082d4417e8e19cd28bb6608b2d649e77720) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Documentation with a preview of every component
892
+
893
+ - The README catalogue has a **Preview** column, and each name links to its
894
+ entry in `docs/components/`.
895
+ - `docs/` now ships in the package:
896
+ - one page per category: preview, example, options;
897
+ - the component gallery;
898
+ - getting started;
899
+ - guides for forms, i18n, the app shell, theming, printing, maps, SSR and
900
+ the helpers;
901
+ - troubleshooting;
902
+ - the roadmap.
903
+ - The README's long sections moved to those guides.
904
+
905
+ - [#366](https://github.com/softistx/nxgt-material/pull/366) [`24bc584`](https://github.com/softistx/nxgt-material/commit/24bc58416b64f86f3b80bc4c34eb89378338a830) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `DateRangeField`: the trigger button carries `data-slot="date-range-field-trigger"`
906
+ (instead of `popover-trigger`), as `DateField`'s carries `date-field-trigger`.
907
+
908
+ - [#339](https://github.com/softistx/nxgt-material/pull/339) [`b60cffc`](https://github.com/softistx/nxgt-material/commit/b60cffc3b3081c054983e317cbe7764892462de4) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `TimeField` renders the same trigger on the server and in a desktop browser's
909
+ first render (`useMedia` now has a default), so SSR pages no longer log a
910
+ hydration attribute mismatch or react-use's "defaultState should be defined"
911
+ warning; a phone switches to the dialog once mounted.
912
+
913
+ `DateField` and `DateRangeField`: `disabled` disables the field (the trigger
914
+ can no longer be opened) instead of reaching the calendar as "every day is
915
+ disabled". Day rules move to the new `disabledDays`; a non-boolean `disabled`
916
+ is still passed to the calendar. `id`, `aria-describedby` and `data-*` land on
917
+ the trigger in `DateField`, `DateRangeField` and `TimeField` (both its popover
918
+ and its phone dialog) instead of on the calendar, the inner box, or nowhere.
919
+
920
+ `UploadField`: its file input is visually hidden instead of `hidden`, so a
921
+ `required` field with no file stops the submit with the browser's message
922
+ instead of a console-only "An invalid form control … is not focusable".
923
+ `ImageField`, `FileListField` and the `NetworkUploadField` / `S3UploadField`
924
+ drop zone no longer put `aria-invalid` on a `group` or a `button`: the error text
925
+ describes them, and the outline comes from `data-invalid`.
926
+
927
+ - [#341](https://github.com/softistx/nxgt-material/pull/341) [`ee9d630`](https://github.com/softistx/nxgt-material/commit/ee9d630423a3ae068dae6f240a2d2c5b5bb9f404) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Icon badges that looked like an `IconButton` but did nothing are no longer
928
+ buttons: the list-row badges of `Contacts`, `OpeningHours`, `PostalAddress`,
929
+ `AttributesList` and `FileListField`, the `NetworkUploadField` file preview,
930
+ the `EntityHeader`, `ConfirmationDialog` and `AppSidebar` icons, the `Stepper`
931
+ info icon, the `AccordionCard` chevron and the map's default marker pins. Each
932
+ was a nameless `<button>` (focusable, except the disabled upload preview); each is now a `<span>` with
933
+ `aria-hidden="true"` and the same look. A `Contacts`
934
+ badge is `role="img"`, named by the contact type. The `AccordionCard` header
935
+ is now the `<button>` itself (it was a `<div>`, reachable from the keyboard only
936
+ through the chevron), and its chevron now turns when open. A `Marker` with a
937
+ string `tooltip` passes it to leaflet as `title`, which names the focusable
938
+ marker now that the pin inside is hidden (a `title` you pass wins). `ConfirmationDialog`'s `icon` is the glyph alone (the
939
+ dialog wraps it in the badge; style it with `iconProps`): an `IconButton`
940
+ passed there is now a button inside a hidden `<span>`.
941
+
942
+ An `Icon` inside a named `IconButton` (and so `InputGroupButton` and
943
+ `IconLinkButton`) is `aria-hidden` instead of `role="img"` named
944
+ "Icon {name}", so a screen reader reads "Delete", not "Delete, Icon trash". An
945
+ `aria-label` you give the `Icon` still wins; an `Icon` anywhere else keeps its
946
+ "Icon {name}" default. See troubleshooting for the selectors this changes.
947
+
948
+ - [#374](https://github.com/softistx/nxgt-material/pull/374) [`917f477`](https://github.com/softistx/nxgt-material/commit/917f47724037c5bd0b9e844a3ca6154abcb37f4b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `definePdfDocument`: an unannotated data parameter is typed `unknown`, not
949
+ `never`. With `never`, the template compiled and the error appeared only
950
+ where the template was called (`Argument of type 'Invoice' is not assignable
951
+ to parameter of type 'never'`); now reading a property of it fails in the
952
+ template itself. Annotated parameters and defaults (`language = 'en'`) are
953
+ typed as before.
954
+
955
+ - [#316](https://github.com/softistx/nxgt-material/pull/316) [`1906412`](https://github.com/softistx/nxgt-material/commit/1906412fee6fde2a485ef87e40fe8fe662e5aa96) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `ContactItem` and `OpeningHoursItem` are deprecated, and removed in 3.0
956
+
957
+ Both are single rows bound to a react-hook-form array (`control` + `name` +
958
+ `index`), and nothing in the library renders them. Use `ContactsFormField` and
959
+ `OpeningHoursFormField`, which own the list, the add / edit dialog and the
960
+ array. 3.0 moves every react-hook-form-bound export to a `@nxgt/material/forms`
961
+ subpath with react-hook-form as an optional peer (see `docs/roadmap.md`).
962
+
963
+ - [#374](https://github.com/softistx/nxgt-material/pull/374) [`5401d94`](https://github.com/softistx/nxgt-material/commit/5401d941717d711ec8bb97243f2086f928802494) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `DialogContent` hands focus back, on close, to the element that had it when
964
+ the dialog opened — a `DialogTrigger` as before, and now also the button of
965
+ yours that opened a controlled `Dialog`, where focus used to fall to `<body>`.
966
+ Focus that already went elsewhere (a non-modal dialog closed by a click in
967
+ another input) stays there. An `onCloseAutoFocus` that calls
968
+ `event.preventDefault()` opts out.
969
+
970
+ - [#300](https://github.com/softistx/nxgt-material/pull/300) [`bf5b452`](https://github.com/softistx/nxgt-material/commit/bf5b452454cd0e48241723381865d8f4a6dcdfc9) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Dialog fixes
971
+
972
+ - `ConfirmationDialog` can be controlled: `open` and `onOpenChange` from a
973
+ parent now drive it. Before, `open` only seeded internal state, and passing
974
+ `onOpenChange` stopped the dialog from opening or closing itself. Without
975
+ `open` it is uncontrolled (`defaultOpen`), and `onOpenChange` only
976
+ observes. If you passed `open` only to open it initially, pass
977
+ `defaultOpen` instead.
978
+ - `ConfirmationDialog` validates its form before confirming: `onConfirm`
979
+ receives the schema's parsed output (trims and transforms applied) instead
980
+ of the raw `getValues()`.
981
+ - `ConfirmDialog` stays open while an async `onConfirm` runs (buttons
982
+ disabled, "Processing..."), closes on success, and on a throw or rejection
983
+ stays open and shows the error's message instead of logging it and closing.
984
+ The pending label and the message shown for an error without one are
985
+ translated (`material.confirm-dialog.processing`,
986
+ `material.confirm-dialog.failed`, en and fr).
987
+ - `CommandDialog` renders its visually hidden title and description inside the
988
+ dialog, rather than in the page next to it.
989
+
990
+ - [#337](https://github.com/softistx/nxgt-material/pull/337) [`f3e8f1b`](https://github.com/softistx/nxgt-material/commit/f3e8f1b4211748d3bcdb39ba58097d78280488b7) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The package no longer ships type declarations for its stories, tests and their
991
+ data: 351 files under `dist/lib` (`*.stories.d.ts`, `*.test.d.ts`,
992
+ `*.rows.d.ts`, `__stories__/`, `stories-*.d.ts`, story data) are gone from the
993
+ tarball. None was reachable from an entry point, so no import changes; a
994
+ declaration that named `storybook` or `vitest` types can no longer surface in a
995
+ consumer's type check.
996
+
997
+ - [#372](https://github.com/softistx/nxgt-material/pull/372) [`69cbb3e`](https://github.com/softistx/nxgt-material/commit/69cbb3e9f8bef8cfb3cc6ff8e28b6a97d084a333) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `DateField`, `DateRangeField` and `TimeField`: the `Label` and the helper text
998
+ are no longer inside the trigger button (a `<label>` in a `<button>` is
999
+ invalid, and the helper text was read as part of the value). They sit beside
1000
+ it in the field's root `div`, which takes `className`; the trigger is named by
1001
+ the Label (`aria-labelledby`) and described by the helper text, then your
1002
+ `aria-describedby` ids. The Label is `for` the trigger, so a click on it opens
1003
+ the picker, and the open popover (or `TimeField`'s dialog on a phone) is
1004
+ labelled by the trigger. A disabled field dims its trigger only, no longer its
1005
+ label and helper text. `DateFieldTrigger`, `DateRangeFieldTrigger` and `TimeFieldTrigger`
1006
+ render only what the button shows.
1007
+
1008
+ `Slider`: an `aria-label` or `aria-labelledby`, and `aria-invalid` (or
1009
+ `error`), go on every thumb rather than on the root `span`, and a vertical
1010
+ `Slider`'s `label` names its thumbs.
1011
+
1012
+ `DurationField`: a `group` named by its Label and described by its helper
1013
+ text; the inputs are named "Hours" and "Minutes"
1014
+ (`material.duration-field.hours-label` / `minutes-label`, en and fr). The
1015
+ minutes input had no name. `NumberField` takes an `aria-label`.
1016
+
1017
+ `CodeEditor`: Monaco's textarea is named by the field's label (`ariaLabel`),
1018
+ and follows it when it changes.
1019
+
1020
+ - [#319](https://github.com/softistx/nxgt-material/pull/319) [`cadee35`](https://github.com/softistx/nxgt-material/commit/cadee353d2d01f5c1ce051d1ffa2c3f0bf35326e) Thanks [@SteveGT96](https://github.com/SteveGT96)! - The filter trigger's accessible name reads "Filters, 2 active" instead of
1021
+ "Filters2": the count badge is hidden from assistive technology and the count
1022
+ is announced through `material.filter.active-count` (en, fr).
1023
+
1024
+ - [#351](https://github.com/softistx/nxgt-material/pull/351) [`892eed6`](https://github.com/softistx/nxgt-material/commit/892eed6c7731237141136e07d669e1c3db220fb1) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Every `*FormField` now calls React Hook Form's `field.onBlur` when focus leaves the field, so `touchedFields` flips and `mode: 'onBlur'` / `'onTouched'` validate as they do for a plain input. About twenty wrappers never called it (`SelectFormField`, `ComboboxFormField`, `ColorFormField`, `CountryFormField`, `TimezoneFormField`, `RatingFormField`, `OtpFormField`, `DurationFormField`, `InlineEditFormField`, `CheckboxFormField`, `SortableListFormField`, `TransferListFormField`, the dialog editors, the uploads, and the config-driven `FormField` for most types), and others called it too early, when focus moved to their own button, thumb or popover. Moving between a field's own controls, or into the popover or dialog it opened, is not leaving it: a picker is touched when focus leaves its trigger and its popover, a dialog editor when focus leaves its buttons and the dialog. When the form validates is unchanged: that is still its `mode`. The `PostalAddress`, `Contacts`, `OpeningHours` and attribute dialogs now hand focus back to the button that opened them when they close, and `SliderFormField` no longer crashes once the form holds a number for it (after the first change, or with a numeric default value): it passes that number to the slider as a one-thumb array. A vertical `SliderFormField` is touched when focus leaves all of its thumbs, like a horizontal one.
1025
+
1026
+ - [#359](https://github.com/softistx/nxgt-material/pull/359) [`6eb7b52`](https://github.com/softistx/nxgt-material/commit/6eb7b526f65ef11fe54861d6381b0d2982ec4134) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `RadioGroup` / `CheckboxGroup`: an option's `id` is the group's `id` plus its
1027
+ index (`` `${id}-${i}` ``), no longer its `value`, so two groups with the same
1028
+ options (two yes/no questions) no longer share ids and a label no longer
1029
+ checks the other group's option. The group's `id` is yours, or generated with
1030
+ `useId()`. `RadioGroupFormField` / `CheckboxGroupFormField` no longer use the
1031
+ field's `name` as that `id`, which collided between two forms on one page. A
1032
+ `#<name>` or `#<option value>` selector needs an `id` of yours: see the
1033
+ troubleshooting entry.
1034
+
1035
+ `AppBreadcrumbItem`: React Router's options besides `replace` and `state`
1036
+ (`preventScrollReset`, `relative`, `reloadDocument`, `viewTransition`, …) and
1037
+ the anchor-only attributes (`target`, `rel`, `download`, …) now go to its link.
1038
+ They were spread onto the `<li>`, where React warned about them and the link
1039
+ never saw them.
1040
+
1041
+ - [#361](https://github.com/softistx/nxgt-material/pull/361) [`66454a7`](https://github.com/softistx/nxgt-material/commit/66454a7c8d69f0643c1cb330ac1faac9538a6f60) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `RadioGroup`, `CheckboxGroup` and their form wrappers are now named by their
1042
+ `label`: the `radiogroup` (and `CheckboxGroup`'s wrapper, now a
1043
+ `role="group"`) carries `aria-labelledby` to the label and `aria-describedby`
1044
+ to the helper or error text, so a screen reader announces the question, not
1045
+ only the first option. A caller's `aria-label` / `aria-labelledby` still wins.
1046
+ `CheckboxGroup` no longer puts `aria-invalid` on the group, where it is not
1047
+ allowed; an error without text is described by a visually hidden "This value
1048
+ is not valid.", and so is a `CheckboxGroupFormField` in error with no message
1049
+ that shows the caller's `helperText`. `RadioGroup` sets `aria-invalid` on its
1050
+ `radiogroup` from `error` (a caller's own `aria-invalid` wins).
1051
+
1052
+ - [#357](https://github.com/softistx/nxgt-material/pull/357) [`5614b57`](https://github.com/softistx/nxgt-material/commit/5614b57d01f379692bc0c03b00811a335d231ed6) Thanks [@SteveGT96](https://github.com/SteveGT96)! - More built-in labels follow `I18nProvider`
1053
+
1054
+ 30 keys join the `material` bundles, in English and French, with the same
1055
+ names and French as `@nxgt/material-vue`: the filter's empty state, `+N more`
1056
+ chip and the Yes/No, From, Until and Up to of its chips (`filter.empty`,
1057
+ `filter.more`, `filter.value.*`); the `aria-label` of `Breadcrumb` and
1058
+ `Pagination`, the breadcrumb ellipsis's "More" and `DocHeader`'s breadcrumb
1059
+ (`breadcrumb.*`, `pagination.label`, `doc.breadcrumb`); `MessageCard`'s
1060
+ "Unread" (`cards.unread`); the network upload field's "Replace {name}",
1061
+ "Upload failed" and its type, size and count errors (`upload.*`); the editor's
1062
+ placeholder and emoji buttons (`rich-text-editor.placeholder`,
1063
+ `insert-emoji`); `CircularProgress`'s label, `StxPlayer`'s "Media player",
1064
+ `PaneLayoutPane`'s sheet title, `JsonViewer`'s "Empty JSON" and
1065
+ `TimePicker`'s AM/PM. `pagination.first` / `last`,
1066
+ `carousel.roledescription` / `slide` and `attributes.buttons.add-option` are
1067
+ used by the Vue library only.
1068
+
1069
+ The editor's default placeholder now follows a language change without
1070
+ rebuilding the editor, as does a new `placeholder`. `formatFilterValue` takes
1071
+ the chip words as an optional third argument, typed `FilterValueWords`, with
1072
+ the English in the newly exported `FILTER_VALUE_WORDS`; without it the English
1073
+ is unchanged. `parseJsonSource` marks a blank source with `empty: true`. The
1074
+ upload count error now reads "Maximum of 1 file reached" for a single file. Your
1075
+ `placeholder`, `sheetTitle` and `aria-label` still win.
1076
+
1077
+ `NetworkUploadField`: a successfully uploaded item gets its primary border
1078
+ and tint again.
1079
+
1080
+ - [#336](https://github.com/softistx/nxgt-material/pull/336) [`f4cac2e`](https://github.com/softistx/nxgt-material/commit/f4cac2e35398d0dd177aa608ea8fa395300af39b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Icon-only buttons that screen readers announced as a bare "button", or by
1081
+ their icon's name ("Icon chevron-down", "Icon trash"), now have a name,
1082
+ translated under `I18nProvider`: the `Sidebar` toggle ("Toggle sidebar",
1083
+ `material.sidebar.toggle`, with `aria-expanded`), the data table's `expand`
1084
+ column ("Toggle row details", `material.table.labels.toggle-row`, with
1085
+ `aria-expanded`), the `LocationPreview` default trigger ("Preview location" /
1086
+ "Preview {name}", `material.map.preview` / `preview-named`), and the delete
1087
+ button of the deprecated `ContactItem` / `OpeningHoursItem` (the existing
1088
+ `material.contacts.buttons.remove` / `material.opening-hours.buttons.remove`).
1089
+
1090
+ A chip's remove button is named after the chip: "Remove {label}"
1091
+ (`material.chip.dismiss-label`) on a dismissible `Chip` whose `label` shows text
1092
+ and on a `ComboboxChip` whose children are text; `ComboboxChip` takes a
1093
+ `removeLabel` for other children. `Autocomplete`'s chips are dismissible
1094
+ `Chip`s, so each remove button is a real, focusable `<button>` instead of a
1095
+ nameless `div` nested inside the chip's button, and it is disabled with the
1096
+ field. The `Sidebar` toggle also shows while focus is inside the sidebar, so
1097
+ the keyboard can reach it (it used to appear on hover only).
1098
+
1099
+ - [#307](https://github.com/softistx/nxgt-material/pull/307) [`8ad61ed`](https://github.com/softistx/nxgt-material/commit/8ad61ed3cca67ca8df3b8fe50fd5834b40a84315) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `ImageCropper` no longer crashes with "Maximum update depth exceeded" when it
1100
+ mounts in free mode before its image has loaded. The 0×0 crop size reported
1101
+ before the image has a size used to become a `NaN` aspect, which the cropper
1102
+ saw as a change on every update.
1103
+
1104
+ - [#334](https://github.com/softistx/nxgt-material/pull/334) [`3cd0a34`](https://github.com/softistx/nxgt-material/commit/3cd0a340226bba46665e602760823b333a130c10) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `Label`'s required asterisk is hidden from screen readers (`aria-hidden`): a
1105
+ required field was named "Email*" or read "Email star". The field's own
1106
+ `required` / `aria-required` says it, which every field of the library now
1107
+ sets. A test that found a field by a name ending in `*` finds it by the label
1108
+ alone: see the troubleshooting page.
1109
+
1110
+ - [#365](https://github.com/softistx/nxgt-material/pull/365) [`41e7e60`](https://github.com/softistx/nxgt-material/commit/41e7e605f6e145369c50bca221345aa84cf352cc) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `@nxgt/material/map` no longer loads the rest of the library with it: in the
1111
+ built package its static imports drop from about 5.5 MiB to about 0.4 MiB, so a
1112
+ lazily loaded map fetches far less code. No API change.
1113
+
1114
+ - [#342](https://github.com/softistx/nxgt-material/pull/342) [`c22223d`](https://github.com/softistx/nxgt-material/commit/c22223db5eeadc1cdae76bbed88db75961c4496d) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `useIsMobile`, `useTheme`, and every component that reads them (`AppHeader`,
1115
+ `AppBreadcrumb`, `DataTable`, `ResponsiveButton`, `ThemeToggle`, `CodeBlock`,
1116
+ `CodeDiff`, `JsonViewer`, `CodeEditor`), plus `Doc` and `TimeField`, now give
1117
+ the server an answer for their media query: a desktop, light screen. SSR
1118
+ servers no longer log react-use's "defaultState should be defined" warning,
1119
+ and a phone or a dark-mode browser no longer hits a hydration mismatch that
1120
+ made React throw the server HTML away. The client switches right after
1121
+ hydrating; a page rendered only in the browser reads the real answer from its
1122
+ first render, as before.
1123
+
1124
+ - [#376](https://github.com/softistx/nxgt-material/pull/376) [`f6b751e`](https://github.com/softistx/nxgt-material/commit/f6b751eead179285e169487057a7f72d397572ce) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `DialogContent`, `AlertDialogContent`, `SheetContent` and `DrawerContent` move
1125
+ focus into themselves when they open, as a modal must (WCAG 2.4.3): to their
1126
+ first control, or to the content when it has none. `AlertDialogContent`
1127
+ focuses `AlertDialogCancel`, or its first control when there is no Cancel.
1128
+ They used to cancel Radix's open auto-focus, so focus stayed behind the
1129
+ overlay. `Drawer` now turns Vaul's `autoFocus` on by default. An
1130
+ `onOpenAutoFocus` that calls `event.preventDefault()` (or `autoFocus={false}`
1131
+ on a `Drawer`) keeps focus where it was; focus still returns to the opener on
1132
+ close. A test that types right after opening may now type into the overlay:
1133
+ see the troubleshooting entry "Keys typed right after opening a dialog, sheet
1134
+ or drawer land inside it".
1135
+
1136
+ - [#375](https://github.com/softistx/nxgt-material/pull/375) [`a49f3f5`](https://github.com/softistx/nxgt-material/commit/a49f3f5d5e45f14161f899a29b00fcd9ce46beec) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `AlertDialogContent`, `SheetContent` and `DrawerContent` hand focus back, on
1137
+ close, to the element that had it when they opened, as `DialogContent` does:
1138
+ the button of yours that opened a controlled one (no trigger) gets it back,
1139
+ where focus used to fall to `<body>`. Focus that already went elsewhere stays
1140
+ there, and an `onCloseAutoFocus` that calls `event.preventDefault()` opts out.
1141
+
1142
+ - [#375](https://github.com/softistx/nxgt-material/pull/375) [`e7bce88`](https://github.com/softistx/nxgt-material/commit/e7bce883a159d5f2ac80b1ff68a8ce5c7b40b11b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader`'s text and annotation layer styles (react-pdf's `TextLayer.css`
1143
+ and `AnnotationLayer.css`) now come with `@nxgt/material/styles.css`, so a
1144
+ Tailwind app gets selection, search highlights, links and notes styled. They
1145
+ used to reach only the compiled `dist/material.css`.
1146
+
1147
+ - [#373](https://github.com/softistx/nxgt-material/pull/373) [`be623e1`](https://github.com/softistx/nxgt-material/commit/be623e1c75d9326b3d93251585322d81cb094b2c) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader`:
1148
+
1149
+ - A page's own `/Rotate` is honoured: a page the PDF shows landscape is drawn,
1150
+ thumbnailed, placed and fitted landscape (it was drawn at rotation 0).
1151
+ `PdfPageSize` gains an optional `rotate`.
1152
+ - Fit width and fit page use the first 25 pages, read together when the
1153
+ document opens, so the zoom no longer moves as later page sizes arrive
1154
+ (still read 25 pages at a time); the fit stops at 500%.
1155
+ - `onPageChange` also reports the page the reader moves to itself: an
1156
+ `initialPage` clamped to the last page, and `initialPage` again after a new
1157
+ `file`.
1158
+ - The password field is named "Password" (new key
1159
+ `material.pdf-reader.password-label`), not after the "Password protected"
1160
+ heading.
1161
+ - The first search renders the pages once per 25 pages read, not once per
1162
+ page.
1163
+ - A note's icon shows: pdf.js asked the host page for `annotation-note.svg`
1164
+ (a 404); the icons are now inlined, loaded only for a document with notes.
1165
+
1166
+ `TooltipContent` (and so every `Tooltip`) opens inside the element in
1167
+ fullscreen while there is one: `PdfReader`'s toolbar tooltips show in
1168
+ fullscreen.
1169
+
1170
+ - [#330](https://github.com/softistx/nxgt-material/pull/330) [`a10e3de`](https://github.com/softistx/nxgt-material/commit/a10e3de8437ab1334c805afac41e6f2a67ea2674) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader` is no longer shown as deprecated in your editor: the `@deprecated`
1171
+ tag now sits on its old alias `PdfViewer`, where it was meant to be. Code that
1172
+ imports `PdfViewer` now sees the strike-through; switch to `PdfReader`, which
1173
+ takes the same props.
1174
+
1175
+ - [#338](https://github.com/softistx/nxgt-material/pull/338) [`a0bc68b`](https://github.com/softistx/nxgt-material/commit/a0bc68b00200c56b97f7dd9d21320120e221f4a8) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader`: fit width and fit page use each page's real size instead of
1176
+ assuming US Letter, fitting the widest and tallest page with one zoom, so A4
1177
+ and landscape pages no longer overflow or leave a gap. Placeholders and
1178
+ thumbnails take their page's shape before it draws. `PdfPageList`,
1179
+ `PdfSidebar` and `PdfThumbnailSidebar` take an optional `pageSizes`, and the
1180
+ `PdfPageSize` type is exported. The fit also follows the reader's real size;
1181
+ it used to fit an 800 × 600 box.
1182
+
1183
+ - [#335](https://github.com/softistx/nxgt-material/pull/335) [`e74c53f`](https://github.com/softistx/nxgt-material/commit/e74c53fa980d3d94e57ac2c8911404503585533b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader`: a wrong password says "Incorrect password. Try again." (new key
1184
+ `material.pdf-reader.password-incorrect`, en and fr), marks the field `aria-
1185
+ invalid`, announces it and focuses the field again. The loading spinner, the
1186
+ error message and the password field now show before the document has loaded;
1187
+ they never appeared, so a protected or broken PDF left the reader blank, and
1188
+ kept the pages hidden even after the right password. Escape closes the open
1189
+ search bar before a Radix `Dialog` or `Sheet` around the reader sees it, and
1190
+ every other Escape reaches the dialog. `onPageChange` reports a page inside the
1191
+ document, and only when it changes.
1192
+
1193
+ - [#345](https://github.com/softistx/nxgt-material/pull/345) [`9931914`](https://github.com/softistx/nxgt-material/commit/99319140fb2fc170ec714b41204fd43a4042d1c6) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader`: opens on `initialPage`, which it showed in the page field but
1194
+ never scrolled to (past the end, it opens the last page; a new `file` opens on
1195
+ `initialPage` again). The page number follows scrolling, and `onPageChange`
1196
+ reports the page most on screen; a page you ask for is reported once, not each
1197
+ page passed on the way.
1198
+
1199
+ - [#340](https://github.com/softistx/nxgt-material/pull/340) [`f633f98`](https://github.com/softistx/nxgt-material/commit/f633f980a2bd74805f4c79694416fa3e12b5b17a) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `PdfReader`: search covers the whole document, pages not drawn yet included,
1200
+ and counts as you type. It finds every match, including one split across text
1201
+ runs, and ignores case, accents, ligatures and the width of white space.
1202
+ Moving to a match shows its page and marks it. `PdfPageList` takes an optional
1203
+ `highlight`, which `PdfReader` passes it, and the `PdfSearchHighlight` type is
1204
+ exported.
1205
+
1206
+ - [#311](https://github.com/softistx/nxgt-material/pull/311) [`5b7b609`](https://github.com/softistx/nxgt-material/commit/5b7b609b63cc04eea38701649c68c52eb9460f47) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `StxPlayer` no longer leaves an unhandled promise rejection
1207
+ (`Failed to execute 'exitFullscreen' on 'Document': Document not active`) when
1208
+ it unmounts while fullscreen or in picture-in-picture, or when the browser
1209
+ refuses a fullscreen request. A refused change leaves the player as it is.
1210
+
1211
+ - [#334](https://github.com/softistx/nxgt-material/pull/334) [`b656f23`](https://github.com/softistx/nxgt-material/commit/b656f237e7a104e719ce88bb292f7a40297804e9) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Every field that shows the required asterisk now says "required" on the
1212
+ element a screen reader lands on, not only with the `*`:
1213
+
1214
+ - `DateField`, `DateRangeField`, `TimeField`: the trigger is a `combobox`
1215
+ (a `button` may not carry `aria-required`) named by the label, with
1216
+ `aria-required` (and `aria-invalid` with `error`). An `aria-label` /
1217
+ `aria-labelledby` you pass now names it (a picker with no label needs one,
1218
+ or a `placeholder`); on `DateField` / `DateRangeField` it used to reach the
1219
+ calendar instead. `DateField`,
1220
+ `DateRangeField` and the phone-width `TimeField` now show the asterisk they
1221
+ were missing, and the phone-width `TimeField` trigger is a focusable button
1222
+ instead of a `div`.
1223
+ - `DurationField`: `required` on both inputs.
1224
+ - `Slider`: `aria-required` on every thumb, and each thumb is named by the
1225
+ label ("Price Minimum" / "Price Maximum" for a range).
1226
+ - `RatingField`: the stars are a `radiogroup`, named by the label, with
1227
+ `aria-required`.
1228
+ - `RichTextEditor`: the editable area is a multi-line `textbox` named by the
1229
+ label, with `aria-required`. `CodeEditor`: Monaco's `ariaRequired`.
1230
+ - `TransferList`: `aria-required` on the Selected listbox, and both panes are
1231
+ named by the label, then their title ("Team Selected"). `SortableList`: the
1232
+ listbox is named by the label, with `aria-required`.
1233
+ - `ComboboxField`, `Autocomplete`: `aria-required` on the input;
1234
+ `Autocomplete`'s label now names its input.
1235
+ - Tests that found a picker by the `button` role, or a thumb or a pane by
1236
+ its old exact name: see the troubleshooting page.
1237
+ - `ImageField`, `FileListField`, `UploadField` (a file picker is a button, and
1238
+ their file input is hidden): the field is a `group` named by the label and
1239
+ described by a visually hidden "This field is required."
1240
+
1241
+ - [#304](https://github.com/softistx/nxgt-material/pull/304) [`8136ffa`](https://github.com/softistx/nxgt-material/commit/8136ffa7578b4d16dc51804a30a248bea2c80114) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Fix select, time, OTP and radio fields:
1242
+
1243
+ - `SelectChipFormField` and `SelectCardFormField` in `mode="single"` no longer crash after the choice is cleared (the form value `null` is now read as "nothing selected"), and only the option equal to the value is shown active: option `pro` no longer lights up for `professional`. The form value keeps its type (`string | null` in single mode, `string[]` in multiple). The same fix applies to the config-driven `chips-single` field.
1244
+ - `SelectField` now shows its `placeholder` (it still falls back to `label`) and the required asterisk when `required` is set.
1245
+ - `SelectTrigger` gets its small radius back (a stray fragment had broken its class list), and the focused `SelectItem` is tinted again.
1246
+ - `TimeField` on desktop and `TimeFormField` show the empty state (`placeholder`) when there is no value, instead of the current time or `00:00`. `TimeFormField asString` with `''` no longer throws `RangeError: Invalid time value`.
1247
+ - `OtpField` generates its id with `useId()`, so server and client render the same markup.
1248
+ - `SelectFormField`, `SelectChipFormField`, `SelectCardFormField`, `RadioGroupFormField`, `TimeFormField` and `OtpFormField` honour your own `disabled` prop, and show your own `helperText` while there is no validation error. Before, they overwrote both with the form state.
1249
+ - `RadioGroup` spaces its label, helper text and options again (`gap-2`).
1250
+
1251
+ - [#318](https://github.com/softistx/nxgt-material/pull/318) [`aec0a95`](https://github.com/softistx/nxgt-material/commit/aec0a95fc152f52e1e99e0d28f347ca0018456db) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Switch helper text, a resumable step form, translated confirm labels, one link per LinkButton, named collapsed buttons
1252
+
1253
+ - `Switch` shows `helperText` without a `placeholder` (under the labelled row
1254
+ or the bare switch), so `SwitchFormField`'s validation message is no longer
1255
+ lost. A switch in a labelled row is no longer announced as invalid when it
1256
+ has no `error`.
1257
+ - `createStepFormSlice` resumes from what `updateStep` stored under its `name`
1258
+ (step and values; nothing is read on a server, and anything that is not a
1259
+ step-form state is ignored), and `reset` clears that entry.
1260
+ `useSliceReducer` computes a slice's initial state once, on mount.
1261
+ - `ConfirmDialog` and `ConfirmationDialog` translate their default button
1262
+ labels: `material.confirm-dialog.confirm` / `.cancel` (English and French).
1263
+ - `LinkButton`, `IconLinkButton` and `ResponsiveLinkButton` render a single
1264
+ `<a>` styled as the button instead of a `<button>` inside a link: one tab
1265
+ stop, role `link`. `Button` with `asChild` keeps its loading spinner inside
1266
+ the child, and neither `Button` nor `IconButton` puts `type` on an `asChild`
1267
+ element.
1268
+ - A collapsed `ResponsiveButton` (the `Filter` trigger on a phone, for one)
1269
+ keeps its label as screen-reader text, so the icon-only button has an
1270
+ accessible name.
1271
+
1272
+ - [#368](https://github.com/softistx/nxgt-material/pull/368) [`9b04052`](https://github.com/softistx/nxgt-material/commit/9b04052b7183b4b1d9f6717c48dd2550481ff16e) Thanks [@SteveGT96](https://github.com/SteveGT96)! - A server-rendered wizard built on `createStepFormSlice` and rendered with
1273
+ `useSliceReducer` hydrates without a mismatch: `useSliceReducer` renders the
1274
+ server's state (step 0) while hydrating and resumes at the stored step once
1275
+ mounted. A page rendered only in the browser still shows the stored step from
1276
+ its first render, and `reset` still clears it. Wrapping such a wizard in
1277
+ `clientOnly` is no longer needed; a slice kept in a Redux store follows the
1278
+ forms guide's recipe instead.
1279
+
1280
+ - [#346](https://github.com/softistx/nxgt-material/pull/346) [`1d9e5d4`](https://github.com/softistx/nxgt-material/commit/1d9e5d483be9479e3046ccb724f06205f96ecf0f) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `Stepper` gives the server an answer for its screen-width query (the desktop
1281
+ layout), like the other responsive components: SSR servers no longer log
1282
+ react-use's "defaultState should be defined" warning, and a desktop no longer
1283
+ hits a hydration mismatch on a page with a `Stepper`. `PaneLayout` and
1284
+ `ListDetailsLayout` use the same helper; they rendered the wide layout on the
1285
+ server already, and a page rendered only in the browser now reads the real
1286
+ answer from its first render instead of flashing the wide layout on a phone.
1287
+
1288
+ `TimeField`'s OK button follows the language (`material.buttons.ok`), and
1289
+ both branches place props the same way: on a phone `name`, `title`, `style`
1290
+ and the other `div` props now reach the field (they were dropped), and on a
1291
+ desktop the picker gets `onBlur` only: `name`, `required` and any other
1292
+ `div` prop (`onClick`, `onKeyDown`, `title`, …) now go to the field rather
1293
+ than the picker's `div`, and `disabled` stays on the trigger button instead
1294
+ of also landing on the field's display `div`.
1295
+
1296
+ - [#378](https://github.com/softistx/nxgt-material/pull/378) [`bdfb9aa`](https://github.com/softistx/nxgt-material/commit/bdfb9aa71bd50a00784f8cb7caeb46c69f2b3893) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `Switch` and `Autocomplete` no longer put `aria-invalid` on a plain `div`,
1297
+ where it is not allowed. An invalid `Switch` says so on the `switch` alone,
1298
+ and its bordered row shows it with a red border; an invalid `Autocomplete`
1299
+ says so on its input alone, and the chips box shows it with a red border.
1300
+
1301
+ - [#327](https://github.com/softistx/nxgt-material/pull/327) [`da23d93`](https://github.com/softistx/nxgt-material/commit/da23d93dd204435684d1db59b62a4980d9c42290) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `Switch`'s `helperText` (the validation message under `SwitchFormField`) now
1302
+ describes the switch through `aria-describedby`, with or without a row, next
1303
+ to the row text and any `aria-describedby` you pass.
1304
+
1305
+ - [#375](https://github.com/softistx/nxgt-material/pull/375) [`186f591`](https://github.com/softistx/nxgt-material/commit/186f59155d344fe38ba06cc981ac2da7f1019781) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `TimeField` no longer puts `aria-invalid` on a plain `div`, where it is not
1306
+ allowed: the box inside the trigger shows an error with a class, and an
1307
+ `aria-invalid` you pass (as `TimeFormField` does) goes on the trigger
1308
+ `combobox`, with the field's `error`.
1309
+
1310
+ - [#344](https://github.com/softistx/nxgt-material/pull/344) [`571c2e5`](https://github.com/softistx/nxgt-material/commit/571c2e55695492de09ee3b4de591e1f821bd4a4a) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `TimeField` passes `onBlur` to its time picker on a phone too (under 768 px, in
1311
+ the dialog), as it already did in the desktop popover, so a `TimeFormField` on a
1312
+ phone is marked touched and validated on blur (`mode: 'onBlur'` /
1313
+ `'onTouched'`) instead of only at submit.
1314
+
1315
+ - [#371](https://github.com/softistx/nxgt-material/pull/371) [`af0dbef`](https://github.com/softistx/nxgt-material/commit/af0dbef82373ce9d32390b1c39c1b2694008fed1) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `NetworkUploadField`: the drop zone's limits line ("Max 2.0 MB · Up to 3
1316
+ files") follows the language like the rest of the field
1317
+ (`material.upload.dropzone.max-size` / `max-files`, with French), and says
1318
+ "Up to 1 file" instead of "Up to 1 files".
1319
+
1320
+ - [#343](https://github.com/softistx/nxgt-material/pull/343) [`8c67eee`](https://github.com/softistx/nxgt-material/commit/8c67eeead39fa0a5cfa9a7c24d00f85e5d76bcf3) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `FileListField` says its error once: the field group is described by the error
1321
+ text, and the drop zone no longer is as well (a screen reader read it on
1322
+ entering the group and again on the drop zone). `ImageField`, `FileListField`,
1323
+ `UploadField` with `trailing`, and the `NetworkUploadField` / `S3UploadField`
1324
+ drop zone, in error without `helperText`, are described by a visually hidden
1325
+ "This value is not valid." (`material.zod.default`, translated) instead of
1326
+ being only a red outline.
1327
+
1328
+ ## 2.1.0
1329
+
1330
+ ### Minor Changes
1331
+
1332
+ - [#296](https://github.com/softistx/nxgt-material/pull/296) [`7a4307b`](https://github.com/softistx/nxgt-material/commit/7a4307ba094c1e6bb243ffc92584bc46620af264) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `formatAmount` and `formatDate` are on the barrel again
1333
+
1334
+ 2.0.0 moved the print components out of the barrels, and took these two with
1335
+ them. That was wrong: they are pure `Intl` helpers with no dependency on
1336
+ `@react-pdf/renderer`, `react-pdf-tailwind` or anything else, and they are used
1337
+ far outside print documents — 66 files in `nxgt-federation` alone. On 2.0.x each
1338
+ of those has to import `@nxgt/material/print`, which drags in `pdfkit` and asks
1339
+ the app to install two optional peers it has no use for.
1340
+
1341
+ They now live in `lib/lib/print-format.utils.ts`, which means:
1342
+
1343
+ ```ts
1344
+ import { formatAmount, formatDate } from '@nxgt/material'; // back
1345
+ import { formatAmount, formatDate } from '@nxgt/material/lib'; // new
1346
+ import { formatAmount, formatDate } from '@nxgt/material/print'; // unchanged
1347
+ ```
1348
+
1349
+ `@nxgt/material/print` re-exports them, so no print document changes. Additive
1350
+ everywhere, so this is a minor — and `verify:ssr` still shows `print.js` as the
1351
+ only entry that reaches the print-only dependencies.
1352
+
1353
+ - [#297](https://github.com/softistx/nxgt-material/pull/297) [`25051d4`](https://github.com/softistx/nxgt-material/commit/25051d40f6e8c3a7aab26490d53e5d924f150da9) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `@nxgt/material/map` — the leaflet map comes back from `@nxgt/map`
1354
+
1355
+ `Map`, `Marker`, `LocationMarker`, `LocationPreview`, `MapActions`,
1356
+ `MinimapControl`, `LocationField`, `LocationFormField` and `useGeolocation`, the
1357
+ same eleven exports `@nxgt/map@1.0.2` had, now an entry of this package.
1358
+
1359
+ ```sh
1360
+ bun add leaflet react-leaflet @react-leaflet/core \
1361
+ @adamscybot/react-leaflet-component-marker react-geolocated
1362
+ ```
1363
+
1364
+ ```tsx
1365
+ import '@nxgt/material/map.css';
1366
+ import { LocationFormField } from '@nxgt/material/map';
1367
+ ```
1368
+
1369
+ Those five are **optional peers**, the same pattern `./print` uses for
1370
+ `@react-pdf/renderer`: an app that renders no map never has leaflet in its tree.
1371
+
1372
+ **The entry cannot be imported without a DOM**, and that is the point rather than
1373
+ a defect: `leaflet` reads `window` while it is evaluated, so an SSR app renders
1374
+ these components client-side only — behind `clientOnly` from `@nxgt/material/lib`.
1375
+ An SPA has nothing to do. `verify:ssr` now enforces three things: no other entry
1376
+ reaches leaflet, `map.js` is skipped by the import sweep with the reason printed,
1377
+ and a stale entry in that skip list fails the build.
1378
+
1379
+ `@nxgt/map` existed for this exact SSR crash. What it cost was worse than the
1380
+ crash: it declared `@nxgt/material: ^1.0.0`, so an app depending on both could not
1381
+ take the 2.x major at all. The package is deprecated in favour of this entry.
1382
+
1383
+ ### Patch Changes
1384
+
1385
+ - [#295](https://github.com/softistx/nxgt-material/pull/295) [`04b2ac0`](https://github.com/softistx/nxgt-material/commit/04b2ac0d8edf65820dccc33a53508fb5558e7693) Thanks [@SteveGT96](https://github.com/SteveGT96)! - A staged version is in flight, not lost — correcting 2.0.1's release note
1386
+
1387
+ 2.0.1 shipped with the claim that npm's
1388
+ `409 Cannot publish over previously staged version` burns the version number,
1389
+ and that 2.0.0 was therefore unrecoverable. **That is wrong, and both versions
1390
+ prove it**: npm finished staging 2.0.0 and published it at 11:42 UTC on
1391
+ 2026-09-22, about forty minutes after its first attempt. 2.0.1 took about six
1392
+ minutes. Both are installable, with identical content.
1393
+
1394
+ What staging really costs is patience, and the confirmation check added in
1395
+ 2.0.1 did not have any: it waited ~35 seconds, then failed 2.0.1's own release
1396
+ *after* the publish had succeeded — so `changesets/action` never received its
1397
+ output, and the tag and the GitHub release had to be made by hand.
1398
+
1399
+ - `confirmPublished` now waits **fifteen minutes**, backing off to 30s.
1400
+ - The `isPublished` skip path **tags and announces** instead of returning
1401
+ silently, so re-running a release whose publish landed late completes it.
1402
+ - `AGENTS.md` says to wait and re-run, never to release the next patch to get
1403
+ around a 409.
1404
+
1405
+ ## 2.0.1
1406
+
1407
+ ### Patch Changes
1408
+
1409
+ - [#293](https://github.com/softistx/nxgt-material/pull/293) [`e88d1a0`](https://github.com/softistx/nxgt-material/commit/e88d1a0798ffbe1b0edab43c1f18d68c700bc89d) Thanks [@SteveGT96](https://github.com/SteveGT96)! - A publish is only believed when the registry confirms it
1410
+
1411
+ `bun publish` exited 0 for 2.0.0, `scripts/publish.ts` printed `published`, the
1412
+ action pushed the tag and cut the GitHub release — and npmjs never received the
1413
+ version. Nothing in the log said otherwise, because the script printed bun's
1414
+ output only on the failure path.
1415
+
1416
+ It now re-reads the registry after publishing (six attempts over ~35s, so
1417
+ propagation is not mistaken for failure), **fails the release** if the version is
1418
+ not there, and tags only after that. bun's output is printed either way.
1419
+
1420
+ The cause, surfaced by that change on the re-run: npm answered
1421
+ `409 Conflict — Cannot publish over previously staged version "2.0.0"`. The first
1422
+ attempt staged the tarball and never completed it, and bun exited 0 anyway. A
1423
+ staged version cannot be republished, so 2.0.0 is burnt and this patch is what
1424
+ carries its content to the registry.
1425
+
1426
+ ## 2.0.0
1427
+
1428
+ ### Major Changes
1429
+
1430
+ - [#291](https://github.com/softistx/nxgt-material/pull/291) [`da81b72`](https://github.com/softistx/nxgt-material/commit/da81b720c5b0510a287562b36474187a3247f670) Thanks [@SteveGT96](https://github.com/SteveGT96)! - `@nxgt/material/print` is its own entry, and `@react-pdf/renderer` +
1431
+ `react-pdf-tailwind` are optional peer dependencies
1432
+
1433
+ **Breaking:** the print components (`PrintHeader`, `PrintSection`, `PrintCard`,
1434
+ `PrintTable`, `PrintTotals`, `PrintBadge`, `tw`, `commonPrintStyles`, the
1435
+ formatters) no longer come from `@nxgt/material` or `@nxgt/material/components`.
1436
+ Import them from `@nxgt/material/print`, and install `@react-pdf/renderer` and
1437
+ `react-pdf-tailwind` in the app that prints — they are optional peers now.
1438
+
1439
+ Why it is worth a major: `@react-pdf/renderer` reaches `pdfkit`, which registers
1440
+ its fourteen standard fonts through
1441
+ `createRequire(import.meta.url)('#standard-fonts/<name>')` — a specifier private
1442
+ to pdfkit, called at runtime, that no bundler resolves. Any SSR consumer that
1443
+ bundled this package therefore shipped a server which threw on **every request**,
1444
+ `/health` included; three did on 2026-09-22 and each worked around it locally. An
1445
+ app that does not render PDFs now never has pdfkit in its graph at all.
1446
+
1447
+ Also in this release, for the apps that do print:
1448
+
1449
+ - `@nxgt/material/vite` exports `pdfkitStandardFonts()`, the Vite `transform` that
1450
+ bundles those fonts — one implementation instead of one per consumer. Needed
1451
+ only when the runtime image ships the bundle without a `node_modules`.
1452
+ - `bun run build` fails if any entry but `print.js` reaches those two packages,
1453
+ transitively.
1454
+ - `"sideEffects": ["**/*.css"]` is declared. It does not fix the above (measured),
1455
+ it is simply true — and the CSS entry matters: a bare `false` lets a bundler drop
1456
+ `dist/material.css`.
1457
+
1458
+ ## 1.16.6
1459
+
1460
+ ### Patch Changes
1461
+
1462
+ - [#289](https://github.com/softistx/nxgt-material/pull/289) [`71e6c66`](https://github.com/softistx/nxgt-material/commit/71e6c669bbf89cda6b94f4b048ebff467f2e7a7a) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Stop depending on itself
1463
+
1464
+ `package.json` carried `"@nxgt/material": "."` in its own `dependencies`. It
1465
+ arrived incidentally in an i18n commit in May (`c5154f0`) and nothing in this
1466
+ repository ever used it: no file imports `@nxgt/material` by name, and
1467
+ self-referencing a package by its own `name` works through `exports` without a
1468
+ dependency entry anyway.
1469
+
1470
+ It was not inert in consumers. `bun pm pack` ships the line, so every install of
1471
+ `@nxgt/material` also resolved `@nxgt/material@file:.` — and `.` is the
1472
+ *consumer's* directory, not this package's. In one consumer's monorepo that
1473
+ materialised as a second `@nxgt/material` in `node_modules/.bun` reporting
1474
+ version `1.0.0`, which is the version of that monorepo's own root
1475
+ `package.json`, plus a permanent `"@nxgt/material/@nxgt/material":
1476
+ ["@nxgt/material@file:."]` entry in its `bun.lock` that no manifest declared and
1477
+ that `bun install` kept re-creating.
1478
+
1479
+ Nothing observably broke, because the bundled output uses relative imports and
1480
+ never resolved that alias. The reason to fix it is that it is a lie in a
1481
+ published manifest pointing at a directory this package does not own.
1482
+
1483
+ Verified: the packed manifest no longer lists itself, a fresh consumer install
1484
+ produces no `file:.` entry, the SSR-safety check passes on all 8 entries, and the
1485
+ suite is green (773 tests, 115 files).
1486
+
1487
+ ## 1.16.5
1488
+
1489
+ ### Patch Changes
1490
+
1491
+ - [#287](https://github.com/softistx/nxgt-material/pull/287) [`80a9af9`](https://github.com/softistx/nxgt-material/commit/80a9af9c2dfd418aa890f2555b8016f357576c70) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Fix `import '@nxgt/material'` throwing in any node process
1492
+
1493
+ Since the markdown and code-block editors arrived, the bundle carried two
1494
+ dependencies that touch the DOM at module scope — `document.createElement("i")`
1495
+ from `decode-named-character-reference` and `new DOMParser()` from
1496
+ `hast-util-from-html-isomorphic`. Both are isomorphic packages that answer the
1497
+ `browser` export condition with a DOM implementation, and Vite's library build
1498
+ resolves that condition by default, so the browser halves were bundled in.
1499
+
1500
+ The effect on a consumer is total and immediate: any app that bundles this
1501
+ package into its server build — every SSR consumer, which is what
1502
+ `ssr.noExternal: ['@nxgt/material']` means — dies at boot with
1503
+ `ReferenceError: document is not defined`, before rendering anything. It was
1504
+ found by nxgt-ory's `verify:artifacts`, which imports the published tarball in a
1505
+ bare runtime.
1506
+
1507
+ Both are now pinned to their neutral entries, which are the same decoders built
1508
+ on lookup tables rather than on the DOM and are correct in both environments.
1509
+ `bun run build` checks this from now on: `postbuild` imports all eight entries
1510
+ with no DOM present and fails if one throws.
1511
+
1512
+ ## 1.16.4
1513
+
1514
+ ### Patch Changes
1515
+
1516
+ - [#285](https://github.com/softistx/nxgt-material/pull/285) [`a4d838f`](https://github.com/softistx/nxgt-material/commit/a4d838feef44798ecdf4572f2244a40829418736) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Fit gallery lightbox media to the viewport instead of cropping it: tiles keep the uniform `object-cover` crop, while the lightbox renders the full media with `object-contain`.
1517
+
1518
+ ## 1.16.3
1519
+
1520
+ ### Patch Changes
1521
+
1522
+ - [#283](https://github.com/softistx/nxgt-material/pull/283) [`b9933f8`](https://github.com/softistx/nxgt-material/commit/b9933f8b8177521286e566e7ce70f51596ccbf10) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Crop gallery media to a uniform aspect and restyle carousel controls.
1523
+
1524
+ ## 1.16.2
1525
+
1526
+ ### Patch Changes
1527
+
1528
+ - [#281](https://github.com/softistx/nxgt-material/pull/281) [`4b21097`](https://github.com/softistx/nxgt-material/commit/4b210977d9a5fc5b3626de16d28a414a90c58a86) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Close the chat composer mention list when a suggestion is selected.
1529
+
1530
+ ## 1.16.1
1531
+
1532
+ ### Patch Changes
1533
+
1534
+ - [#279](https://github.com/softistx/nxgt-material/pull/279) [`053bea5`](https://github.com/softistx/nxgt-material/commit/053bea591e5d8941c20009cd38e06db75530f646) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Modernise the emoji picker and add stories for emoji, mentions, chat, markdown and the editor.
1535
+
1536
+ ## 1.16.0
1537
+
1538
+ ### Minor Changes
1539
+
1540
+ - [#277](https://github.com/softistx/nxgt-material/pull/277) [`8be1b8d`](https://github.com/softistx/nxgt-material/commit/8be1b8d32636b077bf0bd13e162ff499f285bb92) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add MentionList (items + local search) for chat and the rich text editor.
1541
+
1542
+ ## 1.15.0
1543
+
1544
+ ### Minor Changes
1545
+
1546
+ - [#276](https://github.com/softistx/nxgt-material/pull/276) [`b28c763`](https://github.com/softistx/nxgt-material/commit/b28c7636a741ae4150be16851252ea5656396cfa) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Extract EmojiPicker for chat, markdown shortcodes, and the rich text editor.
1547
+
1548
+ - [#274](https://github.com/softistx/nxgt-material/pull/274) [`47a9a1b`](https://github.com/softistx/nxgt-material/commit/47a9a1b742ef9701209831d897da17adb2b081b2) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add Terminal, a display-only prompt and log with copy.
1549
+
1550
+ ## 1.14.0
1551
+
1552
+ ### Minor Changes
1553
+
1554
+ - [#273](https://github.com/softistx/nxgt-material/pull/273) [`137a4b0`](https://github.com/softistx/nxgt-material/commit/137a4b09dfb98c668b7df40dd699f053012bc066) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add Markdown (GFM, KaTeX, mermaid) and route ChatMarkdown through it.
1555
+
1556
+ ### Patch Changes
1557
+
1558
+ - [#270](https://github.com/softistx/nxgt-material/pull/270) [`247b800`](https://github.com/softistx/nxgt-material/commit/247b800e7d93714ea571db387c96f5716676a214) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Make EntityHeader a container: stack on small cards, wrap actions, drop useIsMobile.
1559
+
1560
+ - [#271](https://github.com/softistx/nxgt-material/pull/271) [`961ec41`](https://github.com/softistx/nxgt-material/commit/961ec414b2c9c21a0b14f3573e7211a1c7707d15) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Kanban columns use bg-background and rounded; add a 390px Phone story.
1561
+
1562
+ ## 1.13.0
1563
+
1564
+ ### Minor Changes
1565
+
1566
+ - [#268](https://github.com/softistx/nxgt-material/pull/268) [`c1bebca`](https://github.com/softistx/nxgt-material/commit/c1bebca884282847f4e0c30845a21f73330a6fe9) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Rebuild Quiz and QuestionCard on Card, ActionCard, GaugeCard and ChecklistCard.
1567
+
1568
+ ## 1.12.3
1569
+
1570
+ ### Patch Changes
1571
+
1572
+ - [#266](https://github.com/softistx/nxgt-material/pull/266) [`f676791`](https://github.com/softistx/nxgt-material/commit/f676791f33ffa806cb6b988ca9331fa3c3cf9bc1) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Fix ChatInbox desktop layout: side-by-side from @md via a nested container.
1573
+
1574
+ ## 1.12.2
1575
+
1576
+ ### Patch Changes
1577
+
1578
+ - [#264](https://github.com/softistx/nxgt-material/pull/264) [`a4e62b1`](https://github.com/softistx/nxgt-material/commit/a4e62b1fa0999af0fdc3b15753118b8a7a39053f) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Drop bg-muted gallery wells and wrap lightbox chrome on small viewports.
1579
+
1580
+ ## 1.12.1
1581
+
1582
+ ### Patch Changes
1583
+
1584
+ - [#262](https://github.com/softistx/nxgt-material/pull/262) [`c11a657`](https://github.com/softistx/nxgt-material/commit/c11a6573a951c4e43506175b03137902e0c010e3) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Let the PDF toolbar wrap on small containers instead of overflowing a 48px bar.
1585
+
1586
+ - [#260](https://github.com/softistx/nxgt-material/pull/260) [`0ffc029`](https://github.com/softistx/nxgt-material/commit/0ffc029b7ef0fe3edd94195daa5cdb4b9bbb1606) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Make Quiz container-query responsive and show results on a GaugeCard.
1587
+
1588
+ ## 1.12.0
1589
+
1590
+ ### Minor Changes
1591
+
1592
+ - [#259](https://github.com/softistx/nxgt-material/pull/259) [`bd39a39`](https://github.com/softistx/nxgt-material/commit/bd39a39657428e329e26af45ebb8f5a87065aca0) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add ChatInbox (list + thread, container queries) and fix assistant bubbles in dark.
1593
+
1594
+ ## 1.11.0
1595
+
1596
+ ### Minor Changes
1597
+
1598
+ - [#257](https://github.com/softistx/nxgt-material/pull/257) [`32d48a0`](https://github.com/softistx/nxgt-material/commit/32d48a0a13c161728fc97042a5cf0d31eb11c603) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add EmptyState offline, search and maintenance presets, and drop bg-muted on not-found.
1599
+
1600
+ ## 1.10.0
1601
+
1602
+ ### Minor Changes
1603
+
1604
+ - [#255](https://github.com/softistx/nxgt-material/pull/255) [`8c5e0df`](https://github.com/softistx/nxgt-material/commit/8c5e0dfcc9574c3cf14d3cc52634eb5eb00647d1) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add ScoreCard, ShortcutCard, ChecklistCard, PaceCard and HistogramCard presets.
1605
+
1606
+ ## 1.9.0
1607
+
1608
+ ### Minor Changes
1609
+
1610
+ - [#253](https://github.com/softistx/nxgt-material/pull/253) [`ccdb1e8`](https://github.com/softistx/nxgt-material/commit/ccdb1e8d1d2405d32a7a71f81bb84362a4301b1a) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add PlaceCard, FileCard, StatusCard, RatioCard and ThresholdCard presets.
1611
+
1612
+ ## 1.8.0
1613
+
1614
+ ### Minor Changes
1615
+
1616
+ - [#251](https://github.com/softistx/nxgt-material/pull/251) [`bee0cd2`](https://github.com/softistx/nxgt-material/commit/bee0cd2163240ffcc258f42081befeca137a89d5) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add ReceiptCard, ReviewCard, OfferCard, BandCard and LatencyCard presets.
1617
+
1618
+ ## 1.7.0
1619
+
1620
+ ### Minor Changes
1621
+
1622
+ - [#250](https://github.com/softistx/nxgt-material/pull/250) [`eadfcf5`](https://github.com/softistx/nxgt-material/commit/eadfcf5fb8e9f9d1110573638d72fc3674ad3fba) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add AlbumCard, TagCard and DeltaListCard presets.
1623
+
1624
+ - [#248](https://github.com/softistx/nxgt-material/pull/248) [`1fb8e29`](https://github.com/softistx/nxgt-material/commit/1fb8e29ff35bbab37d17007212f06f951f8a85eb) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add LinkCard and SparkbarCard presets.
1625
+
1626
+ ## 1.6.0
1627
+
1628
+ ### Minor Changes
1629
+
1630
+ - [#246](https://github.com/softistx/nxgt-material/pull/246) [`66bb2bd`](https://github.com/softistx/nxgt-material/commit/66bb2bdfea35f91cdf8f29383905f000436a2036) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add EventCard and StackCard presets.
1631
+
1632
+ ## 1.5.0
1633
+
1634
+ ### Minor Changes
1635
+
1636
+ - [#244](https://github.com/softistx/nxgt-material/pull/244) [`dd75c1e`](https://github.com/softistx/nxgt-material/commit/dd75c1efd537b6c45d94b8bcad57dfdf7820cb90) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add MessageCard and GaugeCard presets.
1637
+
1638
+ ## 1.4.0
1639
+
1640
+ ### Minor Changes
1641
+
1642
+ - [#243](https://github.com/softistx/nxgt-material/pull/243) [`ee2aefd`](https://github.com/softistx/nxgt-material/commit/ee2aefd9699af8a07a1cfad44b34d19384b07a57) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add TimelineCard and FunnelCard presets.
1643
+
1644
+ ### Patch Changes
1645
+
1646
+ - [#240](https://github.com/softistx/nxgt-material/pull/240) [`23fe4a7`](https://github.com/softistx/nxgt-material/commit/23fe4a7ebe27c20a08001d6bedc4b7e3e3115637) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Tree rows are full-width rounded bars, including indent.
1647
+
1648
+ ## 1.3.0
1649
+
1650
+ ### Minor Changes
1651
+
1652
+ - [#239](https://github.com/softistx/nxgt-material/pull/239) [`13106c9`](https://github.com/softistx/nxgt-material/commit/13106c9c635964780f8ae6b67d6b2eaee9055f69) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add CompareCard and HeatweekCard dashboard tiles.
1653
+
1654
+ ## 1.2.0
1655
+
1656
+ ### Minor Changes
1657
+
1658
+ - [#236](https://github.com/softistx/nxgt-material/pull/236) [`b52b78b`](https://github.com/softistx/nxgt-material/commit/b52b78b7fb31dff4b8a4ca5e84101f34b4a4b1ad) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add SplitCard, TicketCard and NoticeCard presets.
1659
+
1660
+ ### Patch Changes
1661
+
1662
+ - [#237](https://github.com/softistx/nxgt-material/pull/237) [`af768bb`](https://github.com/softistx/nxgt-material/commit/af768bbb5fc29d574e9aeb6131d41bc5f29dd7ea) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Tree rows are rounded pills that hug the label; indent is empty gutter, not a full-width bar.
1663
+
1664
+ ## 1.1.0
1665
+
1666
+ ### Minor Changes
1667
+
1668
+ - [#209](https://github.com/softistx/nxgt-material/pull/209) [`830c7b1`](https://github.com/softistx/nxgt-material/commit/830c7b1e0653132366f05d4a6fb51393839c4847) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add ApiOperation: method + path, optional ParamTable, optional example CodeGroup.
1669
+
1670
+ - [#235](https://github.com/softistx/nxgt-material/pull/235) [`4c7dbcd`](https://github.com/softistx/nxgt-material/commit/4c7dbcd7abd398621b53068b98c44945ae88f14c) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add OverlayCard, SpotlightCard and CrewCard presets.
1671
+
1672
+ - [#230](https://github.com/softistx/nxgt-material/pull/230) [`24b870d`](https://github.com/softistx/nxgt-material/commit/24b870daf6f10219aa0d058e479ae780ecf523c6) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add FeatureCard and MediaCard presets (responsive, compose Card).
1673
+
1674
+ - [#231](https://github.com/softistx/nxgt-material/pull/231) [`caa16e7`](https://github.com/softistx/nxgt-material/commit/caa16e70dd391024bdcba43b653abe067cb2b79c) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add PricingCard and ProfileCard presets.
1675
+
1676
+ - [#232](https://github.com/softistx/nxgt-material/pull/232) [`e5c5988`](https://github.com/softistx/nxgt-material/commit/e5c598839bc3696e9b219aa495396717e97d8ccb) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add QuoteCard and a responsive UI/Cards Gallery story.
1677
+
1678
+ - [#205](https://github.com/softistx/nxgt-material/pull/205) [`ddb6f70`](https://github.com/softistx/nxgt-material/commit/ddb6f70e3fa45459804acb3b2405efdf29a20951) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add ParamTable, AnchorHeading, a reading-progress bar, copy-page on DocHeader, and DocChildCards for section landings.
1679
+
1680
+ - [#201](https://github.com/softistx/nxgt-material/pull/201) [`d9756c5`](https://github.com/softistx/nxgt-material/commit/d9756c5f716f7ea011e88cd14cb229095c7d95e8) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Documentation site shell (`Doc`) that reuses `ListTile`, `Card`, `Tabs`, `Alert` and `ActionCard`. Adds `Steps`, `Hero`, `Figure`, a `line` Tabs variant and a `sm` ListTile size.
1681
+
1682
+ - [#206](https://github.com/softistx/nxgt-material/pull/206) [`8fbf1e2`](https://github.com/softistx/nxgt-material/commit/8fbf1e2c0f647a9e34258f1c44e1eac168efd580) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Doc sidebar can filter pages in place. `[` / `]` move to the previous / next page. Skip-to-content link and hash restore after navigation.
1683
+
1684
+ - [#208](https://github.com/softistx/nxgt-material/pull/208) [`a814c77`](https://github.com/softistx/nxgt-material/commit/a814c777fea5908ad2f05d34a5d1e0168587fee2) Thanks [@SteveGT96](https://github.com/SteveGT96)! - DocPage lists sibling pages in a See also block before the pager.
1685
+
1686
+ - [#214](https://github.com/softistx/nxgt-material/pull/214) [`7789a73`](https://github.com/softistx/nxgt-material/commit/7789a7355a0754c78461c0b9d74b7dd25c2c6230) Thanks [@SteveGT96](https://github.com/SteveGT96)! - EmptyState uses a tinted icon well and tighter type. DocEmpty is the docs 404/empty slot.
1687
+
1688
+ - [#229](https://github.com/softistx/nxgt-material/pull/229) [`8e1bf7d`](https://github.com/softistx/nxgt-material/commit/8e1bf7d1c8c5534a4a932d74207844e77cc49cda) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add useHeadingSpy. Doc chrome uses it for the on-this-page active heading.
1689
+
1690
+ - [#228](https://github.com/softistx/nxgt-material/pull/228) [`4b62083`](https://github.com/softistx/nxgt-material/commit/4b620831172430c5ff286518330ab2bed6702109) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add HighlightText and splitMatch. Doc search hits highlight the query.
1691
+
1692
+ - [#234](https://github.com/softistx/nxgt-material/pull/234) [`8226c91`](https://github.com/softistx/nxgt-material/commit/8226c9138ceccb3cde8065c47c68e65abe148433) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add BreakdownCard and RankCard, plus a UI/Metrics Dashboard gallery.
1693
+
1694
+ - [#233](https://github.com/softistx/nxgt-material/pull/233) [`f2433a8`](https://github.com/softistx/nxgt-material/commit/f2433a8a3df0f841bba9efedfbf6e1166f86418f) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add TrendCard and GoalCard dashboard tiles (metrics folder).
1695
+
1696
+ - [#222](https://github.com/softistx/nxgt-material/pull/222) [`bae7c1a`](https://github.com/softistx/nxgt-material/commit/bae7c1a4344dc3922c2f0876112cd2e2063c511e) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Extract Pager from the doc shell. isExternalHref lives on @nxgt/material/lib.
1697
+
1698
+ - [#221](https://github.com/softistx/nxgt-material/pull/221) [`c963673`](https://github.com/softistx/nxgt-material/commit/c9636733d5bee69de876b686c1f80f7cfe0c2bc9) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Extract ScrollProgress and SkipLink from the doc shell. Scroll chrome uses react-use (useScroll / useWindowScroll).
1699
+
1700
+ - [#224](https://github.com/softistx/nxgt-material/pull/224) [`89e0162`](https://github.com/softistx/nxgt-material/commit/89e01627f142b1e4272414ffdbad41095ddddd3b) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Extract SeeAlso from the doc shell. Rows use a dark-safe hover, not bg-muted.
1701
+
1702
+ - [#223](https://github.com/softistx/nxgt-material/pull/223) [`58df191`](https://github.com/softistx/nxgt-material/commit/58df191e660e1f56d2628af37b956838d8b6b1d3) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Extract Toc from the doc shell. DocToc composes it with DocLink and the active heading.
1703
+
1704
+ ### Patch Changes
1705
+
1706
+ - [#225](https://github.com/softistx/nxgt-material/pull/225) [`17b0178`](https://github.com/softistx/nxgt-material/commit/17b01785366c4513ae6d5c8035470e5915906f01) Thanks [@SteveGT96](https://github.com/SteveGT96)! - CopyButton uses react-use (useCopyToClipboard, useTimeoutFn). Doc copy actions compose it.
1707
+
1708
+ - [#217](https://github.com/softistx/nxgt-material/pull/217) [`1f652e1`](https://github.com/softistx/nxgt-material/commit/1f652e1c64814efb1a248059d43331875dfebfb9) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Document the Storybook UI/Doc/Api recipe: DocPage with AnchorHeading, ApiOperation, ParamTable and CodeGroup.
1709
+
1710
+ - [#203](https://github.com/softistx/nxgt-material/pull/203) [`16956bb`](https://github.com/softistx/nxgt-material/commit/16956bb6120a13bfeee088835baad3ff43cb7e22) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Lift `CodeGroup` and `ApiEndpoint` out of `doc/` as generic components. The API path stays readable in dark mode. The Doc sidebar sits on the left edge instead of a centred max-width row.
1711
+
1712
+ - [#207](https://github.com/softistx/nxgt-material/pull/207) [`6fbd0cc`](https://github.com/softistx/nxgt-material/commit/6fbd0cc8c97a5a0559edcaf232d6a79ed744d3f6) Thanks [@SteveGT96](https://github.com/SteveGT96)! - DocHeader can copy the page as markdown (`markdown` prop, or the article text as a fallback).
1713
+
1714
+ - [#220](https://github.com/softistx/nxgt-material/pull/220) [`066430a`](https://github.com/softistx/nxgt-material/commit/066430a72edd0d3b80ea921cb75a58df993aafc9) Thanks [@SteveGT96](https://github.com/SteveGT96)! - DocHeader editHref shows a pencil and an outbound icon on external links.
1715
+
1716
+ - [#219](https://github.com/softistx/nxgt-material/pull/219) [`f9ccbbf`](https://github.com/softistx/nxgt-material/commit/f9ccbbfe995b511af2ce9b3de7baba81c248b3bd) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Sidebar filter empty uses a compact well, and nav icon wells use bg-background.
1717
+
1718
+ - [#216](https://github.com/softistx/nxgt-material/pull/216) [`f284e1b`](https://github.com/softistx/nxgt-material/commit/f284e1b9323570d7fb7c82f597ae329ba1128351) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Pager and See also show an outbound icon on external hrefs, same as the sidebar.
1719
+
1720
+ - [#218](https://github.com/softistx/nxgt-material/pull/218) [`1eeee15`](https://github.com/softistx/nxgt-material/commit/1eeee156eaca92abe3525731ef4f18bc4376c7ee) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Doc search hits are airier: icon well, snippet, outbound icon, compact empty, and a kbd footer.
1721
+
1722
+ - [#212](https://github.com/softistx/nxgt-material/pull/212) [`7bf7c83`](https://github.com/softistx/nxgt-material/commit/7bf7c83b3f5db2c4c94bee16d416bfa7f09f90f2) Thanks [@SteveGT96](https://github.com/SteveGT96)! - On this page: per-item primary marker (transparent when idle), active row bg-primary/10 and text-primary.
1723
+
1724
+ - [#213](https://github.com/softistx/nxgt-material/pull/213) [`b74623e`](https://github.com/softistx/nxgt-material/commit/b74623e33e1611ace3c7c5be7d5d4a0e87613140) Thanks [@SteveGT96](https://github.com/SteveGT96)! - On this page uses `rounded` on the rail panel, items, and marker.
1725
+
1726
+ - [#210](https://github.com/softistx/nxgt-material/pull/210) [`633efc1`](https://github.com/softistx/nxgt-material/commit/633efc1503d37d3aa39fe90619fc89a8e8809150) Thanks [@SteveGT96](https://github.com/SteveGT96)! - On this page items each have a left bar (primary when active) so the indicator stays on the text. Search hits show descriptions; pager cards show `[` / `]`.
1727
+
1728
+ - [#227](https://github.com/softistx/nxgt-material/pull/227) [`7184407`](https://github.com/softistx/nxgt-material/commit/7184407d405e9d99c1ac03f1fb554c4d88a533e6) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Doc topbar search chip uses bg-background and rounded, not bg-muted.
1729
+
1730
+ - [#215](https://github.com/softistx/nxgt-material/pull/215) [`2d33c38`](https://github.com/softistx/nxgt-material/commit/2d33c38cbd469c703cb975eccb5849b74dba7f5c) Thanks [@SteveGT96](https://github.com/SteveGT96)! - DocHeader accepts a Date for `updatedAt` and renders it with RelativeTime.
1731
+
1732
+ - [#226](https://github.com/softistx/nxgt-material/pull/226) [`10084b5`](https://github.com/softistx/nxgt-material/commit/10084b51f80e7def1c8e7397b494a9f75b10c4c9) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Doc chrome uses react-use for keydown (useEvent) and the md breakpoint (useMedia).
1733
+
1734
+ - [#204](https://github.com/softistx/nxgt-material/pull/204) [`768e733`](https://github.com/softistx/nxgt-material/commit/768e733630efed344b03451c1482604fd5b2f5eb) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Repo-sync now covers adding and improving catalog widgets in `@nxgt/material-vue`. README Vue-equivalent column for Doc, CodeGroup, ApiEndpoint, Steps, Hero and Figure.
1735
+
1736
+ - [#211](https://github.com/softistx/nxgt-material/pull/211) [`3d8ed8d`](https://github.com/softistx/nxgt-material/commit/3d8ed8dd09f23588792481684463a54f61b8de59) Thanks [@SteveGT96](https://github.com/SteveGT96)! - ThemeToggle and ToggleGroup use bg-background so dark-mode icons stay visible.
1737
+
1738
+ ## 1.0.5
1739
+
1740
+ ### Patch Changes
1741
+
1742
+ - [#198](https://github.com/softistx/nxgt-material/pull/198) [`babed08`](https://github.com/softistx/nxgt-material/commit/babed08afe185a3cfdec2fb3685c6c9f97674886) Thanks [@SteveGT96](https://github.com/SteveGT96)! - README: "At a glance" overview, table of contents, a grouped component catalogue with the `@nxgt/material-vue` equivalent of each entry, and a helpers reference for `/lib`, `/hooks`, `/i18n`, `/models`, `/types` and `/dnd`.
1743
+
1744
+ - [#198](https://github.com/softistx/nxgt-material/pull/198) [`babed08`](https://github.com/softistx/nxgt-material/commit/babed08afe185a3cfdec2fb3685c6c9f97674886) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Export `UploadFormField` from `@nxgt/material/components`. `AppSidebar`'s default brand is the icon alone. Licensed under MIT.
1745
+
1746
+ ## 1.0.4
1747
+
1748
+ ### Patch Changes
1749
+
1750
+ - [#196](https://github.com/softistx/nxgt-material/pull/196) [`92b06b6`](https://github.com/softistx/nxgt-material/commit/92b06b66bac86f72e37688e93639347ef6107b25) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Keep the `ActionCard` icon box square and cap its height (`max-h-16 aspect-square`) so tall content no longer stretches it.
1751
+
1752
+ ## 1.0.3
1753
+
1754
+ ### Patch Changes
1755
+
1756
+ - [#195](https://github.com/softistx/nxgt-material/pull/195) [`732f5da`](https://github.com/softistx/nxgt-material/commit/732f5da8dac7258c17f9704a126185618bdc06af) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Document that `styles.css` must be imported explicitly in the Tailwind entry (single `@theme` graph, Tailwind CSS IntelliSense), and that the compiled `style` condition (`dist/material.css`) is for apps without Tailwind.
1757
+
1758
+ - [#192](https://github.com/softistx/nxgt-material/pull/192) [`3f459a3`](https://github.com/softistx/nxgt-material/commit/3f459a3e85df5042ba50e471e88040820e103908) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Add fill-available size utilities and stretch the split-button chevron to the control height.
1759
+
1760
+ - [#194](https://github.com/softistx/nxgt-material/pull/194) [`a7e957d`](https://github.com/softistx/nxgt-material/commit/a7e957d119582cc551641d694eb5c5bfc6f5fd36) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Export styles.css from lib/ and copy any relative CSS imports into dist on postbuild.
1761
+
1762
+ ## 1.0.2
1763
+
1764
+ ### Patch Changes
1765
+
1766
+ - [#190](https://github.com/softistx/nxgt-material/pull/190) [`17c171d`](https://github.com/softistx/nxgt-material/commit/17c171daaad4f596ab4f9748dbdea5baa1c25348) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Rewrite the npm README for a developer who does not know the private apps.
1767
+
1768
+ Install once, setup (CSS + sprites) as snippets, then usage by area — each
1769
+ with a copy-paste example. Private application names are gone from this
1770
+ page; they stay in AGENTS.md.
1771
+
1772
+ ## 1.0.1
1773
+
1774
+ ### Patch Changes
1775
+
1776
+ - [#188](https://github.com/softistx/nxgt-material/pull/188) [`65f7f8d`](https://github.com/softistx/nxgt-material/commit/65f7f8d991f9a3455e0be2f37502d2603ddc90b4) Thanks [@SteveGT96](https://github.com/SteveGT96)! - Complete the npm README.
1777
+
1778
+ Install, every `exports` subpath, the required peers, i18n wiring, the
1779
+ parameterised app shell, and the SSR traps (`clientOnly`, no `<svg><title>`)
1780
+ now live on the page a consumer reads. The CSS `@source` line and the Font
1781
+ Awesome Pro sprite rule were already there and stay.