@nxgt/material 2.6.0 → 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,1759 @@
1
+ # @nxgt/material
2
+
3
+ ## 2.6.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#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
8
+ `softistx/nxgt-material` repository is now a Claude Code plugin marketplace
9
+ for apps that use the libraries — `nxgt-material` for React apps,
10
+ `nxgt-material-vue` for Vue and Nuxt apps — each with a skill that reads the
11
+ installed package's docs, a screen-builder agent, an upgrade-assistant agent
12
+ and an advisory hook that names the component a file hand-rolls. Install
13
+ with `/plugin marketplace add softistx/nxgt-material`, then
14
+ `/plugin install nxgt-material@nxgt-material` (or
15
+ `nxgt-material-vue@nxgt-material`); the repository is private, so this needs
16
+ GitHub access. Nothing in the package itself changed.
17
+
18
+ - [#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
19
+ version are in `node_modules/<package>/CHANGELOG.md` next to the docs, for
20
+ reading offline and for the upgrade agents of the Claude Code plugins.
21
+ The getting-started pages of `@nxgt/material` and `@nxgt/material-vue`
22
+ list it among the tarball's contents.
23
+ - Updated dependencies [[`13c3eda`](https://github.com/softistx/nxgt-material/commit/13c3edad01f08656ca196aca7c37a21e8c003466)]:
24
+ - @nxgt/material-core@0.3.2
25
+
26
+ ## 2.6.1
27
+
28
+ ### Patch Changes
29
+
30
+ - [#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`.
31
+ - Updated dependencies [[`d4e953f`](https://github.com/softistx/nxgt-material/commit/d4e953fd4fc147b366c5d9d70ebfb1aa3a3c0ba7)]:
32
+ - @nxgt/material-core@0.3.1
33
+
34
+ ## 2.6.0
35
+
36
+ ### Minor Changes
37
+
38
+ - [#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`.
39
+ `presets` lists one-click ranges beside the calendar: built-in ids
40
+ (`DATE_RANGE_PRESET_IDS`: `today`, `yesterday`, `last-7-days`,
41
+ `last-30-days`, `this-month`, `last-month`, `this-year`) and
42
+ `{ label, range }` of your own, where `range` may be `(now) => range`. A
43
+ preset is a toggle button in a group named "Presets", pressed when its days
44
+ are the value's, and disabled when its range starts before `minDate`, ends
45
+ after `maxDate`, either end is one of `disabledDays`, or (a preset of your
46
+ own) its end comes before its start. Picking one closes the popover, except
47
+ with `withTime`, where OK closes it. `withTime` adds a start and an end `TimePicker` under the
48
+ calendar, each disabled until its end has a day, and an OK button; the
49
+ popover then stays open until OK, the trigger shows each end's time, a day picked on the calendar keeps the
50
+ time its end had (a new start 00:00, a new end 23:59, as the built-in
51
+ presets end, so the range covers its last day), and an end set before the
52
+ start on the same day becomes the start time. The built-in presets use the
53
+ current date (there is no `now` prop).
54
+ `DATE_RANGE_PRESET_IDS` and the types `DateRangePreset`, `DateRangePresetId`,
55
+ `DateRangePresetOption` and `DayRange` are exported from
56
+ `@nxgt/material/components` (from `@nxgt/material-core/date-range`).
57
+
58
+ New `material.*` keys: `material.date-range-field.presets`,
59
+ `material.date-range-field.preset.today`, `.yesterday`, `.last-7-days`,
60
+ `.last-30-days`, `.this-month`, `.last-month`, `.this-year`,
61
+ `material.date-range-field.start-time`, `material.date-range-field.end-time`,
62
+ `material.time-picker.hours` and `material.time-picker.minutes`.
63
+
64
+ `TimePicker` fix: AM / PM now follows the `date` it is given, so a date
65
+ changed from outside shows its own half of the day; switching AM / PM always
66
+ moves the time into that half (it could add 12 hours twice before), and a
67
+ click on the pressed half changes nothing. Its hours and minutes inputs are
68
+ named "Hours" and "Minutes".
69
+
70
+ - [#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
71
+ its extension. `file` is a `File`, a `Blob` or a URL, with optional `type`
72
+ and `name` overriding what the file says. An image is an `<img>` whose alt
73
+ text is the file name; a PDF opens in `PdfReader`, imported the first time a
74
+ PDF is previewed, and falls back to the file card when the viewer cannot be
75
+ loaded; a video or audio file gets the browser's `<video controls>` /
76
+ `<audio controls>`; a text, JSON, CSV or Markdown file is read up to
77
+ `maxTextBytes` (256 KB by default) and shown in a scrolling `<pre>`, JSON in
78
+ `JsonViewer` and Markdown rendered, with "Showing the first … of this file."
79
+ when it was cut; anything else is a `FileCard` with its size and type and a
80
+ Download link. A text is decoded with the `charset` of the response's
81
+ `Content-Type` when the browser supports it, else as UTF-8. A URL must be
82
+ `http(s):`, relative, `blob:`, or a `data:` URL of an image, video, audio, plain text, CSV, Markdown, JSON or PDF: any
83
+ other (`javascript:`, `data:text/html`, and also a `data:` URL of XML, TSV or
84
+ YAML) is never linked, loaded or fetched, and shows the error state with no
85
+ 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
86
+ its own error. The scrolling text and Markdown are regions named "Contents
87
+ of {name}", and "Loading preview…" is announced from a live region that
88
+ stays mounted. A `File` or `Blob` is shown through an object URL, revoked on
89
+ change and on unmount. On the server, and in the first client render, it
90
+ renders an empty placeholder (`data-state="pending"`): no object URL, no
91
+ fetch, nothing to mismatch on hydration. Then `loading`, `ready` or `error`
92
+ ("This file could not be previewed.", with a Download link), and `onError`
93
+ receives the error. Also exported from `@nxgt/material/components`:
94
+ `fileKind`, `FilePreviewHttpError`, `FilePreviewUnsafeUrlError`,
95
+ `TEXT_PREVIEW_MAX_BYTES` and the types
96
+ `FileKind`, `FilePreviewSource`, `FilePreviewProps` and `FilePreviewStatus`
97
+ (from `@nxgt/material-core/file-preview`).
98
+
99
+ New `material.*` keys: `material.file-preview.label`,
100
+ `material.file-preview.untitled`, `material.file-preview.loading`,
101
+ `material.file-preview.error`, `material.file-preview.download`,
102
+ `material.file-preview.truncated` and `material.file-preview.content`.
103
+
104
+ `useImagePreview` (the upload field's, image field's, file list's and chat
105
+ chip's image thumbnail) no longer hands out a revoked URL for the render
106
+ between a file change and its effect: neither the previous file's, nor, when
107
+ a file is cleared and the same one picked again, that file's old one.
108
+
109
+ - [#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.
110
+
111
+ - `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.
112
+ - 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}".
113
+ - `searchable` adds a filter above the tree; each match stays under its ancestors, which open.
114
+ - 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).
115
+ - 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.
116
+ - `TreeSelectFormField` binds it to react-hook-form (`control`, `name`, `multiple`) or vee-validate (`name`, `multiple`), touched when focus leaves the trigger and its popover.
117
+ - New copy under `material.tree-select-field.*` (`placeholder`, `search`, `empty`, `selected`), in English and French.
118
+
119
+ **`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`.
120
+
121
+ ### Patch Changes
122
+
123
+ - [#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
124
+ range, now say what react-day-picker 10 does — the first day picked is both
125
+ `from` and `to` until the second. The `WithTime` story picks two days and
126
+ checks they start at 12:00 AM and end at 11:59 PM.
127
+
128
+ - [#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`,
129
+ `next`, `page` and `more` (`@nxgt/material`); `InlineEdit` says where focus
130
+ goes when the editor closes, and its Vue props table lists `label` and
131
+ `name`; the Vue `DocLink` paragraph says that without `@navigate` it and the
132
+ sidebar are plain anchors that load the page.
133
+ The Vue i18n guide also names `material.slider.value`, `minimum` and
134
+ `maximum`, which label the slider readout.
135
+
136
+ - [#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
137
+ slow device. The placement scroll could land after the scroll spy's hold had
138
+ run out, and near the end of the document it read as the last page. While
139
+ the view stays where the opening page's pin put it (that page at the top,
140
+ or the page area at its end with that page not above the top), a scroll is the reader's own and is not
141
+ reported; a scroll that moves the view elsewhere — the user's, find in page,
142
+ a screen reader's cursor — lets go of the pin and is reported. A wheel,
143
+ touch, pointer or key in the page area also ends the hold on the reader's
144
+ own scroll, so the scrolling after it is reported at once. In
145
+ `@nxgt/material-core/pdf-viewer`, `pinToPage` takes an optional `onRelease`,
146
+ called once when the pin lets go, and `pinHolds(node)` tells whether the view
147
+ is still where the pin puts it.
148
+
149
+ - [#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.
150
+
151
+ - [#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
152
+ thumb is — your `aria-labelledby`, else your `aria-label`, else the label —
153
+ and "Value" (`material.slider.value`, en and fr) when nothing names the
154
+ slider. `NumberField` takes an `aria-labelledby`, for an input that is a
155
+ part of a larger field. If you override the bundles or ship another
156
+ language, translate the new key.
157
+
158
+ - [#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:
159
+
160
+ - `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.
161
+ - `FilesFormField`: a free-solo autocomplete of file URLs bound to vee-validate, with image thumbnails (`image`, `image-link-builder`).
162
+ - `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).
163
+ - `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.
164
+ - `SliderField` `ticks`, `leading` and `trailing` (props, or `#leading` / `#trailing` slots).
165
+ - `Avatar` `status` and `status-pulse`: a presence dot named Online, Away, Busy, Offline or Active.
166
+ - `QRCode` `#logo` slot (the default slot still works).
167
+ - `AccordionCard` `#header`, `#title` and `#content` slots.
168
+ - `SelectField` `#option` slot.
169
+ - `CodeEditor` `options`: Monaco's own construction options, passed through (`CodeEditorOptions` is exported). Monaco stays client-only.
170
+ - `Tabs` `variant="line"` (`TabsVariant` is exported).
171
+
172
+ `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.
173
+
174
+ `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.
175
+
176
+ `@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.
177
+ - 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)]:
178
+ - @nxgt/material-core@0.3.0
179
+
180
+ ## 2.5.0
181
+
182
+ ### Minor Changes
183
+
184
+ - [#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
185
+ (`Skeleton`), one cell per visible column, as many as the page size or
186
+ `loadingRows`; with rows already shown, they stay in place, dimmed and
187
+ `inert`, so a refresh does not move the page. The table is `aria-busy` and a
188
+ visually hidden status says "Loading data…" (`material.table.loading`, en and
189
+ fr: translate it if you override the bundles or ship another language).
190
+ Sorting (`DataTableColumnHeader`), the `select` column's "Select all", the
191
+ toolbar's native controls and pagination (`DataTablePagination`'s new
192
+ `disabled`) are disabled meanwhile; the toolbar is now a `<fieldset>`.
193
+
194
+ An `empty` prop (a node, or `({ filtered }) => node`) replaces the "No data
195
+ available" row, inside a `TableEmpty` spanning every visible column;
196
+ `filtered` is `true` when rows exist but none matches the column filters. The
197
+ default empty row is a `TableEmpty` too, and spans the visible columns rather
198
+ than every defined one, as an expanded row now does. An `empty` that renders
199
+ nothing gets the default. New types: `DataTableEmpty`, `DataTableEmptyState`.
200
+
201
+ The table body's `max-h-[300px] overflow-auto` is gone: a table row group is
202
+ neither clipped nor scrolled, so it never had an effect (the wrapper's
203
+ `max-h-[400px]` still scrolls the table).
204
+
205
+ - [#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:
206
+
207
+ - **`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.
208
+ - **`NativeSelect`**, `NativeSelectOption` and `NativeSelectOptGroup`: the browser's own `<select>`, styled; an invalid one uses the `error` colour.
209
+ - **`InputOTP`**, `InputOTPGroup`, `InputOTPSlot` and `InputOTPSeparator`, the parts `OtpField` is built from. `InputOTPSeparator` now has `role="separator"` and takes children.
210
+ - **`PaginationFirst`** and **`PaginationLast`** (double chevrons), **`TableEmpty`**, **`DialogScrollContent`**, and the **`ToastAction`** type.
211
+ - **`Button` `size`**: `xs`, `sm`, `default`, `lg`, and the square `icon-xs`, `icon-sm`, `icon` and `icon-lg`. A button without `size` renders as before.
212
+ - **`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.
213
+
214
+ `@nxgt/material-vue`:
215
+
216
+ - An `icon*` `Button` holding an icon is square again: the base `has-[>svg]:px-3` padding no longer survives the icon sizes.
217
+ - An invalid `NativeSelect` uses the library's `error` colour instead of `destructive`.
218
+ - `PaginationFirst` and `PaginationLast` show double chevrons, so they no longer look like Previous and Next when their text is hidden on a narrow screen.
219
+ - Its README and component pages name the new React counterparts.
220
+
221
+ ### Patch Changes
222
+
223
+ - [#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
224
+ alert instead of overflowing it, and the icon keeps its size. The text
225
+ column now shrinks (`min-w-0`) and breaks long words, as `Banner`'s text
226
+ column does, and the icon sits in a `shrink-0` wrapper.
227
+
228
+ - [#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` /
229
+ `aria-labelledby`) is now `aria-hidden`, so the button is announced "Add"
230
+ instead of "Icon plus Add". A `Button` holding only an `Icon` still reads it,
231
+ and so does one whose only other text is `aria-hidden` (a decorative count).
232
+ If a test of yours found such a button by "Icon {name} …", select it by its
233
+ text — see "A button named "Icon plus Add" is now named "Add"" in
234
+ `docs/troubleshooting.md`.
235
+
236
+ - [#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
237
+ `InlineEdit`:
238
+
239
+ - `NumberField`: without an `id`, its `label` named nothing — the input had
240
+ no accessible name and `getByLabelText` could not find it. The input now
241
+ gets a generated id (React's `useId`) when you pass none.
242
+ - `InlineEdit`: closing the editor with Enter, Escape, Save or Cancel put
243
+ focus on `<body>`; it now returns to the field. Clicking elsewhere still
244
+ saves and leaves focus where you clicked.
245
+
246
+ - [#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`.
247
+
248
+ - `ListTile`: a long title or an unbroken URL wraps inside the tile instead
249
+ of scrolling sideways, and the `leading` / `trailing` pieces keep their
250
+ size (a trailing chevron was squeezed to a sliver).
251
+ - `StatCard`: a long label, value or hint wraps inside the card instead of
252
+ running out of it; the icon and the delta keep their size.
253
+ - `Banner`: a long title or description wraps instead of scrolling sideways;
254
+ the icon keeps its size.
255
+ - `Stepper`: a long step title or label truncates with an ellipsis instead of
256
+ scrolling inside its step.
257
+
258
+ - [#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
259
+ (an element), the link's `aria-label` fell back to English "Previous" /
260
+ "Next" whatever the language. It now uses the translated
261
+ `material.pagination.previous` / `next`, as `@nxgt/material-vue` does. The
262
+ new `PaginationFirst` / `PaginationLast` use `material.pagination.first` /
263
+ `last` the same way.
264
+
265
+ - [#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
266
+ (and described by its description and error), so two range filters no longer
267
+ read as "Min", "Max", "Min", "Max"; the inputs keep "Min" and "Max" as their
268
+ part names, as `DurationField` does. A labelled range `Slider` (the
269
+ `slider-range` filter included) is a group named by its label too, and its
270
+ readout inputs, which had no name, are named "Minimum" and "Maximum"
271
+ (`material.slider.minimum` / `maximum`, en and fr). If you override the
272
+ bundles or ship another language, translate the two new keys.
273
+
274
+ - [#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
275
+ in progress, the exports shared with `@nxgt/material-vue`, and the
276
+ `DateRangeField` presets, `TreeSelectField`, `DataTable` loading and empty
277
+ states and `FilePreview` to follow. Range filters in a `Filter` schema, which
278
+ already render, are listed as shipped.
279
+
280
+ - [#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"`
281
+ follows the foreground colour. The variant listed `default` twice, so
282
+ `default` rendered white and `light` had no colour, which made a spinner on a
283
+ dark surface (the player's buffering indicator) look static. A filled
284
+ `Button` with `spinnerVariant="1"` gets a white loading spinner again too.
285
+
286
+ - [#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
287
+ assistive tech.
288
+
289
+ - Each step's `id` was a static `step-N`, so two steppers on one page
290
+ produced duplicate ids. It is now prefixed with React's `useId`.
291
+ **Note:** if a test or stylesheet of yours targets `#step-N`, select the
292
+ step by `data-pw` instead (`step-N`, or `<name>-step-N`), which did not
293
+ change — see "A `Stepper` step selected by `#step-N` stops matching" in
294
+ `docs/troubleshooting.md`.
295
+ - The steps are an `<ol>` of `<li>`; the current step's button carries
296
+ `aria-current="step"`; a completed or failed step adds visually hidden text
297
+ (`material.stepper.completed`, `material.stepper.error`) so a screen reader
298
+ does not depend on the icon.
299
+ - A long step title no longer widens its column or pushes the next step: the
300
+ columns are `minmax(0, 1fr)` and the title truncates with an ellipsis.
301
+
302
+ - [#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
303
+ same names:
304
+
305
+ - `Checkbox` takes `label` and `helper-text` (or the `#label` and
306
+ `#helper-text` slots) and then renders the box in a labelled row. Without
307
+ them it is the same bare Reka UI box as before. `CheckboxFormField` is its
308
+ vee-validate wrapper: an unset value shows as indeterminate, and an
309
+ indeterminate value is written as `null`.
310
+ - `InlineEditFormField` (the value changes on save) and `CodeEditorFormField`
311
+ (Monaco still loads in the browser only).
312
+ - `PaginationBar` (`v-model:page`, `page-count`, `sibling-count`), the
313
+ `paginationItems` window it renders, and `PaginationLink`.
314
+ - `CopyValue`, a value in a monospace chip with its copy button.
315
+ - `MermaidDiagram`, one mermaid chart rendered in the browser only.
316
+ - The `Doc` parts `DocArticle`, `DocNav`, `DocLink`, `DefaultDocLink` and
317
+ `DocCopyPage`, with the `DocArticleProps`, `DocNavProps` and `DocLinkProps`
318
+ types, to build a documentation shell of your own.
319
+
320
+ Fixed in `Doc`: a parent's `@navigate` runs once per click (it ran twice);
321
+ without a navigate handler the links are plain anchors again, instead of
322
+ links that went nowhere; and the shell follows later `pathname`, `items`,
323
+ `toc`, `previous` and `next` changes, so the active entry tracks the route.
324
+
325
+ The Nuxt module registers the new components. In `@nxgt/material`, the README
326
+ and component pages name these Vue equivalents and list what is still
327
+ React-only.
328
+
329
+ ## 2.4.2
330
+
331
+ ### Patch Changes
332
+
333
+ - [#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
334
+ edge of the card. It sat right after the title and description, so it moved
335
+ with their length; the text now fills the row, as in `@nxgt/material-vue`. With
336
+ `variant="md"` and no `icon`, the indicator is at the right edge too.
337
+
338
+ ## 2.4.1
339
+
340
+ ### Patch Changes
341
+
342
+ - [#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
343
+ `withNxgtPrint` (`@nxgt/material/next`) now come from `@nxgt/material-core`
344
+ (`/format-file-size`, `/pdfkit-fonts`), shared with `@nxgt/material-vue`,
345
+ instead of a copy in this package. No API change: `formatFileSize` is still
346
+ exported by name, and `withNxgtPrint` adds the same globs, warns at most once
347
+ with the same `[@nxgt/material/next]` prefix (that one warning now lists
348
+ every resolution problem, joined by `; `, instead of only the first), and
349
+ still skips fonts on
350
+ another Windows drive with a warning. `@nxgt/material/next` now imports that
351
+ core subpath besides `node:*` built-ins; your install already has
352
+ `@nxgt/material-core`.
353
+ - Updated dependencies [[`bf43a14`](https://github.com/softistx/nxgt-material/commit/bf43a14abb147e6dcd2bcac5f1e0e359b0aae6a2), [`b890457`](https://github.com/softistx/nxgt-material/commit/b8904573f18dc6d0cdd16f62146183ff0b7aef18)]:
354
+ - @nxgt/material-core@0.2.0
355
+
356
+ ## 2.4.0
357
+
358
+ ### Minor Changes
359
+
360
+ - [#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
361
+ Next.js config ready for route handlers that import `@nxgt/material/print`.
362
+
363
+ ```ts
364
+ // next.config.ts
365
+ import { withNxgtPrint } from '@nxgt/material/next';
366
+
367
+ export default withNxgtPrint({ output: 'standalone' });
368
+ ```
369
+
370
+ It adds `'@nxgt/material'` to `serverExternalPackages` (route handlers are
371
+ bundled with React's `react-server` build, which has no `createContext`), and
372
+ pdfkit's `.cjs` fonts, with the chunk they `require`, to
373
+ `outputFileTracingIncludes` for every route, so `.next/standalone/server.js`
374
+ run alone can render PDFs. pdfkit is resolved through `@react-pdf/renderer`
375
+ and `@react-pdf/font` to its real directory, so the glob follows the
376
+ installation (checked end to end under npm and pnpm). Both lists are merged, never overwritten.
377
+ Without `@react-pdf/renderer` no fonts are added; any other resolution failure
378
+ prints one warning. The function form of `next.config` is accepted too, and
379
+ `{ dir }` sets the project directory (default `process.cwd()`).
380
+
381
+ It runs in Node at config time, imports only `node:*` built-ins, and adds no
382
+ dependency: `next` is not a peer, the config is typed structurally and the
383
+ type you pass in is the type you get back.
384
+
385
+ ### Patch Changes
386
+
387
+ - [#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:
388
+ `material.buttons.save` read « Sauvegarder » while every component's own `save`
389
+ key already read « Enregistrer ». Consumers who assert on the French label in
390
+ their own tests will now see « Enregistrer ».
391
+
392
+ - [#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
393
+ handler that imports `@nxgt/material/print` needs
394
+ `serverExternalPackages: ['@nxgt/material']`, or `next build` stops with
395
+ `TypeError: (0 , t.createContext) is not a function` (route handlers are
396
+ bundled with React's `react-server` build). With `output: 'standalone'`, add
397
+ `outputFileTracingIncludes: { '/**': ['./node_modules/pdfkit/js/standard-fonts/**/*.cjs'] }`:
398
+ the tracer copies only the `.mjs` fonts, and a webpack build's standalone
399
+ server answered every PDF request with
400
+ `Cannot find module '…/node_modules/pdfkit/js/standard-fonts/Helvetica.cjs'`.
401
+ `withNxgtPrint` (above) sets both for you; the manual config stays documented
402
+ as the fallback.
403
+
404
+ - [#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
405
+ …", the drop zone's "Max 2.0 MB") and the chat composer's attachment chip now
406
+ format sizes with the exported `formatFileSize`, as `FileList` and `FileCard`
407
+ already did. A negative, `NaN` or infinite size now shows nothing instead of
408
+ `NaN undefined` or `Infinity GB`; every other size reads as before.
409
+
410
+ ## 2.3.1
411
+
412
+ ### Patch Changes
413
+
414
+ - [#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.
415
+ - Updated dependencies [[`1abb908`](https://github.com/softistx/nxgt-material/commit/1abb90800ec213d8ec98abd7df5dda9f65c14204), [`b4e5770`](https://github.com/softistx/nxgt-material/commit/b4e5770922ad4582a1ad2dcca439f7c8c999a0c7)]:
416
+ - @nxgt/material-core@0.1.1
417
+
418
+ ## 2.3.0
419
+
420
+ ### Minor Changes
421
+
422
+ - [#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
423
+ your install adds on its own: there is nothing to change in your
424
+ `package.json` or your imports.
425
+
426
+ `@nxgt/material-core` holds the code `@nxgt/material` and
427
+ `@nxgt/material-vue` used to ship as two identical copies: the PDF document
428
+ model and its renderer, the print formatters (`formatAmount`, `formatDate`,
429
+ `createFormatters`), the column and value helpers of `/print`, the PDF file
430
+ helpers behind `PdfReader` and `usePdfFile`, the page geometry, scroll spy
431
+ and text search inside `PdfReader`, the decimals of `CurrencyField`, the rule
432
+ that keeps a field from blurring while focus is in its own popover, and
433
+ `pdfkitStandardFonts()`.
434
+ `@nxgt/material/print`, `/lib`, `/vite` and the other entries export exactly
435
+ the same names as before, with the same types, and now import that code from
436
+ `@nxgt/material-core` instead of bundling it.
437
+
438
+ One case to check: a server bundled into a single file and run **without**
439
+ `node_modules` must bundle `@nxgt/material-core` too, like any other
440
+ dependency of `@nxgt/material` — a bundler that inlines everything already
441
+ does. A server that keeps `node_modules` loads it from there. With Vite, list both:
442
+ `ssr: { noExternal: ['@nxgt/material', '@nxgt/material-core'] }`. Without it
443
+ the server fails at boot with `Cannot find package '@nxgt/material-core'`
444
+ ([troubleshooting](https://cdn.jsdelivr.net/npm/@nxgt/material@2/docs/troubleshooting.md#cannot-find-package-nxgtmaterial-core)).
445
+
446
+ ### Patch Changes
447
+
448
+ - [#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
449
+ `@nxgt/material-vue` already ships. The tiptap packages are regular
450
+ `dependencies` of `@nxgt/material`, not peers, so nothing changes in your own
451
+ `package.json`: their ranges are now `^3.31.3`, and your install picks it up.
452
+ The editor code is bundled, so the upgrade comes with the package either way.
453
+
454
+ What you may notice in `RichTextEditor`:
455
+
456
+ - **Security fixes.** ProseMirror's view is now 1.42.3, which fixes an XSS
457
+ where pasting crafted HTML could run script
458
+ ([GHSA-c8x8-7fp4-3x9w](https://github.com/advisories/GHSA-c8x8-7fp4-3x9w)).
459
+ Tiptap also stops untrusted HTML attributes from reaching an object's
460
+ prototype through `mergeAttributes`, and crafted Markdown attributes can no
461
+ longer spin the CPU.
462
+ - **Lists.** Tab at the start of a paragraph right after a list now moves the
463
+ paragraph into the list's last item.
464
+ - **Fixes.** The custom node views (callout, gallery, action card, radio
465
+ group, accordion, checkbox group, select card and select chip) no longer
466
+ freeze on iOS and Android and render faster in long documents; a YouTube
467
+ `/live/` link embeds, and a pasted iframe without `src` no longer crashes
468
+ the editor; unsetting one text style inside a blockquote keeps the others.
469
+
470
+ Code highlighting uses `highlight.js` 11.12.0 in `CodeBlock` (and so in
471
+ Markdown code fences and `ChatMarkdown`) and `highlight.js` 11.11.2 under
472
+ `lowlight` in the editor's code blocks (both were 11.11.1). Mermaid and nivo charts are
473
+ unchanged: only the internal `d3-format` and `d3-time-format` imports of the
474
+ bundled `d3-scale` moved to the versions Mermaid already used, and no rendered label is expected
475
+ to change.
476
+
477
+ If your app installs `@tiptap/*` itself below 3.31.3, a second copy is
478
+ nested under `@nxgt/material`. It works at runtime (the editor is bundled),
479
+ but an `onUpdate` or `onCreate` handler typed with your own `Editor` can
480
+ fail to type-check with "Types have separate declarations of a private
481
+ property": raise your tiptap packages to `^3.31.3`, or leave the handler's
482
+ 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,
483
+ and a line in `RichTextEditor`'s component docs.
484
+
485
+ - Updated dependencies [[`deaf0c4`](https://github.com/softistx/nxgt-material/commit/deaf0c49cbc2268fd20e9e2272104dbd27e4ce7f)]:
486
+ - @nxgt/material-core@0.1.0
487
+
488
+ ## 2.2.0
489
+
490
+ ### Minor Changes
491
+
492
+ - [#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
493
+ is shown with the currency's own decimals by default (ISO 4217: `JPY` 0,
494
+ `EUR` 2, `KWD` 3), the rule `formatAmount` follows in this release too; it
495
+ always showed two before. Pass `fractionDigits={2}` to keep two decimals whatever the
496
+ currency; it is clamped to 0–20 (the range every `Intl.NumberFormat`
497
+ accepts), and `NaN` or `Infinity` counts as not given. The emitted number is
498
+ unchanged.
499
+
500
+ The helpers guide now says which formatter to use: `useFormatters()`'s
501
+ `formatAmount` for money, `useNumberFormatter()` for the other
502
+ `Intl.NumberFormat` styles.
503
+
504
+ - [#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`)
505
+
506
+ The themed scroll container that `Filter`, `SortableList`, `PdfReader`,
507
+ `StxPlayer`'s queue, the notifications list and `EventCalendar` already use
508
+ inside is now public, as it is in `@nxgt/material-vue`. Give it a height and it
509
+ scrolls whatever overflows, with a thin scrollbar that matches the theme
510
+ (`type` controls when the bar shows; `hover` by default).
511
+
512
+ ```tsx
513
+ import { ScrollArea } from '@nxgt/material/components';
514
+
515
+ <ScrollArea className="h-72 w-48 rounded-md border">{children}</ScrollArea>;
516
+ ```
517
+
518
+ - [#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.
519
+
520
+ - `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.
521
+ - 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.
522
+ - `ContactsFormField`, `OpeningHoursFormField` and `PostalAddressFormField` passed only their own `disabled` and ignored `useForm({ disabled })`; they follow the same rule now.
523
+ - `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.
524
+ - `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`.
525
+ - `FilesFormField`: the image thumbnail's class `ronded` is now `rounded`.
526
+
527
+ - [#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
528
+
529
+ - Components rendered without an `I18nProvider` no longer show raw ICU
530
+ placeholders: the Stepper reads `Step 01` (not `Step {step}`), the
531
+ JsonViewer `3 items` (not `{count} items`), the Kanban WIP badge `3/5`, and
532
+ so on. `useMaterialTranslation().t(key, fallback, values)` now formats the
533
+ fallback with `values`, with or without a provider, and on a server. A
534
+ fallback that cannot be formatted (a stray `{`) is shown as written, with a
535
+ `[@nxgt/material] Could not format the fallback` warning in development
536
+ only. Plain fallbacks are not parsed at all.
537
+ - `zodLocaleError()`'s `invalid_type` messages were French in the English
538
+ bundle. They are English now (`This value must be a number.`), and a type
539
+ with no message of its own gets `This value has the wrong type.` instead of
540
+ its raw key.
541
+ - `zodLocaleError()` no longer throws on a server or when storage is blocked:
542
+ it writes English. It takes optional `{ storageKey, language, translate }`
543
+ so it can read the `storageKey` you gave `I18nProvider`, follow a language
544
+ you pass, and format through your own merged translator so your
545
+ `material.zod.*` overrides apply. Calling it with no argument behaves as
546
+ before.
547
+
548
+ - [#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`
549
+
550
+ 121 keys join the `material` bundles, in English and French, with the same
551
+ names and the same French as `@nxgt/material-vue`:
552
+
553
+ - `material.rich-text-editor.*` (111): every toolbar tooltip, the table menu,
554
+ the table, callout, accordion, action-card, gallery, selector, checkbox and
555
+ radio group dialogs and forms, the colour picker's `Clear`, the slash menu's
556
+ `No matching commands`, and the `words` / `characters` counter. The
557
+ dialogs' `Save` and the colour picker's `OK` use the existing
558
+ `material.buttons.save` / `material.buttons.ok`.
559
+ - `material.carousel.previous` / `next` (the arrows' screen-reader text),
560
+ `material.camera.*` (capture button, captured image `alt`, and a new
561
+ start/stop tooltip on the stream toggle, which had no accessible name),
562
+ `material.autocomplete.add` (`Add "{query}"`) and `empty`.
563
+ - `material.qrcode.label` and `material.barecode.label`: `QRCode` and
564
+ `Barecode` now render with `role="img"` and an accessible name. `Barecode`
565
+ wraps its drawing in an `inline-flex` `<span data-slot="barecode">` sized to
566
+ the barcode (`max-w-full`), because react-barcode forwards only `id` and
567
+ `className`; its `aria-label` goes on that wrapper, and a `className` still
568
+ reaches the barcode element. `QRCode`'s `title` names the code through
569
+ `aria-label` only and no longer renders an `<svg><title>`, which announced
570
+ the text twice.
571
+
572
+ Without a provider the English is what it was. Anything you pass still wins
573
+ over the translation: `CarouselPrevious`/`CarouselNext` children,
574
+ `Autocomplete`'s `freeSoloLabel` and `empty`, the editor's
575
+ `options.labels.save` / `cancel`, `QRCode`'s `title` or `aria-label`, and
576
+ `Barecode`'s `aria-label`.
577
+
578
+ - [#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`
579
+
580
+ 82 keys join the `material` bundles, in English and French, with the same
581
+ names and the same French as `@nxgt/material-vue`:
582
+
583
+ - `material.rich-text-editor.*` (47): the slash menu's group, item titles and
584
+ descriptions (`slash-menu.*`), the font menu (`font-default`, `font-sans`,
585
+ `font-serif`, `font-mono`) and the table-caption alignment choices
586
+ (`table-align-left` / `-center` / `-right`).
587
+ - `material.filter.*` (19): `min`, `max`, and the presets menu and dialog
588
+ (`presets.*`).
589
+ - `material.calendar.*` (9): the previous/next month and year tooltips, the
590
+ year view's paging, `Go to Today`, and the caption button's label.
591
+ - `material.table.labels.select-all` / `select-row`,
592
+ `material.upload.retry` / `remove`, `material.spinner.loading`,
593
+ `material['scroll-to-top'].label`, `material.map.you-are-here`.
594
+
595
+ The slash menu filters on the translated title and description and on the
596
+ English ones, accent-insensitively; `RichTextEditor` hands the menu its own
597
+ translator, so there is nothing to wire.
598
+
599
+ Without a provider the English is unchanged. An explicit label still wins:
600
+ `Spinner`'s `aria-label` now reaches the `variant="4"` SVG, and
601
+ `LocationMarker`'s `tooltip` prop is no longer overwritten by `You are here`.
602
+
603
+ - [#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
604
+
605
+ - **`PrintDataTable`** (`@nxgt/material/print`) prints `rows` through
606
+ `columns` from `definePrintColumns<T>()`: a `key` or an `accessor`, a
607
+ `label`, a `width` in points or percent or a `flex` share, an `align`
608
+ (right by default for figures), a `format` (`'amount'`, `'date'`,
609
+ `'number'`, `'percent'` or your own function), a `currency` per column or
610
+ per row, and a `total` (`'sum'` or your own). The header repeats on each
611
+ page the table crosses, rows never split (`keepRowsWhole`), rows stripe
612
+ (`striped`), a totals row closes the table, and an empty table shows one
613
+ "No rows" row (`emptyText`, or `hideWhenEmpty`). Amounts and dates follow
614
+ the `PrintDocument`'s locale.
615
+ - **`PrintKeyValue`**: label and value pairs in one or two columns, from
616
+ `items` or from `data` with the same column definitions as `fields`; empty
617
+ values are skipped unless `keepEmpty`.
618
+ - **`PrintMetric`** / **`PrintMetrics`**: a figure with its label, hint and
619
+ change (`+4%`), the printed `StatCard`, lined up in a row.
620
+ - **`resolvePrintTable`**, **`resolvePrintFields`**, **`toPrintValue`** and
621
+ the `PrintValue` helpers (`formatPrintValue`) are exported from `/print`
622
+ too, for your own blocks.
623
+ - New i18n keys `material.print.table.total` and `material.print.table.empty`,
624
+ in English and French.
625
+
626
+ - [#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`
627
+
628
+ - **`renderPdfBlob(doc)`**, **`renderPdfBuffer(doc)`** (a `Uint8Array`, in
629
+ the browser or on Node/Bun) and **`renderPdfStream(doc)`** (a Web
630
+ `ReadableStream`, Node 18+ or Bun).
631
+ - **`downloadPdf(doc | blob, { fileName })`** saves the file
632
+ (`document.pdf` by default; `.pdf` is added), and **`openPdf(doc | blob)`**
633
+ opens it in a new tab — the tab opens before rendering so popup blockers
634
+ let it through, and a blocked tab falls back to a download.
635
+ - **`pdfResponse(doc, { fileName, disposition, headers })`**: a Web
636
+ `Response` with the PDF's type, length and an RFC 6266 file name, for a
637
+ React Router loader, a Next route handler, Hono or `Bun.serve`;
638
+ **`contentDisposition(disposition, fileName)`** builds that header for a
639
+ response of your own (Express, a Node stream).
640
+ - **`usePdfFile(doc)`**: `{ blob, url, loading, error, refresh }`, the
641
+ object URL revoked when replaced or unmounted.
642
+ - `doc` is a react-pdf element (such as a `<PrintDocument>`) **or a document
643
+ model**: plain data built with the new builders (`pdfDocument`,
644
+ `definePdfDocument`, `page`, `heading`, `text`, `table`, `keyValue`,
645
+ `totals`, `metric`, `badge`, `header`, `dataTable` and `keyValueOf` from
646
+ the same column definitions as `PrintDataTable`, …), which can be built on a server
647
+ and sent as JSON. A `definePdfDocument` template annotates its data
648
+ parameter, and its other parameters may take defaults
649
+ (`(invoice: Invoice, language = 'en') => …`). A model is rendered with the package's translations
650
+ (`language`) and `@nxgt/material` as its creator. The model is
651
+ `@experimental`: it may still change in a minor release.
652
+
653
+ - [#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
654
+
655
+ - **`PrintDocument`** (`@nxgt/material/print`) replaces the `Document` +
656
+ `Page` you wrote by hand: `size`, `orientation`, `margins`, the PDF
657
+ metadata (`title`, `author`, `subject`, `keywords`, `creator`, `producer`),
658
+ a `header` and a `footer` repeated on every page, `pageNumbers` ("Page 2 of
659
+ 5", translated) and a `watermark`. A `footer` may be a function of
660
+ `{ pageNumber, totalPages }` returning text. Its `language` defaults to the
661
+ `I18nProvider` around the document, then `'en'`.
662
+ - **`translateMaterial(language, key, fallback, values)`** (`@nxgt/material/i18n`):
663
+ the package's own strings for a language given as a value, with no
664
+ provider and no hook, for a PDF's page-number callback or a server.
665
+ `useMaterialTranslation().t` now goes through it, so a regional language
666
+ such as `'fr-CA'` reads the French strings instead of falling back to
667
+ English.
668
+ - New i18n keys `material.print.page-of` and `material.print.page`, in
669
+ English and French.
670
+ - **`PrintHeader`**: a `logo` image (URL or data URL, or
671
+ `{ src, width, height }`), a `subtitle` and a `referencePrefix` (`'#'` by
672
+ default). **Changed:** with no `logo` and no `logoText` it no longer draws
673
+ a grey "LOGO" placeholder, and with no `qrDataUrl` no empty QR box; the
674
+ title stays centred. Pass `logoText="LOGO"` to keep the old box.
675
+ - French amounts print correctly (also inside mixed text such as
676
+ `{amount} TTC`, and in a `PrintDocument` footer function) in `PrintSection`, `PrintInfoRow`,
677
+ `PrintTableCell`, `PrintTotalRow`, `PrintCardField` and `PrintBadge`: the
678
+ narrow no-break space `Intl` puts in `1 234,50 €` used to print as a slash
679
+ (`1/234,50 €`) in the standard fonts.
680
+ - **Peer floor raised:** `@react-pdf/renderer` is now `^4.9`, the version
681
+ the page shell and `pdfkitStandardFonts()` are built and tested against
682
+ (pdfkit 0.20). Upgrade with
683
+ `bun add @react-pdf/renderer@^4.9` (or `npm i @react-pdf/renderer@^4.9`).
684
+
685
+ - [#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
686
+ `File`), an `ArrayBuffer` or a `Uint8Array`, a `URL`, or
687
+ `{ url, httpHeaders, withCredentials }` for a URL that needs a token, as well
688
+ as a URL string. The new `PdfSource` and `PdfUrlSource` types are exported.
689
+
690
+ - Bytes are copied: pdf.js empties the buffer it is given, and yours stays
691
+ usable after the reader opened it.
692
+ - The document reloads only when it changes: a URL with the same headers, even
693
+ in a new object on each render, keeps the view; a Blob or a buffer is
694
+ compared by identity.
695
+ - Download and print fetch a URL source with its headers, and save a Blob or
696
+ bytes as they are. When that fetch fails, a URL with headers is not opened
697
+ in a new tab, which would load it without them. With no `fileName`, a
698
+ `File`'s name or a URL segment, it is named "document.pdf", translated
699
+ (`material.pdf-reader.default-file-name`).
700
+ - The headers are part of what identifies the document: new headers (a
701
+ renewed token) load it again, from `initialPage`.
702
+
703
+ - [#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
704
+ attached before its click and the object URL is revoked a minute later, not
705
+ right away. A new `fileName` prop names the saved file, and a URL document is
706
+ named after its last path segment instead of always `document.pdf` (`.pdf` is
707
+ added when missing). A URL document is saved from the bytes already loaded,
708
+ so a cross-origin one downloads instead of navigating. Print uses a
709
+ same-origin copy too, and opens a new tab instead of throwing `SecurityError`
710
+ when the browser refuses.
711
+
712
+ - [#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' |
713
+ '_top'`, default `'_blank'`): a link to a website inside the PDF now opens in a
714
+ new tab, whatever the PDF asks; pass `'_self'` for the previous behaviour.
715
+ `rel` stays `noopener noreferrer nofollow`.
716
+
717
+ `usePdfSource`, `toPdfjsSource` and `toPdfjsParams` are exported beside
718
+ `usePdfReader` (with the `PdfjsSourceParams` / `PdfjsUrlParams` types), so a
719
+ layout of your own opens bytes without detaching your buffer.
720
+
721
+ `Tooltip` and `TooltipContent` take `container`, where the tooltip is
722
+ portalled.
723
+
724
+ - [#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
725
+ or Space, the arrows move, Escape closes and gives focus back), its button is
726
+ named after the current zoom ("Zoom level 150%",
727
+ `material.pdf-reader.zoom-menu`), and in fullscreen it opens inside the reader.
728
+ Keys pressed in the menu no longer turn pages. An outline entry without a title
729
+ reads "Untitled" in the reader's language (`material.pdf-reader.untitled`).
730
+
731
+ A link inside the PDF to another page goes to it through the reader, so it
732
+ works for a page not drawn yet and the page number follows; it used to do
733
+ nothing unless that page was already drawn.
734
+
735
+ `DropdownMenuContent` takes `container`, the element the menu is portalled into
736
+ (`document.body` by default): pass `document.fullscreenElement` for a menu
737
+ opened in fullscreen.
738
+
739
+ - [#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
740
+ `useFormatters()` / `createFormatters()` bind them.
741
+
742
+ - `formatAmount(value, currency?, { locale?, fractionDigits? })` and
743
+ `formatDate(value, { locale?, timeZone?, dateStyle?, timeStyle? })`. Without a
744
+ `locale` they still use the runtime's (a deterministic default is planned for
745
+ 3.0). `formatDate` also accepts a `Date` or an epoch number.
746
+ - `useFormatters({ locale?, timeZone? })` from `@nxgt/material/hooks` binds both
747
+ to the `I18nProvider` language (`en` without a provider), so a server render
748
+ and the browser print the same string (for a date with a time, pass
749
+ `timeZone`).
750
+ - `createFormatters({ locale?, timeZone? })` from `@nxgt/material/lib` (and
751
+ `/print`) binds them outside React: a server route, a PDF document.
752
+
753
+ **Changed output.** Amounts use their currency's own decimals (ISO 4217):
754
+ `formatAmount(1234.5, 'JPY')` is now `¥1,235` (was `¥1,234.50`), KWD, BHD and
755
+ OMR show three decimals. EUR, USD, GBP and the other two-decimal currencies do
756
+ not change, nor does an amount without a currency. To keep two decimals, pass
757
+ `{ fractionDigits: 2 }`. `fractionDigits` is clamped to 0–20, as in
758
+ `CurrencyField`, and `NaN` or `Infinity` counts as not given.
759
+
760
+ **Fixed.** A date-only string (`'2026-03-01'`) keeps its calendar day in every
761
+ time zone; west of UTC it used to show the day before.
762
+
763
+ - [#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
764
+
765
+ - A locally packed or linked build could ship declaration files importing
766
+ `'../../../node_modules/react'`. That path does
767
+ not exist in a consumer's install, so `ReactNode` and `ComponentProps` were
768
+ typed `any`, and `ConfirmationDialog` demanded `variant` and `color`.
769
+ Versions installed from npm were not affected; locally packed or linked
770
+ builds were.
771
+ - `CircularProgressProps` is exported from `@nxgt/material/components` (and
772
+ `@nxgt/material`), like the other components' props types.
773
+
774
+ - [#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:
775
+
776
+ - **Calendar** (and the calendars in `DateField` / `DateRangeField`): weeks with two-digit days no longer push the last column out of the 312px calendar.
777
+ - **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.
778
+ - **ScrollToTop**: `variant` and `color` now reach the button.
779
+ - **ButtonGroup**: a `tonal` button marked `data-state="active"` now shows its label (primary foreground on the primary fill).
780
+ - **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).
781
+ - **HydrateLoading**: new `loadingIcon` prop; the misspelled `lodadingIcon` still works and is deprecated.
782
+ - **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]`.
783
+ - **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.
784
+ - **filterMentions**: new optional `{ match: 'prefix' | 'substring' }` (default `'substring'`, unchanged).
785
+ - **RichTextEditor**: `options.mentions.suggestions` accepts `MentionItem[]` (the type `MentionList` and `ChatComposer` take) as well as `string[]`.
786
+ - Print: the `PrintHeader` QR box is centred again (a class typo dropped `justify-center`).
787
+
788
+ ### Patch Changes
789
+
790
+ - [#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
791
+ label, and once a file hides the drop zone (or it is disabled: `maxFiles`
792
+ reached, or `disabled`) the group carries the error (the `helperText`, or a
793
+ hidden "This value is not valid."), which nothing said before. `ImageField`: a
794
+ failed upload with no message of its own is described as not valid, not only
795
+ outlined in red.
796
+
797
+ `AppBreadcrumb`: each crumb is `<li><a>…</a></li>` (the `<li>` sat inside the
798
+ `<a>`), and the home crumb is a link named "Home" rather than a `<button>`
799
+ inside a link.
800
+
801
+ `SwitchFormField` and `CheckboxFormField` no longer use the field's `name` as
802
+ the control's `id`, which collided between two forms on one page; without an
803
+ `id` of yours one is generated.
804
+
805
+ `UploadField` and `TimeField` are tinted only while they hold a value, like an
806
+ `<input>`: `inputDefaultClassNames`' `not-placeholder-shown:` tint always
807
+ matched these `div`s. The `Switch` row uses the same untinted base.
808
+
809
+ - [#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
810
+ `EntityHeader` actions built on it) no longer sets `aria-expanded`: it said
811
+ whether the label was showing, so screen readers announced a button that
812
+ controls nothing as "collapsed". Used as a popover, drawer or menu trigger (the
813
+ `Filter` trigger), it still carries the trigger's own `aria-expanded`.
814
+
815
+ `Switch`: the row's text is now the switch's `<label>`, so a labelled switch
816
+ has an accessible name (even without an `id`) and clicking the text toggles
817
+ it. With a `placeholder`, the `label` above the row is the name
818
+ (`aria-labelledby`) and the row text is the description (`aria-describedby`),
819
+ as in `@nxgt/material-vue`; an `aria-label` / `aria-labelledby` you pass
820
+ wins. The row is tinted from the switch's real state (`checked`, or
821
+ `defaultChecked` and each toggle) instead of `value`, and is no longer tinted
822
+ while off. `className` applies when there is no field
823
+ around the switch, on the row or the bare switch.
824
+
825
+ The row carries `data-checked="true"` / `"false"`, from the same state.
826
+ `data-filed` (misspelt) read `value`, so a form-bound switch always showed
827
+ `"false"`; it now follows the switch's state too, and is deprecated: it is
828
+ removed in 3.0.
829
+
830
+ - [#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
831
+ overlay — a `SelectFormField` list opened by pressing the mouse on its
832
+ trigger, which takes focus without the trigger ever having it — marks the
833
+ field touched as it is left. It used to count that overlay as the field's own
834
+ and mark the field touched only when the list handed focus back to its
835
+ trigger. An overlay is the field's when one of its controls names it
836
+ (`aria-controls`), or when it opened since focus entered and no other control
837
+ names it.
838
+
839
+ - [#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
840
+ "Reporting a bug" section of the troubleshooting page, linked from the README.
841
+
842
+ - [#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 ✕
843
+ "Remove Paris" after the text it shows, as a text label already did; a label
844
+ with no text keeps "Remove". The same rule as `@nxgt/material-vue`'s `Chip`,
845
+ which names it after its default slot when one is given.
846
+
847
+ - [#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
848
+ close button) are named "Close" when they show only their icon: screen readers
849
+ announced an unnamed button. The map's zoom buttons are named "Zoom in" /
850
+ "Zoom out". All follow `I18nProvider` (`material.buttons.close`,
851
+ `material.map.zoom-in`, `material.map.zoom-out`); text children or an
852
+ `aria-label` you pass win.
853
+
854
+ - [#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.
855
+
856
+ 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`.
857
+
858
+ `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.
859
+
860
+ - [#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
861
+ components need, which reached only the compiled stylesheet before: KaTeX
862
+ (`Markdown` maths), the code theme (`CodeBlock`), highlight.js
863
+ (`RichTextEditor` code blocks), react-easy-crop (`ImageCropper`), and the
864
+ stylesheets of `ButtonGroup`, `BottomAppBar`, `ContactItem`,
865
+ `OpeningHoursItem`, `AttributeForm` and `AttributeValuesForm`, which were CSS
866
+ modules and are global `nxgt-*` classes now. The compiled stylesheet styles
867
+ the same components the same way.
868
+
869
+ - [#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
870
+
871
+ - The README catalogue has a **Preview** column, and each name links to its
872
+ entry in `docs/components/`.
873
+ - `docs/` now ships in the package:
874
+ - one page per category: preview, example, options;
875
+ - the component gallery;
876
+ - getting started;
877
+ - guides for forms, i18n, the app shell, theming, printing, maps, SSR and
878
+ the helpers;
879
+ - troubleshooting;
880
+ - the roadmap.
881
+ - The README's long sections moved to those guides.
882
+
883
+ - [#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"`
884
+ (instead of `popover-trigger`), as `DateField`'s carries `date-field-trigger`.
885
+
886
+ - [#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
887
+ first render (`useMedia` now has a default), so SSR pages no longer log a
888
+ hydration attribute mismatch or react-use's "defaultState should be defined"
889
+ warning; a phone switches to the dialog once mounted.
890
+
891
+ `DateField` and `DateRangeField`: `disabled` disables the field (the trigger
892
+ can no longer be opened) instead of reaching the calendar as "every day is
893
+ disabled". Day rules move to the new `disabledDays`; a non-boolean `disabled`
894
+ is still passed to the calendar. `id`, `aria-describedby` and `data-*` land on
895
+ the trigger in `DateField`, `DateRangeField` and `TimeField` (both its popover
896
+ and its phone dialog) instead of on the calendar, the inner box, or nowhere.
897
+
898
+ `UploadField`: its file input is visually hidden instead of `hidden`, so a
899
+ `required` field with no file stops the submit with the browser's message
900
+ instead of a console-only "An invalid form control … is not focusable".
901
+ `ImageField`, `FileListField` and the `NetworkUploadField` / `S3UploadField`
902
+ drop zone no longer put `aria-invalid` on a `group` or a `button`: the error text
903
+ describes them, and the outline comes from `data-invalid`.
904
+
905
+ - [#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
906
+ buttons: the list-row badges of `Contacts`, `OpeningHours`, `PostalAddress`,
907
+ `AttributesList` and `FileListField`, the `NetworkUploadField` file preview,
908
+ the `EntityHeader`, `ConfirmationDialog` and `AppSidebar` icons, the `Stepper`
909
+ info icon, the `AccordionCard` chevron and the map's default marker pins. Each
910
+ was a nameless `<button>` (focusable, except the disabled upload preview); each is now a `<span>` with
911
+ `aria-hidden="true"` and the same look. A `Contacts`
912
+ badge is `role="img"`, named by the contact type. The `AccordionCard` header
913
+ is now the `<button>` itself (it was a `<div>`, reachable from the keyboard only
914
+ through the chevron), and its chevron now turns when open. A `Marker` with a
915
+ string `tooltip` passes it to leaflet as `title`, which names the focusable
916
+ marker now that the pin inside is hidden (a `title` you pass wins). `ConfirmationDialog`'s `icon` is the glyph alone (the
917
+ dialog wraps it in the badge; style it with `iconProps`): an `IconButton`
918
+ passed there is now a button inside a hidden `<span>`.
919
+
920
+ An `Icon` inside a named `IconButton` (and so `InputGroupButton` and
921
+ `IconLinkButton`) is `aria-hidden` instead of `role="img"` named
922
+ "Icon {name}", so a screen reader reads "Delete", not "Delete, Icon trash". An
923
+ `aria-label` you give the `Icon` still wins; an `Icon` anywhere else keeps its
924
+ "Icon {name}" default. See troubleshooting for the selectors this changes.
925
+
926
+ - [#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
927
+ `never`. With `never`, the template compiled and the error appeared only
928
+ where the template was called (`Argument of type 'Invoice' is not assignable
929
+ to parameter of type 'never'`); now reading a property of it fails in the
930
+ template itself. Annotated parameters and defaults (`language = 'en'`) are
931
+ typed as before.
932
+
933
+ - [#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
934
+
935
+ Both are single rows bound to a react-hook-form array (`control` + `name` +
936
+ `index`), and nothing in the library renders them. Use `ContactsFormField` and
937
+ `OpeningHoursFormField`, which own the list, the add / edit dialog and the
938
+ array. 3.0 moves every react-hook-form-bound export to a `@nxgt/material/forms`
939
+ subpath with react-hook-form as an optional peer (see `docs/roadmap.md`).
940
+
941
+ - [#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
942
+ the dialog opened — a `DialogTrigger` as before, and now also the button of
943
+ yours that opened a controlled `Dialog`, where focus used to fall to `<body>`.
944
+ Focus that already went elsewhere (a non-modal dialog closed by a click in
945
+ another input) stays there. An `onCloseAutoFocus` that calls
946
+ `event.preventDefault()` opts out.
947
+
948
+ - [#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
949
+
950
+ - `ConfirmationDialog` can be controlled: `open` and `onOpenChange` from a
951
+ parent now drive it. Before, `open` only seeded internal state, and passing
952
+ `onOpenChange` stopped the dialog from opening or closing itself. Without
953
+ `open` it is uncontrolled (`defaultOpen`), and `onOpenChange` only
954
+ observes. If you passed `open` only to open it initially, pass
955
+ `defaultOpen` instead.
956
+ - `ConfirmationDialog` validates its form before confirming: `onConfirm`
957
+ receives the schema's parsed output (trims and transforms applied) instead
958
+ of the raw `getValues()`.
959
+ - `ConfirmDialog` stays open while an async `onConfirm` runs (buttons
960
+ disabled, "Processing..."), closes on success, and on a throw or rejection
961
+ stays open and shows the error's message instead of logging it and closing.
962
+ The pending label and the message shown for an error without one are
963
+ translated (`material.confirm-dialog.processing`,
964
+ `material.confirm-dialog.failed`, en and fr).
965
+ - `CommandDialog` renders its visually hidden title and description inside the
966
+ dialog, rather than in the page next to it.
967
+
968
+ - [#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
969
+ data: 351 files under `dist/lib` (`*.stories.d.ts`, `*.test.d.ts`,
970
+ `*.rows.d.ts`, `__stories__/`, `stories-*.d.ts`, story data) are gone from the
971
+ tarball. None was reachable from an entry point, so no import changes; a
972
+ declaration that named `storybook` or `vitest` types can no longer surface in a
973
+ consumer's type check.
974
+
975
+ - [#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
976
+ are no longer inside the trigger button (a `<label>` in a `<button>` is
977
+ invalid, and the helper text was read as part of the value). They sit beside
978
+ it in the field's root `div`, which takes `className`; the trigger is named by
979
+ the Label (`aria-labelledby`) and described by the helper text, then your
980
+ `aria-describedby` ids. The Label is `for` the trigger, so a click on it opens
981
+ the picker, and the open popover (or `TimeField`'s dialog on a phone) is
982
+ labelled by the trigger. A disabled field dims its trigger only, no longer its
983
+ label and helper text. `DateFieldTrigger`, `DateRangeFieldTrigger` and `TimeFieldTrigger`
984
+ render only what the button shows.
985
+
986
+ `Slider`: an `aria-label` or `aria-labelledby`, and `aria-invalid` (or
987
+ `error`), go on every thumb rather than on the root `span`, and a vertical
988
+ `Slider`'s `label` names its thumbs.
989
+
990
+ `DurationField`: a `group` named by its Label and described by its helper
991
+ text; the inputs are named "Hours" and "Minutes"
992
+ (`material.duration-field.hours-label` / `minutes-label`, en and fr). The
993
+ minutes input had no name. `NumberField` takes an `aria-label`.
994
+
995
+ `CodeEditor`: Monaco's textarea is named by the field's label (`ariaLabel`),
996
+ and follows it when it changes.
997
+
998
+ - [#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
999
+ "Filters2": the count badge is hidden from assistive technology and the count
1000
+ is announced through `material.filter.active-count` (en, fr).
1001
+
1002
+ - [#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.
1003
+
1004
+ - [#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
1005
+ index (`` `${id}-${i}` ``), no longer its `value`, so two groups with the same
1006
+ options (two yes/no questions) no longer share ids and a label no longer
1007
+ checks the other group's option. The group's `id` is yours, or generated with
1008
+ `useId()`. `RadioGroupFormField` / `CheckboxGroupFormField` no longer use the
1009
+ field's `name` as that `id`, which collided between two forms on one page. A
1010
+ `#<name>` or `#<option value>` selector needs an `id` of yours: see the
1011
+ troubleshooting entry.
1012
+
1013
+ `AppBreadcrumbItem`: React Router's options besides `replace` and `state`
1014
+ (`preventScrollReset`, `relative`, `reloadDocument`, `viewTransition`, …) and
1015
+ the anchor-only attributes (`target`, `rel`, `download`, …) now go to its link.
1016
+ They were spread onto the `<li>`, where React warned about them and the link
1017
+ never saw them.
1018
+
1019
+ - [#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
1020
+ `label`: the `radiogroup` (and `CheckboxGroup`'s wrapper, now a
1021
+ `role="group"`) carries `aria-labelledby` to the label and `aria-describedby`
1022
+ to the helper or error text, so a screen reader announces the question, not
1023
+ only the first option. A caller's `aria-label` / `aria-labelledby` still wins.
1024
+ `CheckboxGroup` no longer puts `aria-invalid` on the group, where it is not
1025
+ allowed; an error without text is described by a visually hidden "This value
1026
+ is not valid.", and so is a `CheckboxGroupFormField` in error with no message
1027
+ that shows the caller's `helperText`. `RadioGroup` sets `aria-invalid` on its
1028
+ `radiogroup` from `error` (a caller's own `aria-invalid` wins).
1029
+
1030
+ - [#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`
1031
+
1032
+ 30 keys join the `material` bundles, in English and French, with the same
1033
+ names and French as `@nxgt/material-vue`: the filter's empty state, `+N more`
1034
+ chip and the Yes/No, From, Until and Up to of its chips (`filter.empty`,
1035
+ `filter.more`, `filter.value.*`); the `aria-label` of `Breadcrumb` and
1036
+ `Pagination`, the breadcrumb ellipsis's "More" and `DocHeader`'s breadcrumb
1037
+ (`breadcrumb.*`, `pagination.label`, `doc.breadcrumb`); `MessageCard`'s
1038
+ "Unread" (`cards.unread`); the network upload field's "Replace {name}",
1039
+ "Upload failed" and its type, size and count errors (`upload.*`); the editor's
1040
+ placeholder and emoji buttons (`rich-text-editor.placeholder`,
1041
+ `insert-emoji`); `CircularProgress`'s label, `StxPlayer`'s "Media player",
1042
+ `PaneLayoutPane`'s sheet title, `JsonViewer`'s "Empty JSON" and
1043
+ `TimePicker`'s AM/PM. `pagination.first` / `last`,
1044
+ `carousel.roledescription` / `slide` and `attributes.buttons.add-option` are
1045
+ used by the Vue library only.
1046
+
1047
+ The editor's default placeholder now follows a language change without
1048
+ rebuilding the editor, as does a new `placeholder`. `formatFilterValue` takes
1049
+ the chip words as an optional third argument, typed `FilterValueWords`, with
1050
+ the English in the newly exported `FILTER_VALUE_WORDS`; without it the English
1051
+ is unchanged. `parseJsonSource` marks a blank source with `empty: true`. The
1052
+ upload count error now reads "Maximum of 1 file reached" for a single file. Your
1053
+ `placeholder`, `sheetTitle` and `aria-label` still win.
1054
+
1055
+ `NetworkUploadField`: a successfully uploaded item gets its primary border
1056
+ and tint again.
1057
+
1058
+ - [#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
1059
+ their icon's name ("Icon chevron-down", "Icon trash"), now have a name,
1060
+ translated under `I18nProvider`: the `Sidebar` toggle ("Toggle sidebar",
1061
+ `material.sidebar.toggle`, with `aria-expanded`), the data table's `expand`
1062
+ column ("Toggle row details", `material.table.labels.toggle-row`, with
1063
+ `aria-expanded`), the `LocationPreview` default trigger ("Preview location" /
1064
+ "Preview {name}", `material.map.preview` / `preview-named`), and the delete
1065
+ button of the deprecated `ContactItem` / `OpeningHoursItem` (the existing
1066
+ `material.contacts.buttons.remove` / `material.opening-hours.buttons.remove`).
1067
+
1068
+ A chip's remove button is named after the chip: "Remove {label}"
1069
+ (`material.chip.dismiss-label`) on a dismissible `Chip` whose `label` shows text
1070
+ and on a `ComboboxChip` whose children are text; `ComboboxChip` takes a
1071
+ `removeLabel` for other children. `Autocomplete`'s chips are dismissible
1072
+ `Chip`s, so each remove button is a real, focusable `<button>` instead of a
1073
+ nameless `div` nested inside the chip's button, and it is disabled with the
1074
+ field. The `Sidebar` toggle also shows while focus is inside the sidebar, so
1075
+ the keyboard can reach it (it used to appear on hover only).
1076
+
1077
+ - [#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
1078
+ mounts in free mode before its image has loaded. The 0×0 crop size reported
1079
+ before the image has a size used to become a `NaN` aspect, which the cropper
1080
+ saw as a change on every update.
1081
+
1082
+ - [#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
1083
+ required field was named "Email*" or read "Email star". The field's own
1084
+ `required` / `aria-required` says it, which every field of the library now
1085
+ sets. A test that found a field by a name ending in `*` finds it by the label
1086
+ alone: see the troubleshooting page.
1087
+
1088
+ - [#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
1089
+ built package its static imports drop from about 5.5 MiB to about 0.4 MiB, so a
1090
+ lazily loaded map fetches far less code. No API change.
1091
+
1092
+ - [#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`,
1093
+ `AppBreadcrumb`, `DataTable`, `ResponsiveButton`, `ThemeToggle`, `CodeBlock`,
1094
+ `CodeDiff`, `JsonViewer`, `CodeEditor`), plus `Doc` and `TimeField`, now give
1095
+ the server an answer for their media query: a desktop, light screen. SSR
1096
+ servers no longer log react-use's "defaultState should be defined" warning,
1097
+ and a phone or a dark-mode browser no longer hits a hydration mismatch that
1098
+ made React throw the server HTML away. The client switches right after
1099
+ hydrating; a page rendered only in the browser reads the real answer from its
1100
+ first render, as before.
1101
+
1102
+ - [#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
1103
+ focus into themselves when they open, as a modal must (WCAG 2.4.3): to their
1104
+ first control, or to the content when it has none. `AlertDialogContent`
1105
+ focuses `AlertDialogCancel`, or its first control when there is no Cancel.
1106
+ They used to cancel Radix's open auto-focus, so focus stayed behind the
1107
+ overlay. `Drawer` now turns Vaul's `autoFocus` on by default. An
1108
+ `onOpenAutoFocus` that calls `event.preventDefault()` (or `autoFocus={false}`
1109
+ on a `Drawer`) keeps focus where it was; focus still returns to the opener on
1110
+ close. A test that types right after opening may now type into the overlay:
1111
+ see the troubleshooting entry "Keys typed right after opening a dialog, sheet
1112
+ or drawer land inside it".
1113
+
1114
+ - [#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
1115
+ close, to the element that had it when they opened, as `DialogContent` does:
1116
+ the button of yours that opened a controlled one (no trigger) gets it back,
1117
+ where focus used to fall to `<body>`. Focus that already went elsewhere stays
1118
+ there, and an `onCloseAutoFocus` that calls `event.preventDefault()` opts out.
1119
+
1120
+ - [#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`
1121
+ and `AnnotationLayer.css`) now come with `@nxgt/material/styles.css`, so a
1122
+ Tailwind app gets selection, search highlights, links and notes styled. They
1123
+ used to reach only the compiled `dist/material.css`.
1124
+
1125
+ - [#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`:
1126
+
1127
+ - A page's own `/Rotate` is honoured: a page the PDF shows landscape is drawn,
1128
+ thumbnailed, placed and fitted landscape (it was drawn at rotation 0).
1129
+ `PdfPageSize` gains an optional `rotate`.
1130
+ - Fit width and fit page use the first 25 pages, read together when the
1131
+ document opens, so the zoom no longer moves as later page sizes arrive
1132
+ (still read 25 pages at a time); the fit stops at 500%.
1133
+ - `onPageChange` also reports the page the reader moves to itself: an
1134
+ `initialPage` clamped to the last page, and `initialPage` again after a new
1135
+ `file`.
1136
+ - The password field is named "Password" (new key
1137
+ `material.pdf-reader.password-label`), not after the "Password protected"
1138
+ heading.
1139
+ - The first search renders the pages once per 25 pages read, not once per
1140
+ page.
1141
+ - A note's icon shows: pdf.js asked the host page for `annotation-note.svg`
1142
+ (a 404); the icons are now inlined, loaded only for a document with notes.
1143
+
1144
+ `TooltipContent` (and so every `Tooltip`) opens inside the element in
1145
+ fullscreen while there is one: `PdfReader`'s toolbar tooltips show in
1146
+ fullscreen.
1147
+
1148
+ - [#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`
1149
+ tag now sits on its old alias `PdfViewer`, where it was meant to be. Code that
1150
+ imports `PdfViewer` now sees the strike-through; switch to `PdfReader`, which
1151
+ takes the same props.
1152
+
1153
+ - [#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
1154
+ assuming US Letter, fitting the widest and tallest page with one zoom, so A4
1155
+ and landscape pages no longer overflow or leave a gap. Placeholders and
1156
+ thumbnails take their page's shape before it draws. `PdfPageList`,
1157
+ `PdfSidebar` and `PdfThumbnailSidebar` take an optional `pageSizes`, and the
1158
+ `PdfPageSize` type is exported. The fit also follows the reader's real size;
1159
+ it used to fit an 800 × 600 box.
1160
+
1161
+ - [#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
1162
+ `material.pdf-reader.password-incorrect`, en and fr), marks the field `aria-
1163
+ invalid`, announces it and focuses the field again. The loading spinner, the
1164
+ error message and the password field now show before the document has loaded;
1165
+ they never appeared, so a protected or broken PDF left the reader blank, and
1166
+ kept the pages hidden even after the right password. Escape closes the open
1167
+ search bar before a Radix `Dialog` or `Sheet` around the reader sees it, and
1168
+ every other Escape reaches the dialog. `onPageChange` reports a page inside the
1169
+ document, and only when it changes.
1170
+
1171
+ - [#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
1172
+ never scrolled to (past the end, it opens the last page; a new `file` opens on
1173
+ `initialPage` again). The page number follows scrolling, and `onPageChange`
1174
+ reports the page most on screen; a page you ask for is reported once, not each
1175
+ page passed on the way.
1176
+
1177
+ - [#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,
1178
+ and counts as you type. It finds every match, including one split across text
1179
+ runs, and ignores case, accents, ligatures and the width of white space.
1180
+ Moving to a match shows its page and marks it. `PdfPageList` takes an optional
1181
+ `highlight`, which `PdfReader` passes it, and the `PdfSearchHighlight` type is
1182
+ exported.
1183
+
1184
+ - [#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
1185
+ (`Failed to execute 'exitFullscreen' on 'Document': Document not active`) when
1186
+ it unmounts while fullscreen or in picture-in-picture, or when the browser
1187
+ refuses a fullscreen request. A refused change leaves the player as it is.
1188
+
1189
+ - [#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
1190
+ element a screen reader lands on, not only with the `*`:
1191
+
1192
+ - `DateField`, `DateRangeField`, `TimeField`: the trigger is a `combobox`
1193
+ (a `button` may not carry `aria-required`) named by the label, with
1194
+ `aria-required` (and `aria-invalid` with `error`). An `aria-label` /
1195
+ `aria-labelledby` you pass now names it (a picker with no label needs one,
1196
+ or a `placeholder`); on `DateField` / `DateRangeField` it used to reach the
1197
+ calendar instead. `DateField`,
1198
+ `DateRangeField` and the phone-width `TimeField` now show the asterisk they
1199
+ were missing, and the phone-width `TimeField` trigger is a focusable button
1200
+ instead of a `div`.
1201
+ - `DurationField`: `required` on both inputs.
1202
+ - `Slider`: `aria-required` on every thumb, and each thumb is named by the
1203
+ label ("Price Minimum" / "Price Maximum" for a range).
1204
+ - `RatingField`: the stars are a `radiogroup`, named by the label, with
1205
+ `aria-required`.
1206
+ - `RichTextEditor`: the editable area is a multi-line `textbox` named by the
1207
+ label, with `aria-required`. `CodeEditor`: Monaco's `ariaRequired`.
1208
+ - `TransferList`: `aria-required` on the Selected listbox, and both panes are
1209
+ named by the label, then their title ("Team Selected"). `SortableList`: the
1210
+ listbox is named by the label, with `aria-required`.
1211
+ - `ComboboxField`, `Autocomplete`: `aria-required` on the input;
1212
+ `Autocomplete`'s label now names its input.
1213
+ - Tests that found a picker by the `button` role, or a thumb or a pane by
1214
+ its old exact name: see the troubleshooting page.
1215
+ - `ImageField`, `FileListField`, `UploadField` (a file picker is a button, and
1216
+ their file input is hidden): the field is a `group` named by the label and
1217
+ described by a visually hidden "This field is required."
1218
+
1219
+ - [#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:
1220
+
1221
+ - `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.
1222
+ - `SelectField` now shows its `placeholder` (it still falls back to `label`) and the required asterisk when `required` is set.
1223
+ - `SelectTrigger` gets its small radius back (a stray fragment had broken its class list), and the focused `SelectItem` is tinted again.
1224
+ - `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`.
1225
+ - `OtpField` generates its id with `useId()`, so server and client render the same markup.
1226
+ - `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.
1227
+ - `RadioGroup` spaces its label, helper text and options again (`gap-2`).
1228
+
1229
+ - [#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
1230
+
1231
+ - `Switch` shows `helperText` without a `placeholder` (under the labelled row
1232
+ or the bare switch), so `SwitchFormField`'s validation message is no longer
1233
+ lost. A switch in a labelled row is no longer announced as invalid when it
1234
+ has no `error`.
1235
+ - `createStepFormSlice` resumes from what `updateStep` stored under its `name`
1236
+ (step and values; nothing is read on a server, and anything that is not a
1237
+ step-form state is ignored), and `reset` clears that entry.
1238
+ `useSliceReducer` computes a slice's initial state once, on mount.
1239
+ - `ConfirmDialog` and `ConfirmationDialog` translate their default button
1240
+ labels: `material.confirm-dialog.confirm` / `.cancel` (English and French).
1241
+ - `LinkButton`, `IconLinkButton` and `ResponsiveLinkButton` render a single
1242
+ `<a>` styled as the button instead of a `<button>` inside a link: one tab
1243
+ stop, role `link`. `Button` with `asChild` keeps its loading spinner inside
1244
+ the child, and neither `Button` nor `IconButton` puts `type` on an `asChild`
1245
+ element.
1246
+ - A collapsed `ResponsiveButton` (the `Filter` trigger on a phone, for one)
1247
+ keeps its label as screen-reader text, so the icon-only button has an
1248
+ accessible name.
1249
+
1250
+ - [#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
1251
+ `useSliceReducer` hydrates without a mismatch: `useSliceReducer` renders the
1252
+ server's state (step 0) while hydrating and resumes at the stored step once
1253
+ mounted. A page rendered only in the browser still shows the stored step from
1254
+ its first render, and `reset` still clears it. Wrapping such a wizard in
1255
+ `clientOnly` is no longer needed; a slice kept in a Redux store follows the
1256
+ forms guide's recipe instead.
1257
+
1258
+ - [#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
1259
+ layout), like the other responsive components: SSR servers no longer log
1260
+ react-use's "defaultState should be defined" warning, and a desktop no longer
1261
+ hits a hydration mismatch on a page with a `Stepper`. `PaneLayout` and
1262
+ `ListDetailsLayout` use the same helper; they rendered the wide layout on the
1263
+ server already, and a page rendered only in the browser now reads the real
1264
+ answer from its first render instead of flashing the wide layout on a phone.
1265
+
1266
+ `TimeField`'s OK button follows the language (`material.buttons.ok`), and
1267
+ both branches place props the same way: on a phone `name`, `title`, `style`
1268
+ and the other `div` props now reach the field (they were dropped), and on a
1269
+ desktop the picker gets `onBlur` only: `name`, `required` and any other
1270
+ `div` prop (`onClick`, `onKeyDown`, `title`, …) now go to the field rather
1271
+ than the picker's `div`, and `disabled` stays on the trigger button instead
1272
+ of also landing on the field's display `div`.
1273
+
1274
+ - [#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`,
1275
+ where it is not allowed. An invalid `Switch` says so on the `switch` alone,
1276
+ and its bordered row shows it with a red border; an invalid `Autocomplete`
1277
+ says so on its input alone, and the chips box shows it with a red border.
1278
+
1279
+ - [#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
1280
+ describes the switch through `aria-describedby`, with or without a row, next
1281
+ to the row text and any `aria-describedby` you pass.
1282
+
1283
+ - [#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
1284
+ allowed: the box inside the trigger shows an error with a class, and an
1285
+ `aria-invalid` you pass (as `TimeFormField` does) goes on the trigger
1286
+ `combobox`, with the field's `error`.
1287
+
1288
+ - [#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
1289
+ the dialog), as it already did in the desktop popover, so a `TimeFormField` on a
1290
+ phone is marked touched and validated on blur (`mode: 'onBlur'` /
1291
+ `'onTouched'`) instead of only at submit.
1292
+
1293
+ - [#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
1294
+ files") follows the language like the rest of the field
1295
+ (`material.upload.dropzone.max-size` / `max-files`, with French), and says
1296
+ "Up to 1 file" instead of "Up to 1 files".
1297
+
1298
+ - [#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
1299
+ text, and the drop zone no longer is as well (a screen reader read it on
1300
+ entering the group and again on the drop zone). `ImageField`, `FileListField`,
1301
+ `UploadField` with `trailing`, and the `NetworkUploadField` / `S3UploadField`
1302
+ drop zone, in error without `helperText`, are described by a visually hidden
1303
+ "This value is not valid." (`material.zod.default`, translated) instead of
1304
+ being only a red outline.
1305
+
1306
+ ## 2.1.0
1307
+
1308
+ ### Minor Changes
1309
+
1310
+ - [#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
1311
+
1312
+ 2.0.0 moved the print components out of the barrels, and took these two with
1313
+ them. That was wrong: they are pure `Intl` helpers with no dependency on
1314
+ `@react-pdf/renderer`, `react-pdf-tailwind` or anything else, and they are used
1315
+ far outside print documents — 66 files in `nxgt-federation` alone. On 2.0.x each
1316
+ of those has to import `@nxgt/material/print`, which drags in `pdfkit` and asks
1317
+ the app to install two optional peers it has no use for.
1318
+
1319
+ They now live in `lib/lib/print-format.utils.ts`, which means:
1320
+
1321
+ ```ts
1322
+ import { formatAmount, formatDate } from '@nxgt/material'; // back
1323
+ import { formatAmount, formatDate } from '@nxgt/material/lib'; // new
1324
+ import { formatAmount, formatDate } from '@nxgt/material/print'; // unchanged
1325
+ ```
1326
+
1327
+ `@nxgt/material/print` re-exports them, so no print document changes. Additive
1328
+ everywhere, so this is a minor — and `verify:ssr` still shows `print.js` as the
1329
+ only entry that reaches the print-only dependencies.
1330
+
1331
+ - [#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`
1332
+
1333
+ `Map`, `Marker`, `LocationMarker`, `LocationPreview`, `MapActions`,
1334
+ `MinimapControl`, `LocationField`, `LocationFormField` and `useGeolocation`, the
1335
+ same eleven exports `@nxgt/map@1.0.2` had, now an entry of this package.
1336
+
1337
+ ```sh
1338
+ bun add leaflet react-leaflet @react-leaflet/core \
1339
+ @adamscybot/react-leaflet-component-marker react-geolocated
1340
+ ```
1341
+
1342
+ ```tsx
1343
+ import '@nxgt/material/map.css';
1344
+ import { LocationFormField } from '@nxgt/material/map';
1345
+ ```
1346
+
1347
+ Those five are **optional peers**, the same pattern `./print` uses for
1348
+ `@react-pdf/renderer`: an app that renders no map never has leaflet in its tree.
1349
+
1350
+ **The entry cannot be imported without a DOM**, and that is the point rather than
1351
+ a defect: `leaflet` reads `window` while it is evaluated, so an SSR app renders
1352
+ these components client-side only — behind `clientOnly` from `@nxgt/material/lib`.
1353
+ An SPA has nothing to do. `verify:ssr` now enforces three things: no other entry
1354
+ reaches leaflet, `map.js` is skipped by the import sweep with the reason printed,
1355
+ and a stale entry in that skip list fails the build.
1356
+
1357
+ `@nxgt/map` existed for this exact SSR crash. What it cost was worse than the
1358
+ crash: it declared `@nxgt/material: ^1.0.0`, so an app depending on both could not
1359
+ take the 2.x major at all. The package is deprecated in favour of this entry.
1360
+
1361
+ ### Patch Changes
1362
+
1363
+ - [#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
1364
+
1365
+ 2.0.1 shipped with the claim that npm's
1366
+ `409 Cannot publish over previously staged version` burns the version number,
1367
+ and that 2.0.0 was therefore unrecoverable. **That is wrong, and both versions
1368
+ prove it**: npm finished staging 2.0.0 and published it at 11:42 UTC on
1369
+ 2026-09-22, about forty minutes after its first attempt. 2.0.1 took about six
1370
+ minutes. Both are installable, with identical content.
1371
+
1372
+ What staging really costs is patience, and the confirmation check added in
1373
+ 2.0.1 did not have any: it waited ~35 seconds, then failed 2.0.1's own release
1374
+ *after* the publish had succeeded — so `changesets/action` never received its
1375
+ output, and the tag and the GitHub release had to be made by hand.
1376
+
1377
+ - `confirmPublished` now waits **fifteen minutes**, backing off to 30s.
1378
+ - The `isPublished` skip path **tags and announces** instead of returning
1379
+ silently, so re-running a release whose publish landed late completes it.
1380
+ - `AGENTS.md` says to wait and re-run, never to release the next patch to get
1381
+ around a 409.
1382
+
1383
+ ## 2.0.1
1384
+
1385
+ ### Patch Changes
1386
+
1387
+ - [#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
1388
+
1389
+ `bun publish` exited 0 for 2.0.0, `scripts/publish.ts` printed `published`, the
1390
+ action pushed the tag and cut the GitHub release — and npmjs never received the
1391
+ version. Nothing in the log said otherwise, because the script printed bun's
1392
+ output only on the failure path.
1393
+
1394
+ It now re-reads the registry after publishing (six attempts over ~35s, so
1395
+ propagation is not mistaken for failure), **fails the release** if the version is
1396
+ not there, and tags only after that. bun's output is printed either way.
1397
+
1398
+ The cause, surfaced by that change on the re-run: npm answered
1399
+ `409 Conflict — Cannot publish over previously staged version "2.0.0"`. The first
1400
+ attempt staged the tarball and never completed it, and bun exited 0 anyway. A
1401
+ staged version cannot be republished, so 2.0.0 is burnt and this patch is what
1402
+ carries its content to the registry.
1403
+
1404
+ ## 2.0.0
1405
+
1406
+ ### Major Changes
1407
+
1408
+ - [#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` +
1409
+ `react-pdf-tailwind` are optional peer dependencies
1410
+
1411
+ **Breaking:** the print components (`PrintHeader`, `PrintSection`, `PrintCard`,
1412
+ `PrintTable`, `PrintTotals`, `PrintBadge`, `tw`, `commonPrintStyles`, the
1413
+ formatters) no longer come from `@nxgt/material` or `@nxgt/material/components`.
1414
+ Import them from `@nxgt/material/print`, and install `@react-pdf/renderer` and
1415
+ `react-pdf-tailwind` in the app that prints — they are optional peers now.
1416
+
1417
+ Why it is worth a major: `@react-pdf/renderer` reaches `pdfkit`, which registers
1418
+ its fourteen standard fonts through
1419
+ `createRequire(import.meta.url)('#standard-fonts/<name>')` — a specifier private
1420
+ to pdfkit, called at runtime, that no bundler resolves. Any SSR consumer that
1421
+ bundled this package therefore shipped a server which threw on **every request**,
1422
+ `/health` included; three did on 2026-09-22 and each worked around it locally. An
1423
+ app that does not render PDFs now never has pdfkit in its graph at all.
1424
+
1425
+ Also in this release, for the apps that do print:
1426
+
1427
+ - `@nxgt/material/vite` exports `pdfkitStandardFonts()`, the Vite `transform` that
1428
+ bundles those fonts — one implementation instead of one per consumer. Needed
1429
+ only when the runtime image ships the bundle without a `node_modules`.
1430
+ - `bun run build` fails if any entry but `print.js` reaches those two packages,
1431
+ transitively.
1432
+ - `"sideEffects": ["**/*.css"]` is declared. It does not fix the above (measured),
1433
+ it is simply true — and the CSS entry matters: a bare `false` lets a bundler drop
1434
+ `dist/material.css`.
1435
+
1436
+ ## 1.16.6
1437
+
1438
+ ### Patch Changes
1439
+
1440
+ - [#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
1441
+
1442
+ `package.json` carried `"@nxgt/material": "."` in its own `dependencies`. It
1443
+ arrived incidentally in an i18n commit in May (`c5154f0`) and nothing in this
1444
+ repository ever used it: no file imports `@nxgt/material` by name, and
1445
+ self-referencing a package by its own `name` works through `exports` without a
1446
+ dependency entry anyway.
1447
+
1448
+ It was not inert in consumers. `bun pm pack` ships the line, so every install of
1449
+ `@nxgt/material` also resolved `@nxgt/material@file:.` — and `.` is the
1450
+ *consumer's* directory, not this package's. In one consumer's monorepo that
1451
+ materialised as a second `@nxgt/material` in `node_modules/.bun` reporting
1452
+ version `1.0.0`, which is the version of that monorepo's own root
1453
+ `package.json`, plus a permanent `"@nxgt/material/@nxgt/material":
1454
+ ["@nxgt/material@file:."]` entry in its `bun.lock` that no manifest declared and
1455
+ that `bun install` kept re-creating.
1456
+
1457
+ Nothing observably broke, because the bundled output uses relative imports and
1458
+ never resolved that alias. The reason to fix it is that it is a lie in a
1459
+ published manifest pointing at a directory this package does not own.
1460
+
1461
+ Verified: the packed manifest no longer lists itself, a fresh consumer install
1462
+ produces no `file:.` entry, the SSR-safety check passes on all 8 entries, and the
1463
+ suite is green (773 tests, 115 files).
1464
+
1465
+ ## 1.16.5
1466
+
1467
+ ### Patch Changes
1468
+
1469
+ - [#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
1470
+
1471
+ Since the markdown and code-block editors arrived, the bundle carried two
1472
+ dependencies that touch the DOM at module scope — `document.createElement("i")`
1473
+ from `decode-named-character-reference` and `new DOMParser()` from
1474
+ `hast-util-from-html-isomorphic`. Both are isomorphic packages that answer the
1475
+ `browser` export condition with a DOM implementation, and Vite's library build
1476
+ resolves that condition by default, so the browser halves were bundled in.
1477
+
1478
+ The effect on a consumer is total and immediate: any app that bundles this
1479
+ package into its server build — every SSR consumer, which is what
1480
+ `ssr.noExternal: ['@nxgt/material']` means — dies at boot with
1481
+ `ReferenceError: document is not defined`, before rendering anything. It was
1482
+ found by nxgt-ory's `verify:artifacts`, which imports the published tarball in a
1483
+ bare runtime.
1484
+
1485
+ Both are now pinned to their neutral entries, which are the same decoders built
1486
+ on lookup tables rather than on the DOM and are correct in both environments.
1487
+ `bun run build` checks this from now on: `postbuild` imports all eight entries
1488
+ with no DOM present and fails if one throws.
1489
+
1490
+ ## 1.16.4
1491
+
1492
+ ### Patch Changes
1493
+
1494
+ - [#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`.
1495
+
1496
+ ## 1.16.3
1497
+
1498
+ ### Patch Changes
1499
+
1500
+ - [#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.
1501
+
1502
+ ## 1.16.2
1503
+
1504
+ ### Patch Changes
1505
+
1506
+ - [#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.
1507
+
1508
+ ## 1.16.1
1509
+
1510
+ ### Patch Changes
1511
+
1512
+ - [#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.
1513
+
1514
+ ## 1.16.0
1515
+
1516
+ ### Minor Changes
1517
+
1518
+ - [#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.
1519
+
1520
+ ## 1.15.0
1521
+
1522
+ ### Minor Changes
1523
+
1524
+ - [#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.
1525
+
1526
+ - [#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.
1527
+
1528
+ ## 1.14.0
1529
+
1530
+ ### Minor Changes
1531
+
1532
+ - [#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.
1533
+
1534
+ ### Patch Changes
1535
+
1536
+ - [#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.
1537
+
1538
+ - [#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.
1539
+
1540
+ ## 1.13.0
1541
+
1542
+ ### Minor Changes
1543
+
1544
+ - [#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.
1545
+
1546
+ ## 1.12.3
1547
+
1548
+ ### Patch Changes
1549
+
1550
+ - [#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.
1551
+
1552
+ ## 1.12.2
1553
+
1554
+ ### Patch Changes
1555
+
1556
+ - [#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.
1557
+
1558
+ ## 1.12.1
1559
+
1560
+ ### Patch Changes
1561
+
1562
+ - [#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.
1563
+
1564
+ - [#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.
1565
+
1566
+ ## 1.12.0
1567
+
1568
+ ### Minor Changes
1569
+
1570
+ - [#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.
1571
+
1572
+ ## 1.11.0
1573
+
1574
+ ### Minor Changes
1575
+
1576
+ - [#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.
1577
+
1578
+ ## 1.10.0
1579
+
1580
+ ### Minor Changes
1581
+
1582
+ - [#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.
1583
+
1584
+ ## 1.9.0
1585
+
1586
+ ### Minor Changes
1587
+
1588
+ - [#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.
1589
+
1590
+ ## 1.8.0
1591
+
1592
+ ### Minor Changes
1593
+
1594
+ - [#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.
1595
+
1596
+ ## 1.7.0
1597
+
1598
+ ### Minor Changes
1599
+
1600
+ - [#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.
1601
+
1602
+ - [#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.
1603
+
1604
+ ## 1.6.0
1605
+
1606
+ ### Minor Changes
1607
+
1608
+ - [#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.
1609
+
1610
+ ## 1.5.0
1611
+
1612
+ ### Minor Changes
1613
+
1614
+ - [#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.
1615
+
1616
+ ## 1.4.0
1617
+
1618
+ ### Minor Changes
1619
+
1620
+ - [#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.
1621
+
1622
+ ### Patch Changes
1623
+
1624
+ - [#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.
1625
+
1626
+ ## 1.3.0
1627
+
1628
+ ### Minor Changes
1629
+
1630
+ - [#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.
1631
+
1632
+ ## 1.2.0
1633
+
1634
+ ### Minor Changes
1635
+
1636
+ - [#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.
1637
+
1638
+ ### Patch Changes
1639
+
1640
+ - [#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.
1641
+
1642
+ ## 1.1.0
1643
+
1644
+ ### Minor Changes
1645
+
1646
+ - [#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.
1647
+
1648
+ - [#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.
1649
+
1650
+ - [#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).
1651
+
1652
+ - [#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.
1653
+
1654
+ - [#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.
1655
+
1656
+ - [#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.
1657
+
1658
+ - [#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.
1659
+
1660
+ - [#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.
1661
+
1662
+ - [#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.
1663
+
1664
+ - [#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.
1665
+
1666
+ - [#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.
1667
+
1668
+ - [#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.
1669
+
1670
+ - [#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.
1671
+
1672
+ - [#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).
1673
+
1674
+ - [#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.
1675
+
1676
+ - [#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).
1677
+
1678
+ - [#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.
1679
+
1680
+ - [#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.
1681
+
1682
+ ### Patch Changes
1683
+
1684
+ - [#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.
1685
+
1686
+ - [#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.
1687
+
1688
+ - [#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.
1689
+
1690
+ - [#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).
1691
+
1692
+ - [#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.
1693
+
1694
+ - [#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.
1695
+
1696
+ - [#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.
1697
+
1698
+ - [#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.
1699
+
1700
+ - [#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.
1701
+
1702
+ - [#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.
1703
+
1704
+ - [#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 `[` / `]`.
1705
+
1706
+ - [#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.
1707
+
1708
+ - [#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.
1709
+
1710
+ - [#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).
1711
+
1712
+ - [#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.
1713
+
1714
+ - [#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.
1715
+
1716
+ ## 1.0.5
1717
+
1718
+ ### Patch Changes
1719
+
1720
+ - [#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`.
1721
+
1722
+ - [#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.
1723
+
1724
+ ## 1.0.4
1725
+
1726
+ ### Patch Changes
1727
+
1728
+ - [#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.
1729
+
1730
+ ## 1.0.3
1731
+
1732
+ ### Patch Changes
1733
+
1734
+ - [#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.
1735
+
1736
+ - [#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.
1737
+
1738
+ - [#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.
1739
+
1740
+ ## 1.0.2
1741
+
1742
+ ### Patch Changes
1743
+
1744
+ - [#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.
1745
+
1746
+ Install once, setup (CSS + sprites) as snippets, then usage by area — each
1747
+ with a copy-paste example. Private application names are gone from this
1748
+ page; they stay in AGENTS.md.
1749
+
1750
+ ## 1.0.1
1751
+
1752
+ ### Patch Changes
1753
+
1754
+ - [#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.
1755
+
1756
+ Install, every `exports` subpath, the required peers, i18n wiring, the
1757
+ parameterised app shell, and the SSR traps (`clientOnly`, no `<svg><title>`)
1758
+ now live on the page a consumer reads. The CSS `@source` line and the Font
1759
+ Awesome Pro sprite rule were already there and stay.