@nxgt/material 3.73.0 → 3.75.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +72 -0
- package/README.md +10 -7
- package/dist/{alert-dialog-A8K8zai1.js → alert-dialog-BfO_ri-N.js} +1 -1
- package/dist/{async-slice-B8933IHY.js → async-slice-roMAJVzG.js} +1 -1
- package/dist/{background-D1gC4m2R.js → background-Q4Ek3rNy.js} +3 -1
- package/dist/backgrounds/css-react/celestial-day.css +85 -0
- package/dist/backgrounds/css-react/celestial.css +11 -11
- package/dist/backgrounds.js +1 -1
- package/dist/{button-C8moFnIT.js → button-DgenqmpS.js} +1 -1
- package/dist/charts.js +3 -3
- package/dist/{chip-B6AY1E91.js → chip-CiDczNxO.js} +2 -2
- package/dist/{code-editor-DQZIKL54.js → code-editor-CohlhFsA.js} +5 -5
- package/dist/code-editor.js +5 -5
- package/dist/{code-language-items-8VL0aMKG.js → code-language-items-Cs8GLYDp.js} +2 -2
- package/dist/components/ui/collections/data-table/data-table-items-actions.css +37 -0
- package/dist/components/ui/collections/data-table/data-table-items-chevron.css +28 -0
- package/dist/components/ui/collections/data-table/data-table-items-tiles.css +7 -0
- package/dist/components/ui/collections/data-table/index.css +5 -1
- package/dist/components/ui/display/seat-ring/seat-ring.css +26 -0
- package/dist/components/ui/display/secret-card/secret-card.css +5 -1
- package/dist/components/ui/feedback/celebration/celebration.css +49 -0
- package/dist/components/ui/feedback/feedback.css +4 -0
- package/dist/components/ui/feedback/moment/moment.css +22 -0
- package/dist/{components-Cvp2F9uk.js → components-zJdfH4im.js} +16930 -16524
- package/dist/components.js +28 -28
- package/dist/{dialog-WuLTwxkr.js → dialog-AGsB3bv4.js} +4 -4
- package/dist/dnd.js +1 -1
- package/dist/{dropdown-menu-tadySeGH.js → dropdown-menu-CzR6JP31.js} +1 -1
- package/dist/editor.js +10 -10
- package/dist/{empty-state-ZYiFSSBQ.js → empty-state-DjHGMp5F.js} +2 -2
- package/dist/{field-blur-BYR0yE5L.js → field-blur-CdASxjL7.js} +1 -1
- package/dist/{field-blur-context-bEhByoK-.js → field-blur-context-kZ4SD7xu.js} +1 -1
- package/dist/{hooks-GpwD8iDu.js → hooks-Dqq8O3Az.js} +2 -2
- package/dist/hooks.js +2 -2
- package/dist/{hover-card-BowaI_ra.js → hover-card-BPKqte4a.js} +1 -1
- package/dist/i18n.js +3 -3
- package/dist/{icon-badge-B1B0Lg02.js → icon-badge-B9uvDoyY.js} +3 -3
- package/dist/{icon-button-BbGU17cb.js → icon-button-Cr-DyDGg.js} +1 -1
- package/dist/lib/backgrounds/background/available.d.ts +4 -4
- package/dist/lib/backgrounds/background/background.d.ts +1 -1
- package/dist/lib/backgrounds/background/types.d.ts +1 -1
- package/dist/lib/components/ui/collections/data-table/layout-types.d.ts +13 -0
- package/dist/lib/components/ui/collections/data-table/partials/layouts/data-table-item-actions.d.ts +12 -0
- package/dist/lib/components/ui/collections/data-table/partials/layouts/data-table-item-title.d.ts +2 -1
- package/dist/lib/components/ui/collections/data-table/select-checkbox.d.ts +2 -1
- package/dist/lib/components/ui/collections/filter/bar/filter-bar-classes.d.ts +2 -2
- package/dist/lib/components/ui/collections/filter/bar/filter-bar-view.d.ts +1 -1
- package/dist/lib/components/ui/collections/filter/facets/filter-facets.d.ts +1 -1
- package/dist/lib/components/ui/collections/filter/query/types.d.ts +6 -0
- package/dist/lib/components/ui/collections/filter/query/view-types.d.ts +1 -0
- package/dist/lib/components/ui/collections/filter/sections/chips/filter-chip-body.d.ts +1 -1
- package/dist/lib/components/ui/collections/filter/sections/filter-search.d.ts +1 -0
- package/dist/lib/components/ui/collections/filter/sections/filter-sections-classes.d.ts +8 -0
- package/dist/lib/components/ui/collections/server-table/partials/server-table-band-classes.d.ts +3 -2
- package/dist/lib/components/ui/collections/server-table/partials/server-table-empty-page.d.ts +4 -0
- package/dist/lib/components/ui/collections/server-table/server-table-classes.d.ts +8 -2
- package/dist/lib/components/ui/collections/server-table/types.d.ts +11 -1
- package/dist/lib/components/ui/display/change-history/partials/changes-collapse.d.ts +2 -1
- package/dist/lib/components/ui/display/highlighted-text/highlight-ranges.d.ts +11 -0
- package/dist/lib/components/ui/display/highlighted-text/highlighted-text.d.ts +16 -0
- package/dist/lib/components/ui/display/highlighted-text/index.d.ts +2 -0
- package/dist/lib/components/ui/display/index.d.ts +1 -0
- package/dist/lib/components/ui/display/list-tile/list-tile.d.ts +10 -1
- package/dist/lib/components/ui/display/list-tile/partials/list-tile-box.d.ts +16 -0
- package/dist/lib/components/ui/display/list-tile/partials/list-tile-classes.d.ts +3 -1
- package/dist/lib/components/ui/display/list-tile/partials/list-tile-line.d.ts +1 -1
- package/dist/lib/components/ui/display/list-tile/partials/list-tile-row.d.ts +1 -1
- package/dist/lib/components/ui/display/seat-ring/types.d.ts +6 -0
- package/dist/lib/components/ui/display/secret-card/types.d.ts +6 -0
- package/dist/lib/components/ui/feedback/celebration/celebration.d.ts +24 -0
- package/dist/lib/components/ui/feedback/celebration/index.d.ts +1 -0
- package/dist/lib/components/ui/feedback/countdown-timer/countdown-ring.d.ts +1 -1
- package/dist/lib/components/ui/feedback/countdown-timer/countdown-timer.d.ts +2 -1
- package/dist/lib/components/ui/feedback/index.d.ts +2 -0
- package/dist/lib/components/ui/feedback/moment/index.d.ts +1 -0
- package/dist/lib/components/ui/feedback/moment/moment-body.d.ts +9 -0
- package/dist/lib/components/ui/feedback/moment/moment.d.ts +33 -0
- package/dist/lib/components/ui/fields/choice-list/choice-list-control.d.ts +17 -0
- package/dist/lib/components/ui/fields/choice-list/choice-list-focus.d.ts +7 -0
- package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
- package/dist/lib/components/ui/fields/number-field/number-stepper.d.ts +11 -0
- package/dist/lib/components/ui/fields/quantity-field/quantity-row.d.ts +8 -0
- package/dist/lib/components/ui/overlays/command/command-dialog.d.ts +8 -1
- package/dist/lib/components/ui/primitives/tabs/tabs-overflow-classes.d.ts +1 -1
- package/dist/lib/components/ui/selection/chip-group-field/chip-group-field.d.ts +2 -2
- package/dist/lib/components/ui/selection/chip-group-field/partials/chip-group-option.d.ts +5 -3
- package/dist/lib/components/ui/selection/choice-group/choice-group-root.d.ts +3 -1
- package/dist/lib/components/ui/widgets/chat/chat-thread-header-parts.d.ts +17 -0
- package/dist/lib/components/ui/widgets/chat/chat-thread-header.d.ts +9 -3
- package/dist/lib/components/ui/widgets/chat/proposal/chat-tool-activity.d.ts +3 -1
- package/dist/lib/i18n/resources/en/en.d.ts +4 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +5 -1
- package/dist/lib/i18n/resources/fr/fr.d.ts +4 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +5 -1
- package/dist/lib/i18n/resources/resources.d.ts +8 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib/illustrations-game/index.d.ts +6 -5
- package/dist/lib.js +1 -1
- package/dist/main.js +42 -42
- package/dist/map.js +8 -8
- package/dist/material.css +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/print.js +1 -1
- package/dist/{public-CFj2qn7l.js → public-DC9VQw4Z.js} +9 -9
- package/dist/{rich-code-block-B7Teg7-e.js → rich-code-block-DBSL9zEN.js} +5 -5
- package/dist/{sortable-provider-DU2zp_jn.js → sortable-provider-C4tk5esY.js} +2 -2
- package/dist/{spinner-C5Kb6MWu.js → spinner-B2MubVWb.js} +1 -1
- package/dist/theme-picker.js +2 -2
- package/dist/{theme-toggle-B1JZg7mU.js → theme-toggle-BInlu21G.js} +1 -1
- package/dist/{toolbar-items-m_1FTFPQ.js → toolbar-items-CRJSx1MU.js} +3 -3
- package/dist/{translate-material-D1ndrd7a.js → translate-material-pVlPJ1v4.js} +1 -1
- package/dist/{translator-CtEC_AQa.js → translator-BoYCward.js} +10 -2
- package/dist/{typography-prQHb3gI.js → typography-B6YMbCge.js} +1 -1
- package/dist/{use-chat-composer-BAoUsyKN.js → use-chat-composer-BvWQogJe.js} +2046 -2037
- package/dist/{use-heading-spy-Bzj5mIUZ.js → use-heading-spy-B_Z4ArDh.js} +4 -4
- package/dist/{use-material-translation-C6H0B4kE.js → use-material-translation-CIS2n3RB.js} +1 -1
- package/dist/{use-return-focus-CkUX1HFl.js → use-return-focus-DNQtADAR.js} +1 -1
- package/docs/components/data-display.md +60 -6
- package/docs/components/data-table-filter-and-lists.md +22 -6
- package/docs/components/feedback-and-status.md +69 -1
- package/docs/components/form-fields.md +22 -3
- package/docs/components/navigation-and-layout.md +1 -1
- package/docs/components/overlays-and-menus.md +2 -1
- package/docs/components/primitives.md +2 -2
- package/docs/components/rich-widgets.md +3 -2
- package/docs/components/selection-and-choice.md +3 -3
- package/docs/getting-started.md +1 -1
- package/docs/guides/app-shell.md +1 -1
- package/docs/guides/backgrounds.md +23 -11
- package/docs/guides/cards-and-tiles.md +42 -3
- package/docs/guides/i18n.md +4 -1
- package/docs/guides/icon-motifs.md +7 -7
- package/docs/guides/illustrations.md +6 -5
- package/docs/guides/theming.md +5 -1
- package/docs/previews/backgrounds-day-and-dawn-sky.webp +0 -0
- package/docs/previews/data-display-highlightedtext.webp +0 -0
- package/docs/previews/feedback-status-illustration-game.webp +0 -0
- package/docs/roadmap.md +1 -0
- package/docs/troubleshooting.md +44 -4
- package/lib/backgrounds/css-react/celestial-day.css +85 -0
- package/lib/backgrounds/css-react/celestial.css +11 -11
- package/lib/components/ui/collections/data-table/data-table-items-actions.css +37 -0
- package/lib/components/ui/collections/data-table/data-table-items-chevron.css +28 -0
- package/lib/components/ui/collections/data-table/data-table-items-tiles.css +7 -0
- package/lib/components/ui/collections/data-table/index.css +5 -1
- package/lib/components/ui/display/seat-ring/seat-ring.css +26 -0
- package/lib/components/ui/display/secret-card/secret-card.css +5 -1
- package/lib/components/ui/feedback/celebration/celebration.css +49 -0
- package/lib/components/ui/feedback/feedback.css +4 -0
- package/lib/components/ui/feedback/moment/moment.css +22 -0
- package/lib/theme-picker/theme-picker.css +1 -0
- package/lib/theme-picker/theme-tile-textures-day.css +43 -0
- package/lib/themes/teal.css +20 -18
- package/package.json +7 -3
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { t as e } from "./utils-BLfntITJ.js";
|
|
2
|
-
import { a as t, c as n, d as r, i, n as a, r as o, t as s, u as c } from "./alert-dialog-
|
|
3
|
-
import { a as l } from "./button-
|
|
4
|
-
import { i as u } from "./translate-material-
|
|
5
|
-
import { t as d } from "./use-material-translation-
|
|
2
|
+
import { a as t, c as n, d as r, i, n as a, r as o, t as s, u as c } from "./alert-dialog-BfO_ri-N.js";
|
|
3
|
+
import { a as l } from "./button-DgenqmpS.js";
|
|
4
|
+
import { i as u } from "./translate-material-pVlPJ1v4.js";
|
|
5
|
+
import { t as d } from "./use-material-translation-CIS2n3RB.js";
|
|
6
6
|
import { _ as f, g as p, h as m, n as h, v as g, y as _ } from "./number.utils-BG_eEt5g.js";
|
|
7
7
|
import { useCallback as v, useEffect as y, useMemo as b, useRef as x, useState as S } from "react";
|
|
8
8
|
import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { o as e, r as t } from "./dist-DVRQkaTK.js";
|
|
2
2
|
import { a as n, c as r, i, l as a, o, r as s, s as c } from "./dist-lmHmz_bu.js";
|
|
3
|
-
import { C as l, S as u, w as d } from "./spinner-
|
|
3
|
+
import { C as l, S as u, w as d } from "./spinner-B2MubVWb.js";
|
|
4
4
|
import { c as f, i as p, l as m, o as h, r as g, s as ee } from "./in-dialog-Vwkyp-M5.js";
|
|
5
5
|
import { r as _ } from "./escape-owner-DpaP0lm1.js";
|
|
6
6
|
import * as v from "react";
|
|
@@ -13,6 +13,7 @@ from `@nxgt/material/components`.
|
|
|
13
13
|
| <img src="../previews/data-display-shape.webp" width="96" alt=""> | [Shape](#shape) | Material 3 Expressive's shapes as a backdrop, an icon container or a mask for a photo |
|
|
14
14
|
| <img src="../previews/data-display-badge.webp" width="96" alt=""> | [Badge](#badge) | Status pill, 20 px, in seven tones, soft or outlined, with an optional decorative `icon` before the words |
|
|
15
15
|
| <img src="../previews/data-display-countbadge.webp" width="96" alt=""> | [CountBadge](#countbadge) | Count in the corner of its children |
|
|
16
|
+
| <img src="../previews/data-display-highlightedtext.webp" width="96" alt=""> | [HighlightedText](#highlightedtext) | A string with the words of a search query in a themed `<mark>` |
|
|
16
17
|
| <img src="../previews/data-display-dayseparator.webp" width="96" alt=""> | [DaySeparator](#dayseparator) | "Today", "Yesterday" or the date between two hairlines |
|
|
17
18
|
| <img src="../previews/data-display-periodcards.webp" width="96" alt=""> | [PeriodCards](#periodcards) | Consecutive date ranges (terms, sprints) as cards, with a current marker and an optional link per card |
|
|
18
19
|
| <img src="../previews/data-display-chip.webp" width="96" alt=""> | [Chip](#chip) | Three looks and a dashed add pill, with slots, a selected state and a dismiss button |
|
|
@@ -147,6 +148,7 @@ import { SeatRing } from '@nxgt/material/components';
|
|
|
147
148
|
seats={[
|
|
148
149
|
{ id: 'ada', label: 'Ada Example', status: { label: 'Ready', tone: 'success' }, badge: 'Host' },
|
|
149
150
|
{ id: 'bo', label: 'Bo Sample', status: { label: 'Away', tone: 'warning' } },
|
|
151
|
+
{ id: 'cy', label: 'Cy Demo', out: true },
|
|
150
152
|
{ id: 'free', label: 'Free', empty: true },
|
|
151
153
|
]}
|
|
152
154
|
highlightedId="ada"
|
|
@@ -159,7 +161,7 @@ import { SeatRing } from '@nxgt/material/components';
|
|
|
159
161
|
|
|
160
162
|
| Prop | Type | Default | Notes |
|
|
161
163
|
| --- | --- | --- | --- |
|
|
162
|
-
| `seats` | `Seat[]` | required | `{ id, label, avatar?: { src?, fallback? }, status?: { label, tone? }, badge?, disabled?, empty? }`; clockwise from the top |
|
|
164
|
+
| `seats` | `Seat[]` | required | `{ id, label, avatar?: { src?, fallback? }, status?: { label, tone? }, badge?, disabled?, out?, empty? }`; clockwise from the top |
|
|
163
165
|
| `layout` | `'ring' \| 'grid'` | `'ring'` | `ring` falls back to the grid in a narrow box and past the seat limit (10 seats) |
|
|
164
166
|
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | seat width 4.75, 6 or 8rem; ring from a box of 32, 38 or 46rem; at most 10 seats on the circle |
|
|
165
167
|
| `selectionMode` | `'none' \| 'single' \| 'multiple'` | `'none'` | `none`: a list; otherwise a listbox |
|
|
@@ -183,6 +185,7 @@ no `style` attribute and a strict CSP is fine.
|
|
|
183
185
|
keys, Home and End move the focus (an empty seat is skipped); Space or Enter
|
|
184
186
|
picks, and pressing the chosen seat again clears it.
|
|
185
187
|
- A seat is named by its label and its status text, never by colour alone.
|
|
188
|
+
- `out: true` marks an eliminated seat: it stays visible, dimmed, with the name struck through, the avatar greyed and the word "Out" (`material.seat-ring.out`, "Éliminé" in French) written in it, so the word is part of the seat's name (label, status, "Out", then the badge); it stays pickable unless `disabled`. An empty seat ignores it. Seat status words ("Eliminated") remain the app's own.
|
|
186
189
|
- In a listbox, empty and `disabled` seats are `aria-disabled`, and a disabled
|
|
187
190
|
seat can be reached but not picked (in a list, `disabled` only dims the seat).
|
|
188
191
|
- Put no button in a selectable seat: interactive content (an "Invite" action
|
|
@@ -228,6 +231,7 @@ import { Button, SecretCard } from '@nxgt/material/components';
|
|
|
228
231
|
| `revealed` + `onRevealedChange` | `boolean` + `(revealed) => void` | - | controlled |
|
|
229
232
|
| `defaultRevealed`, `onRevealedChange` | `boolean`, `(revealed) => void` | `false` | uncontrolled; never together with `revealed` |
|
|
230
233
|
| `autoHideMs` | `number` | - | hides again after this long shown; also calls `onRevealedChange(false)` |
|
|
234
|
+
| `size` | `'default' \| 'sm'` | `'default'` | `sm` is the compact card for a phone: the faces lose their 6rem minimum height, the padding and gap tighten (`data-size`) |
|
|
231
235
|
| `disabled` | `boolean` | `false` | the card cannot be revealed; a revealed card can always be hidden |
|
|
232
236
|
| `labels` | `Partial<{ reveal, revealed, hidden }>` | `material.secret-card.*` | the button's name ("Show secret") and the announcements |
|
|
233
237
|
|
|
@@ -417,6 +421,35 @@ import { CountBadge, IconButton } from '@nxgt/material/components';
|
|
|
417
421
|
|
|
418
422
|
---
|
|
419
423
|
|
|
424
|
+
## HighlightedText
|
|
425
|
+
|
|
426
|
+

|
|
427
|
+
|
|
428
|
+
A string with the words of a search query in a themed `<mark>`. Case, accents and ligatures are folded (`œ` is `oe`, `æ` is `ae`, `ß` is `ss`), and a query word matches at the start of a word, as a search engine's normaliser does.
|
|
429
|
+
|
|
430
|
+
```tsx
|
|
431
|
+
import { HighlightedText, highlightRanges } from '@nxgt/material/components';
|
|
432
|
+
|
|
433
|
+
<HighlightedText text="Œuvres d'été" query="oeuvre ete" />
|
|
434
|
+
// <mark>Œuvre</mark>s d'<mark>été</mark>
|
|
435
|
+
|
|
436
|
+
// For a table or a custom renderer: offsets into the original text.
|
|
437
|
+
highlightRanges("Œuvres d'été", 'oeuvre ete'); // [[0, 5], [9, 12]]
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
| Prop | Type | Default | Effect |
|
|
441
|
+
| --- | --- | --- | --- |
|
|
442
|
+
| `text` | `string` | — | the string to show |
|
|
443
|
+
| `query` | `string` | — | search words; a blank query marks nothing |
|
|
444
|
+
| `locale` | `string` | runtime's | language for lowercasing |
|
|
445
|
+
| span props | | | `className` and the other `span` props go to the wrapping `span` |
|
|
446
|
+
|
|
447
|
+
`highlightRanges(text, query, locale?)` is pure and returns sorted, non-overlapping `[start, end)` offsets. The marks use theme tokens (readable in dark mode) and the system `Mark` colours in forced colours.
|
|
448
|
+
|
|
449
|
+
**Storybook:** `Data display/HighlightedText`
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
420
453
|
## DaySeparator
|
|
421
454
|
|
|
422
455
|

|
|
@@ -974,15 +1007,16 @@ A tile with an `href` ends with a discreet chevron after `trailing`: decorative
|
|
|
974
1007
|
| `linkComponent` | `AppShellLinkComponent` | plain `<a>` | the link element, as in the app shell: takes `href`, spreads every other prop, forwards `ref`. Defaults to the [`LinkComponentProvider`](./primitives.md#linkcomponentprovider)'s link when one is set. |
|
|
975
1008
|
| `onClick` | `MouseEventHandler` | — | renders a `<button>` |
|
|
976
1009
|
| `selected` | `boolean` | `false` | the current row: drawn selected; a link gets `aria-current="page"` (your own `aria-current` replaces it), a button `aria-pressed` |
|
|
977
|
-
| `toggle` | `Switch` props | — | makes the tile a switch row (see below): the whole row toggles a Switch at its end; `href`, `onClick`, `actions`, `chips`, `chevron` and `linkComponent` are refused beside it by type |
|
|
1010
|
+
| `toggle` | `Switch` props | — | makes the tile a switch row (see below): the whole row toggles a Switch at its end; `href`, `onClick`, `actions`, `chips`, `content`, `chevron` and `linkComponent` are refused beside it by type |
|
|
978
1011
|
| `size` | `'sm' \| 'md'` | `'md'` | `sm` is the dense row (with `chips`, the record row, see below) |
|
|
979
1012
|
| `leading`, `trailing`, `subtitle` | `ReactNode` | — | slots; `trailing` sits inside the link, so it holds decoration only |
|
|
980
1013
|
| `actions` | `ReactNode` | — | buttons at the end of the tile, outside the link, vertically centred (`data-slot="item-actions"`) |
|
|
981
1014
|
| `chips` | `ReactNode` | — | tags after the text, in the compact record row (see below); inside the link, so decoration only; part of the row's accessible name, keep labels short |
|
|
1015
|
+
| `content` | `ReactNode` | — | full-width content under the title row, inside the tile's box and outside its link or button: it may hold inputs and buttons (an expected value beside an editable field), and its clicks do not reach `onClick`. Not available on a `toggle` row |
|
|
982
1016
|
| `variant` | `'tonal' \| 'divided'` | `'tonal'` | `divided`: flat rows with no tonal surface, a hover tint and the record-row layout; the hairline comes with `as="li"` |
|
|
983
1017
|
| `inset` | `boolean` | `false` | With `variant="divided"`: 16px of inline padding (M3's list-item inset) in place of 4px, for a list inside a `Card`; the hairlines on the `<li>` still run edge to edge. Ignored by `tonal` |
|
|
984
1018
|
| `as` | `'li'` | — | renders the tile inside a `<li>`, for a `<ul>` you own |
|
|
985
|
-
| `disabled` | `boolean` | — | disables the link or button, and the `actions` with it |
|
|
1019
|
+
| `disabled` | `boolean` | — | disables the link or button, and the `actions` and `content` with it (both become inert) |
|
|
986
1020
|
|
|
987
1021
|
With a router, pass `linkComponent` so a click navigates on the client instead of reloading the page (React Router's `Link` takes `to`, so wrap it):
|
|
988
1022
|
|
|
@@ -999,7 +1033,27 @@ export function SettingsLink() {
|
|
|
999
1033
|
}
|
|
1000
1034
|
```
|
|
1001
1035
|
|
|
1002
|
-
A link tile's `trailing` sits inside its `<a>`, which cannot hold buttons. Put edit / remove / menu buttons in `actions`: they render outside the link, in the tile's box, while the link still covers the whole tile (a click on the text navigates, a click on a button does not) and hover and selected styles stay on the tile. Tab goes to the link, then to the buttons
|
|
1036
|
+
A link tile's `trailing` sits inside its `<a>`, which cannot hold buttons. Put edit / remove / menu buttons in `actions`: they render outside the link, in the tile's box, while the link still covers the whole tile (a click on the text navigates, a click on a button does not) and hover and selected styles stay on the tile. Tab goes to the link, then to the buttons, then to the controls of `content`. A tile without `href` renders `actions` the same way. With `actions`, `className`, `style` and `data-*` go on the tile's box and every other prop (`aria-label`, `id`, handlers…) on the link or button; `actions={null}` (or `false`) is no actions. and the text of a link tile with actions cannot be selected (the link is stretched over the tile).
|
|
1037
|
+
|
|
1038
|
+
**Content under the row.** `content` renders full-width under the title row, inside the tile's box and outside its link or button, so it may hold inputs and buttons (an expected value beside an editable field). Its clicks never reach the tile's `onClick`.
|
|
1039
|
+
|
|
1040
|
+
```tsx
|
|
1041
|
+
<ul className="m-0 list-none p-0">
|
|
1042
|
+
<ListTile
|
|
1043
|
+
as="li"
|
|
1044
|
+
variant="divided"
|
|
1045
|
+
title="Item"
|
|
1046
|
+
subtitle="SKU-1 · LOT-2"
|
|
1047
|
+
actions={<IconButton label="Remove" onClick={remove}><Trash2 /></IconButton>}
|
|
1048
|
+
content={
|
|
1049
|
+
<label className="flex items-center justify-between gap-2">
|
|
1050
|
+
Expected 12
|
|
1051
|
+
<NumberField aria-label="Counted" value={counted} onChange={setCounted} />
|
|
1052
|
+
</label>
|
|
1053
|
+
}
|
|
1054
|
+
/>
|
|
1055
|
+
</ul>
|
|
1056
|
+
```
|
|
1003
1057
|
|
|
1004
1058
|
```tsx
|
|
1005
1059
|
import { Pencil } from 'lucide-react';
|
|
@@ -1070,7 +1124,7 @@ function GuideList({ guides }: { guides: { to: string; title: string }[] }) {
|
|
|
1070
1124
|
}
|
|
1071
1125
|
```
|
|
1072
1126
|
|
|
1073
|
-
**A switch row.** `toggle` takes a Switch's props (`checked`, `defaultChecked`, `onCheckedChange`, `name`, `thumbIcons`…). The tile is then a `<label>` for the switch, so a press anywhere on the row toggles it; the switch is the one tab stop (Space or Enter), named by `title` and described by `subtitle`, and `disabled` on the tile disables it. A switch row is neither a link nor a button: `href`, `onClick`, `actions` and `chevron` do not apply, and `trailing` sits before the switch.
|
|
1127
|
+
**A switch row.** `toggle` takes a Switch's props (`checked`, `defaultChecked`, `onCheckedChange`, `name`, `thumbIcons`…). The tile is then a `<label>` for the switch, so a press anywhere on the row toggles it; the switch is the one tab stop (Space or Enter), named by `title` and described by `subtitle`, and `disabled` on the tile disables it. A switch row is neither a link nor a button: `href`, `onClick`, `actions`, `content` and `chevron` do not apply, and `trailing` sits before the switch.
|
|
1074
1128
|
|
|
1075
1129
|
```tsx
|
|
1076
1130
|
const [on, setOn] = useState(true);
|
|
@@ -1246,7 +1300,7 @@ yours, the component names no domain.
|
|
|
1246
1300
|
| `kinds` | `Record<string, ChangeHistoryKindOverride>` | built-ins | `label`, `verb`, `icon` (`null` removes it), `tone` per kind; `creation` (default true for `created`) makes an entry list values only |
|
|
1247
1301
|
| `variant` | `'rail' \| 'cards'` | `'rail'` | the look |
|
|
1248
1302
|
| `groupBy` | `'day' \| 'none'` | `'day'` | a heading per day (sticky on the rail), or one list in the order given |
|
|
1249
|
-
| `collapseAfter` | `number` | `3` | an entry with more changes collapses behind "N fields changed" (a creation: "N fields set") and a preview of the names, as a button with `aria-expanded
|
|
1303
|
+
| `collapseAfter` | `number` | `3` | an entry with more changes collapses behind "N fields changed" (a creation: "N fields set") and a preview of the first three names, as a button with `aria-expanded`. The preview wraps instead of clipping with an ellipsis, and a screen reader hears the names behind the "+N" |
|
|
1250
1304
|
| `filter` | `ReactNode` | — | a slot above the list, for a `Filter` bar |
|
|
1251
1305
|
| `loading` | `boolean` | `false` | skeleton entries |
|
|
1252
1306
|
| `hasMore` | `boolean` | — | shows the Load more button |
|
|
@@ -96,8 +96,8 @@ table enables, for typing your own column defs.
|
|
|
96
96
|
| `data` | `T[]` | — | rows; sorting, filtering and pagination run client-side |
|
|
97
97
|
| `layout` | `'table' \| 'cards' \| 'tiles' \| { phone, wide }` | `'table'` | `'cards'` lays the rows out as a list of cards, `'tiles'` as one column in a single surface ([Cards and tiles](../guides/cards-and-tiles.md)); both ignore `virtual`, `renderExpandedRow` and `showViewOptions` |
|
|
98
98
|
| `cardDensity` | `'comfortable' \| 'compact'` | `'comfortable'` | `compact` fits cards of at least `--nxgt-cards-min` (15rem), up to four a row, with a tighter padding; `layout="cards"` only ([Cards and tiles](../guides/cards-and-tiles.md#columns)) |
|
|
99
|
-
| `renderCard` | `(row, { Title, Row, selected, locked, reason }) => ReactNode` | — | a card's content, required with `layout="cards"`; `Title` is the card's one link and its name; `Row` (the compact record row) is also passed, laid out inside the card's own padding, with no height of its own |
|
|
100
|
-
| `renderTile` | `(row, { Title, Row, selected, locked, reason }) => ReactNode` | — | a tile's content, required with `layout="tiles"`; same `Title` as a card, or `Row`, the compact record row (title, subtitle, chips, trailing facts; see the cards and tiles guide) |
|
|
99
|
+
| `renderCard` | `(row, { Title, Actions, Row, selected, locked, reason }) => ReactNode` | — | a card's content, required with `layout="cards"`; `Title` is the card's one link and its name; `Actions` is the trailing action slot (outside the link, at least 44px); `Row` (the compact record row) is also passed, laid out inside the card's own padding, with no height of its own |
|
|
100
|
+
| `renderTile` | `(row, { Title, Actions, Row, selected, locked, reason }) => ReactNode` | — | a tile's content, required with `layout="tiles"`; same `Title` as a card, or `Row`, the compact record row (title, subtitle, chips, trailing facts; see the cards and tiles guide) |
|
|
101
101
|
| `renderExpandedRow` | `(row) => ReactNode` | — | makes rows expandable; pair with the `expand` column |
|
|
102
102
|
| `showViewOptions` | `boolean` | — | column visibility menu above the table |
|
|
103
103
|
| `actions` | `(table, controls) => ReactNode` | — | toolbar content with access to the table (selected rows…); disabled while `loading`. `controls.focusTable()` moves the focus to the table |
|
|
@@ -1018,7 +1018,7 @@ export default function Students() {
|
|
|
1018
1018
|
table: [Cards and tiles](../guides/cards-and-tiles.md)).
|
|
1019
1019
|
|
|
1020
1020
|
Its props are `ServerTableProps<T>`; `filter` is a `ServerTableFilter`,
|
|
1021
|
-
`empty`
|
|
1021
|
+
`empty` an optional `ServerTableEmpty`, and `bulkActions` gets a
|
|
1022
1022
|
`ServerTableSelection`.
|
|
1023
1023
|
|
|
1024
1024
|
| Prop | Type | Default | Effect |
|
|
@@ -1042,10 +1042,11 @@ Its props are `ServerTableProps<T>`; `filter` is a `ServerTableFilter`,
|
|
|
1042
1042
|
| `loadMoreLabel` | `string` | "Load more" | with `footer="load-more"`: the button's label |
|
|
1043
1043
|
| `hideOnSinglePage` | `boolean` | `false` | hides the footer's range and Previous / Next when the result is one page (no previous, no next), in the table, cards and tiles layouts; the count is still announced by the live region but no longer shown, so put it in `description` or keep the footer. With `bulkActions` the footer stays for "N rows selected", without its pager |
|
|
1044
1044
|
| `bandActions` | `'stacked' \| 'inline'` | `'stacked'` | with `filter`, where `actions` sit below 768px: `'stacked'` gives them a row above the search; `'inline'` keeps them on the search's row (the search takes the room left), for a lone icon action such as a "+". An action wider than 3.5rem wraps under the search. From 768px both are the same. With a cards or tiles list on a phone, neither applies: [the two-row header](#the-phone-header-of-a-list-in-cards-or-tiles) puts the actions on its second row |
|
|
1045
|
+
| `surface` | `'card' \| 'none'` | `'card'` | the list's own surface: `'card'` a translucent background and 16px of padding (below 768px neither: the layout owns the page gutter and the tiles sit at it, so a `ServerTable` outside a layout that adds a gutter now sits flush with the screen edge below 768px and the caller wraps it in its own padding); `'none'` neither, for a `ServerTable` placed in a `Card`, a page section or any surface that already frames it, so the screen shows one surface, not a frame inside a frame (the tiles keep their own grouped border) |
|
|
1045
1046
|
| `filterRef` | `Ref<ServerTableFilterHandle>` | — | with `filter`: `filterRef.current.focus()` focuses the Filter's first control, the search, else the first quick filter, "Add filter" or "Filters (N)" the width shows |
|
|
1046
1047
|
| `sorting` | `createSorting(…)` | — | headers sorted from the URL; without it, no column is sortable (the server sorts, so pass `sorting` to let people sort), and the phone list bar shows only for selection |
|
|
1047
1048
|
| `filters` | `tableFilters(model)` | — | without `filter`: "nothing matches" and its clear |
|
|
1048
|
-
| `empty` | `ServerTableEmpty`: `{ none, noMatch, clear?, headingLevel?, iconStyle? }` |
|
|
1049
|
+
| `empty` | `ServerTableEmpty`: `{ none, noMatch, clear?, headingLevel?, iconStyle? }` | the `DataTable` default ("No data available", or "No results" when filtered) | the empty page. Leave it out when your page answers "nothing yet" and "nothing matches" itself and renders the table only when there are rows; the failure with Retry and the page past the last show either way. When given: `none` and `noMatch` are a title or a `ListEmptyPage` (`{ title, description?, illustration?, action? }`); `headingLevel` (default 2) is the level of its title and of the error's; `iconStyle` styles the scenes (see below) |
|
|
1049
1050
|
| `error` | `ServerTableError \| null` | — | the fetch failed: the failure and a Retry replace the rows |
|
|
1050
1051
|
| `onRetry` | `() => unknown` | — | the error's Retry; a returned promise keeps it loading |
|
|
1051
1052
|
| `onClearFilters` | `() => void` | focus the table | without `filter`, after "clear" |
|
|
@@ -1054,7 +1055,17 @@ Its props are `ServerTableProps<T>`; `filter` is a `ServerTableFilter`,
|
|
|
1054
1055
|
| `pageHref` | `(params) => string` | `` `?${params}` `` | Previous / Next hrefs |
|
|
1055
1056
|
| `pagingNames` | `{ after?, trail? }` | `after`, `trail` | the cursor's params |
|
|
1056
1057
|
| `resultsLabel` | `(count) => string` | "N results" | without `filter`: what the list's region says. The page past the last has its own words: `material.server-table.past-end`, `past-end-hint`, `first-page` and `past-end-status` |
|
|
1057
|
-
| `className` | `string` | — | on the list's surface |
|
|
1058
|
+
| `className` | `string` | — | on the list's surface (below 768px the list has no padding or background of its own) |
|
|
1059
|
+
|
|
1060
|
+
A `ServerTable` in a `Card` shows one surface with `surface="none"`:
|
|
1061
|
+
|
|
1062
|
+
```tsx
|
|
1063
|
+
<Card>
|
|
1064
|
+
<CardContent>
|
|
1065
|
+
<ServerTable {...props} surface="none" />
|
|
1066
|
+
</CardContent>
|
|
1067
|
+
</Card>
|
|
1068
|
+
```
|
|
1058
1069
|
|
|
1059
1070
|
```tsx
|
|
1060
1071
|
// A lone icon action stays on the search's row on a phone.
|
|
@@ -1936,8 +1947,13 @@ function Notifications({ items }: { items: Notification[] }) {
|
|
|
1936
1947
|
the loader's own; see [A field's default](#a-fields-default)),
|
|
1937
1948
|
`resultCount`, `loading`; every other prop of `Filter variant="bar"`
|
|
1938
1949
|
(`quick`, `counts`, `previewCount`, `fieldProps`, `density`, `shortcuts`,
|
|
1939
|
-
`searchApply`, `searchMinLength`, `errors`, `label`...) passes through. There is no `query`,
|
|
1950
|
+
`searchApply`, `searchMinLength`, `searchAutoFocus`, `errors`, `label`...) passes through. There is no `query`,
|
|
1940
1951
|
`defaultQuery` nor `onQueryChange`: the URL is the query.
|
|
1952
|
+
- **Focus on arrival.** `searchAutoFocus` puts focus in the search field when
|
|
1953
|
+
the bar mounts (React's `autoFocus`, so it is right on the server too). Use it
|
|
1954
|
+
on a search page reached with an empty query; leave it off on a list page,
|
|
1955
|
+
where it would take focus from the page. `/` still focuses the field. `ServerTable`'s `filter` does not take
|
|
1956
|
+
`searchAutoFocus`, on purpose: a list page should not steal focus.
|
|
1941
1957
|
- **One write.** An edit writes the search, every `f=` and the aliased params,
|
|
1942
1958
|
drops the `reset` ones and keeps the page's others (a tab, a sort).
|
|
1943
1959
|
`onParamsChange` receives the next params for your router; the bar then
|
|
@@ -14,6 +14,8 @@ centre.
|
|
|
14
14
|
| <img src="../previews/feedback-status-stepupdialog.webp" width="96" alt=""> | [StepUpDialog](#stepupdialog) | "Confirm it's you": a dialog that asks for a one-time code, driven by props |
|
|
15
15
|
| <img src="../previews/feedback-status-unsaveddialog.webp" width="96" alt=""> | [UnsavedDialog](#unsaveddialog) | "Leave without saving?": a destructive confirm with Stay focused, used by the unsaved-changes guards |
|
|
16
16
|
| <img src="../previews/feedback-status-stalenotice.webp" width="96" alt=""> | [StaleNotice](#stalenotice) | "This record changed": a warning that another write came first, with one Reload action and no overwrite |
|
|
17
|
+
| | [Celebration](#celebration) | A one-shot confetti burst for a win or a reveal: plays once, never loops, theme colours, nothing under reduced motion |
|
|
18
|
+
| | [Moment](#moment) | A full-screen one-off announcement (a player is eliminated, a role revealed): illustration, title, description and action, dismissed by Escape, a tap, its action or a timer |
|
|
17
19
|
| <img src="../previews/feedback-status-emptystate.webp" width="96" alt=""> | [EmptyState](#emptystate) | Illustration, title, description and action, by kind (empty, search, not-found, unauthorized) |
|
|
18
20
|
| <img src="../previews/feedback-status-pageloadfailed.webp" width="96" alt=""> | [PageLoadFailed](#pageloadfailed) | An empty state for a page opened at a cursor that failed, with a link back to the start |
|
|
19
21
|
| <img src="../previews/feedback-status-errorstate.webp" width="96" alt=""> | [ErrorState](#errorstate) | Failure block with Retry and technical details |
|
|
@@ -522,6 +524,69 @@ import { UnsavedDialog } from '@nxgt/material/components';
|
|
|
522
524
|
|
|
523
525
|
---
|
|
524
526
|
|
|
527
|
+
## Celebration
|
|
528
|
+
|
|
529
|
+
A one-shot confetti burst for a win or a reveal. It plays once when it mounts and again only when `trigger` changes. It never loops: the pieces fly out, fall and fade once, then are removed from the DOM. They are painted with theme tokens (`--primary`, `--secondary`, `--success`, `--info`, `--warning`), sit in a full-screen layer that ignores the pointer, and are `aria-hidden`. Under `prefers-reduced-motion` it renders nothing; it renders nothing on the server either.
|
|
530
|
+
|
|
531
|
+
```tsx
|
|
532
|
+
import { Celebration } from '@nxgt/material/components';
|
|
533
|
+
|
|
534
|
+
{winner && <Celebration trigger={round} onComplete={() => startNextRound()} />}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
| Prop | Type | Default | Effect |
|
|
538
|
+
| --- | --- | --- | --- |
|
|
539
|
+
| `trigger` | `unknown` | — | plays the burst again when its value changes |
|
|
540
|
+
| `pieces` | `number` | `48` | how many pieces fly out |
|
|
541
|
+
| `duration` | `number` (ms) | `2400` | length of one burst |
|
|
542
|
+
| `onComplete` | `() => void` | — | called once when a burst has ended |
|
|
543
|
+
| `className` | `string` | — | on the full-screen layer |
|
|
544
|
+
|
|
545
|
+
It is decorative: say what happened in text (a `Moment`, an alert). It pairs with `Moment` for a win screen.
|
|
546
|
+
|
|
547
|
+
**Storybook:** `Feedback & status/Celebration` · **Vue:** not yet (#2272)
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Moment
|
|
552
|
+
|
|
553
|
+
A transient full-screen announcement for a dramatic one-off event: a player is eliminated, a role is revealed. It is a modal dialog named by its `title` (and described by its `description`), so a screen reader announces it as it opens. Focus moves into it and goes back to the element that had it when it closes; the page behind is inert. Escape, a tap anywhere, the close button and the `action` dismiss it, and so does `duration`. It fades in and its picture pops in; under `prefers-reduced-motion` both stop. The surface is the tone's container with the foreground colour on it, so it holds AA in light and dark, and it fills a phone.
|
|
554
|
+
|
|
555
|
+
```tsx
|
|
556
|
+
import { Moment } from '@nxgt/material/components';
|
|
557
|
+
import { Illustration } from '@nxgt/material/illustrations';
|
|
558
|
+
|
|
559
|
+
<Moment
|
|
560
|
+
open={eliminated != null}
|
|
561
|
+
onOpenChange={(open) => !open && setEliminated(null)}
|
|
562
|
+
duration={6000}
|
|
563
|
+
tone="error"
|
|
564
|
+
illustration={<Illustration scene="error" />}
|
|
565
|
+
title={`${eliminated?.name} was eliminated`}
|
|
566
|
+
description="The village chose her. She was the seer."
|
|
567
|
+
action={<Button onClick={nextNight}>Next night</Button>}
|
|
568
|
+
/>
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
| Prop | Type | Default | Effect |
|
|
572
|
+
| --- | --- | --- | --- |
|
|
573
|
+
| `open` | `boolean` | — | shown or not (controlled) |
|
|
574
|
+
| `onOpenChange` | `(open: boolean) => void` | — | called with `false` on every dismissal |
|
|
575
|
+
| `duration` | `number` (ms) | — | dismisses by itself after this long; unset: stays until dismissed |
|
|
576
|
+
| `illustration` | `ReactNode` | — | the picture (a scene, a pictogram, an icon); decorative, hidden from assistive technology |
|
|
577
|
+
| `title` | `ReactNode` | — | what happened; the accessible name |
|
|
578
|
+
| `description` | `ReactNode` | — | the line under it, in a `<p>` (phrasing content only) |
|
|
579
|
+
| `action` | `ReactNode` | — | a button or link; its click dismisses after its own handler, unless that handler calls `event.preventDefault()` |
|
|
580
|
+
| `tone` | `Tone` | `neutral` | the colour of the meaning: a named tone or one of your `AppTones` |
|
|
581
|
+
| `closeLabel` | `string` | translated | name of the close button |
|
|
582
|
+
| `className` | `string` | — | on the full-screen surface |
|
|
583
|
+
|
|
584
|
+
A tap anywhere dismisses, so keep text a reader must act on out of a short `duration`. Without a `description` the dialog is described by the hint `material.moment.hint` ("Press Escape or tap to dismiss."), read by assistive technology only. The close button is named by `material.buttons.close`.
|
|
585
|
+
|
|
586
|
+
**Storybook:** `Feedback & status/Moment` · **Vue:** not yet (#2248)
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
525
590
|
## StaleNotice
|
|
526
591
|
|
|
527
592
|

|
|
@@ -855,6 +920,9 @@ import { CountdownTimer } from '@nxgt/material/components';
|
|
|
855
920
|
// `criticalAt`); change (both tones) or hide the words:
|
|
856
921
|
<CountdownTimer duration={300} warningText="Hurry up" />;
|
|
857
922
|
<CountdownTimer duration={300} warningText={false} />;
|
|
923
|
+
|
|
924
|
+
// The focal point of a screen:
|
|
925
|
+
<CountdownTimer duration={300} size="lg" />;
|
|
858
926
|
```
|
|
859
927
|
|
|
860
928
|
| Prop | Type | Default | Effect |
|
|
@@ -865,7 +933,7 @@ import { CountdownTimer } from '@nxgt/material/components';
|
|
|
865
933
|
| `warningAt` / `criticalAt` | `number` | a quarter (≤ 60 s) / a tenth (≤ 10 s) | the seconds left when it turns amber / red |
|
|
866
934
|
| `label` | `string` | `'Time remaining'` | its name |
|
|
867
935
|
| `warningText` | `string \| false` | `'Little time left'` / `'Almost out of time'` | the words shown with a clock icon in the warning and critical tones, so low time is not told by colour alone; a string replaces both phrases; `false` hides them |
|
|
868
|
-
| `size` | `'sm' \| 'md'` | `'md'` | 28 or
|
|
936
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | 28, 36 or 56 px high; `lg` is for a countdown that is the focal point of a screen (centred in a ring of seats, a full-screen exam) |
|
|
869
937
|
|
|
870
938
|
It ticks on the client only: the server renders the full time. The clock is a
|
|
871
939
|
`role="timer"`; a polite live region speaks only when a threshold is crossed
|
|
@@ -381,8 +381,15 @@ that focuses the first refused row's text input: the row of the first
|
|
|
381
381
|
built-in blank rule refused the list. An error on the list itself (too few
|
|
382
382
|
rows, no row marked) focuses its first control. So `shouldFocusError`,
|
|
383
383
|
`setFocus('choices')` and `setError('choices', …, { shouldFocus: true })` land
|
|
384
|
-
on the refused row when there is an error, and on the first control otherwise
|
|
385
|
-
|
|
384
|
+
on the refused row when there is an error, and on the first control otherwise.
|
|
385
|
+
A row path works too: `setFocus('choices.1.text')` and
|
|
386
|
+
`setError('choices.1.text', …, { shouldFocus: true })` (a server's refusal)
|
|
387
|
+
focus that row's text input. The form needs no `[aria-invalid="true"]` lookup.
|
|
388
|
+
|
|
389
|
+
```tsx
|
|
390
|
+
// After the server refused the second row:
|
|
391
|
+
form.setError('choices.1.text', { message }, { shouldFocus: true });
|
|
392
|
+
```
|
|
386
393
|
|
|
387
394
|
| Prop | Type | Default | What it does |
|
|
388
395
|
| --- | --- | --- | --- |
|
|
@@ -580,6 +587,13 @@ plain-text `label`, else from `aria-label`; with neither (a field named only
|
|
|
580
587
|
through `aria-labelledby` included), the buttons keep the bare "Increase" /
|
|
581
588
|
"Decrease".
|
|
582
589
|
|
|
590
|
+
**Stepper tooltips.** Each +/− button shows its name in a tooltip on hover and
|
|
591
|
+
on keyboard focus (`:focus-visible`). Focus that a script moves onto it, such
|
|
592
|
+
as a `Sheet` or `Dialog` putting its initial focus on the first tabbable
|
|
593
|
+
control, does not open the tooltip once the user has used the pointer (the browser does not treat that focus as `:focus-visible`), so it does not cover the field's label. The
|
|
594
|
+
buttons stay in the tab order and keep their accessible names. `QuantityField`
|
|
595
|
+
shares this (React only; the Vue steppers are unchanged, #2264).
|
|
596
|
+
|
|
583
597
|
**Decimal separator.** After blur (and after +/−) the value is shown with the
|
|
584
598
|
active language's separator, from `I18nProvider`: `1,5` in French, `1.5` in
|
|
585
599
|
English (no grouping, so it stays editable). Typing accepts both: `1,5` and
|
|
@@ -663,7 +677,7 @@ Without a form library, `QuantityField` takes `value` and `onChange` (or
|
|
|
663
677
|
`defaultValue`). The number is the one `NumberField` has: the language's
|
|
664
678
|
decimal separator, `min`, `max` and `step` (clamped on blur), and steppers
|
|
665
679
|
named after the field ("Increase Dose"). Both parts stay on one line on a
|
|
666
|
-
phone: the number shrinks, the unit
|
|
680
|
+
phone: the number shrinks, the unit is sized to its longest label (see Unit width below).
|
|
667
681
|
|
|
668
682
|
| Prop | Type | Default | What it does |
|
|
669
683
|
| --- | --- | --- | --- |
|
|
@@ -683,6 +697,11 @@ phone: the number shrinks, the unit keeps a fixed 7rem.
|
|
|
683
697
|
is a textbox named after the label, the unit a combobox (or, with
|
|
684
698
|
`allowCustomUnit`, a text input) named "Dose unit". The message describes
|
|
685
699
|
the group, the number and the unit, and both are `aria-invalid` on an error.
|
|
700
|
+
- **Unit width:** the unit part is sized to the longest label of `units`
|
|
701
|
+
(never under 7rem, never over 55% of the row, so the number keeps room at
|
|
702
|
+
360px). Beyond the cap the label is cut with an ellipsis and the full label
|
|
703
|
+
is the select's `title` (the `units` select only; a free unit typed past
|
|
704
|
+
the cap scrolls in its input). No inline style is used for it.
|
|
686
705
|
- **Translations:** `material.quantity-field.unit` and `.unit-named` (English
|
|
687
706
|
and French) name the unit part.
|
|
688
707
|
|
|
@@ -987,7 +987,7 @@ import { Background, Card } from '@nxgt/material/components';
|
|
|
987
987
|
It fills the viewport; pass a `className` such as `h-96 w-full` to use it as a band.
|
|
988
988
|
It is plain CSS from `styles.css`, with no inline `style`, and its colours lift
|
|
989
989
|
in dark. `ActivityLayout` and `ThreadLayout` sit on it. The superset with `grain`,
|
|
990
|
-
`seed`, `fill`, `motif` and the other presets (light, geometry and motif families) is `Background` from `/backgrounds`; see the [backgrounds guide](../guides/backgrounds.md). Light presets there: `mesh`, `aurora`, `spotlight`, `bokeh`, `rays`, `halo`, `plasma`, `glass`, `ripples`, `blobs`, `paper`, `linen`, `concrete`, `marble`, `grain`; geometry: `isometric`, `blueprint`, `dots`, `stripes`, `low-poly`, `topography`, `waves`, `hexagons`, `circuit`, `graph-paper`, `checker`, `diagonal-checker`, `chevrons`; motifs: `shapes`, `confetti-grid`, `constellation`, `halftone`, `truchet`, `floating`, `scatter-icons`, `school`, `night-sky`, `bubbles`, `leaves`, `science`, `music`, `travel`, `sport`, `moon`; sky: `eclipse`, `meteor-shower`, `ringed-planet`, `northern-lights`, or the `sky` prop to lay the moon or one of them over any preset.
|
|
990
|
+
`seed`, `fill`, `motif` and the other presets (light, geometry and motif families) is `Background` from `/backgrounds`; see the [backgrounds guide](../guides/backgrounds.md). Light presets there: `mesh`, `aurora`, `spotlight`, `bokeh`, `rays`, `halo`, `plasma`, `glass`, `ripples`, `blobs`, `paper`, `linen`, `concrete`, `marble`, `grain`; geometry: `isometric`, `blueprint`, `dots`, `stripes`, `low-poly`, `topography`, `waves`, `hexagons`, `circuit`, `graph-paper`, `checker`, `diagonal-checker`, `chevrons`; motifs: `shapes`, `confetti-grid`, `constellation`, `halftone`, `truchet`, `floating`, `scatter-icons`, `school`, `night-sky`, `bubbles`, `leaves`, `science`, `music`, `travel`, `sport`, `moon`; sky: `eclipse`, `meteor-shower`, `ringed-planet`, `northern-lights`, `day-sky`, `dawn-sky`, or the `sky` prop to lay the moon or one of them over any preset.
|
|
991
991
|
|
|
992
992
|
**Storybook:** `Navigation & layout/Background` · **Vue:** `Background`
|
|
993
993
|
|
|
@@ -641,7 +641,7 @@ in a desktop application.
|
|
|
641
641
|
|
|
642
642
|

|
|
643
643
|
|
|
644
|
-
Command palette with filtering, groups and shortcuts. `CommandDialog` shows it in a modal.
|
|
644
|
+
Command palette with filtering, groups and shortcuts. `CommandDialog` shows it in a modal, with a visible close button (`showCloseButton`, on by default; `false` removes it) at the end of the input row: named "Close" (`material.buttons.close`) and 44px on a coarse pointer, since a phone has no Escape key.
|
|
645
645
|
|
|
646
646
|
```tsx
|
|
647
647
|
import {
|
|
@@ -700,6 +700,7 @@ children and they replace that, as before.
|
|
|
700
700
|
| `shouldFilter` (`Command`) | `boolean` | `true` | `false` when you filter the items yourself (server search) |
|
|
701
701
|
| `title` / `description` (`CommandDialog`) | `string` | `"Command Palette"` / `"Search for a command to run..."` (translated) | the dialog's title and description, inside the dialog and visually hidden |
|
|
702
702
|
| `mobilePresentation` (`CommandDialog`) | `'dialog' \| 'top' \| 'fullscreen'` | `'top'` | below 640 px: pinned under the top edge (so the input and the results stay above the on-screen keyboard), the whole screen, or the centred modal it was before |
|
|
703
|
+
| `showCloseButton` (`CommandDialog`) | `boolean` | `true` | a visible close button at the end of the input row, named `material.buttons.close` and 44 px on a coarse pointer; `false` leaves Escape and the backdrop |
|
|
703
704
|
| `commandProps` (`CommandDialog`) | `Pick<CommandProps, 'shouldFilter' \| 'value' \| 'onValueChange' \| 'loop' \| 'filter' \| 'label'>` | — | reaches the inner `Command`: `{ shouldFilter: false }` for a palette whose results come from a server (cmdk then does not hide them again by the typed text), `value` / `onValueChange` for the highlighted item, `label` for the input's accessible name. The input's sizing, the dialog's title and its focus stay as they are |
|
|
704
705
|
| `open`, `onOpenChange` (`CommandDialog`) | as `Dialog` | — | every other prop goes to the `Dialog` |
|
|
705
706
|
|
|
@@ -739,8 +739,8 @@ reached with the arrow keys. The list scrolls sideways when the tabs do not
|
|
|
739
739
|
fit (its scrollbar hidden), which suits a phone or a narrow popover. While it
|
|
740
740
|
overflows, each side with hidden tabs fades and gets a small scroll button
|
|
741
741
|
("Scroll tabs left" / "Scroll tabs right", from `material.tabs.*`; out of the
|
|
742
|
-
tab order; on a touch screen 44px wide and as tall as the list, so it never covers content); the selected and the focused tab are
|
|
743
|
-
scrolled into view, instantly under reduced motion. A list that fits has
|
|
742
|
+
tab order; on a touch screen 44px wide and as tall as the list, so it never covers content). Under a button the list shows nothing (its edge mask is transparent there and fades in just beyond), so no label is ever cut by the button, even at 375px; the selected and the focused tab are
|
|
743
|
+
scrolled into view clear of the button, instantly under reduced motion. A list that fits has
|
|
744
744
|
neither, and neither is in the server markup. `segmented` does the same.
|
|
745
745
|
|
|
746
746
|
**`segmented`** is a neutral pill track: the active tab sits on a raised
|
|
@@ -135,7 +135,7 @@ import {
|
|
|
135
135
|
| `ChatActionProposal` `busy` / `disabled` | `boolean` | `false` | an answer is being sent (both disabled, Confirm spins) / a read-only viewer |
|
|
136
136
|
| `ChatActionProposal` `deadline` | `Date \| string \| number` | — | "Respond by …" in a `<time>`, in the active language; `expired` says "Expired on" |
|
|
137
137
|
| `ChatActionProposal` `failureCode` / `failureMessage` | `string` / `ReactNode` | — | a `failed` card's reason, in an `alert`, with the code |
|
|
138
|
-
| `ChatToolActivity` `name` / `state` / `detail` | `ReactNode` / `'running' \| 'ok' \| 'failed'` / `ReactNode` | — / `'running'` | one compact row for a tool step in a `ChatMessageList`: the name in mono, Running (spinner), Done or Failed |
|
|
138
|
+
| `ChatToolActivity` `name` / `nameText` / `state` / `detail` | `ReactNode` / `string` / `'running' \| 'ok' \| 'failed'` / `ReactNode` | — / — / `'running'` | one compact row for a tool step in a `ChatMessageList`: the name in mono, wrapped onto two lines at most with its full text in a `title` (`nameText` gives it when `name` is a node), then Running (spinner), Done or Failed |
|
|
139
139
|
| `ChatThread` / `ChatMessageList` `layout` | `'bubbles' \| 'flat'` | `'bubbles'` | a messenger's bubbles, or a team chat's rows (name and time over each run) |
|
|
140
140
|
| `ChatThread` / `ChatMessageList` `density` | `'comfortable' \| 'compact'` | `'comfortable'` | tighter bubbles, smaller avatars |
|
|
141
141
|
| `ChatMessageList` `viewportProps` | `ComponentProps` of the viewport | `{ tabIndex: 0 }` | attributes of the scrolling element; it is a tab stop so the keyboard can scroll the transcript (`tabIndex: -1` opts out) |
|
|
@@ -148,8 +148,9 @@ import {
|
|
|
148
148
|
| `ChatComposer` `onStop` | `() => void` | — | while `isLoading`, send becomes stop (an assistant answering) |
|
|
149
149
|
| `ChatComposer` `sendOnEnter` | `boolean` | `true` | Enter sends and Shift+Enter breaks the line; `false`: Ctrl/⌘+Enter sends. Never while an IME composes |
|
|
150
150
|
| `ChatComposer` `emojis` / `mentions` | `EmojiItem[]` / `MentionItem[]` | — | enables the emoji picker and the `@` list |
|
|
151
|
-
| `ChatThreadHeader` | `title`, `subtitle`, `avatarSrc`, `status`, `actions`, `onBack` | — | the title bar; inside a `ChatInbox` it carries the back button on a narrow screen |
|
|
151
|
+
| `ChatThreadHeader` | `title`, `subtitle`, `titleText`, `subtitleText`, `avatarSrc`, `status`, `actions`, `onBack` | — | the title bar; inside a `ChatInbox` it carries the back button on a narrow screen. The title is clamped to two lines and the subtitle truncated, each with its full text in a `title` attribute; `titleText` / `subtitleText` give that text when `title` / `subtitle` is a node |
|
|
152
152
|
| `ChatConversationItem` `title` / `titleText` | `ReactNode` / `string` | — | the clipped title carries its full text in a `title` attribute when it is a string; `titleText` gives it when `title` is a node |
|
|
153
|
+
| `ChatConversationItem` width | — | — | fills its container (`w-full`) and has no intrinsic width (`contain: inline-size`), so a long title never widens an auto-sized grid track and the timestamp stays whole; a shrink-to-fit parent (`w-fit`, `max-content`, an inline-flex wrapper, a popover sized to content) must give it a width |
|
|
153
154
|
| `ChatConversationItem` `status` / `isTyping` | `StatusTone` / `boolean` | — | presence dot; "Typing…" instead of the preview |
|
|
154
155
|
| `ChatInbox` `pane` / `defaultPane` | `'list' \| 'thread'` | `'list'` | which pane shows below the `@md` container width |
|
|
155
156
|
| `ChatWidget` `open` / `defaultOpen` / `onOpenChange` | `boolean` | `false` | the floating launcher's panel (full screen below `sm`); `unreadCount` on the launcher, `side`, `strategy` |
|
|
@@ -846,7 +846,7 @@ export function Topics() {
|
|
|
846
846
|
| `options` | `{ value; label; icon?; count?; description?; disabled?; disabledReason? }[]` | — | the chips, in one flow; `count`: see [option counts](#option-counts) |
|
|
847
847
|
| `groups` | `{ label: string; options }[]` | — | titled sections instead of `options` (it wins when both are given): see [slot picker](#slot-picker-columns-groups-and-reasons) |
|
|
848
848
|
| `columns` | `number \| { base?; sm?; md?; lg? }` | — | equal-width columns (1 to 12; one count per breakpoint, each holding to the next); Up and Down then move by row. Unset, the chips flow and wrap |
|
|
849
|
-
| `showDisabledReason` | `boolean` | `true` | prints a disabled option's `disabledReason`
|
|
849
|
+
| `showDisabledReason` | `boolean` | `true` | prints a disabled option's `disabledReason` below the whole group ("Label: reason"), so the chip row keeps its wrapping; `false` leaves it to the tooltip and the accessible description |
|
|
850
850
|
| `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | the chip's size: 28px, 24px, or `lg`, a 44px box at every pointer (a coarse pointer already gets a 44px box at any size) |
|
|
851
851
|
| `name` | `string` | — | put on every chip (`ChipGroupFormField` passes its field `name`), so `document.getElementsByName(name)` finds the group's chips in order |
|
|
852
852
|
| `label`, `helperText`, `error`, `required`, `disabled`, `id` | | — | the question and its help or error text, the required mark, disabling every chip, the group's `id` |
|
|
@@ -894,7 +894,7 @@ export function Slots() {
|
|
|
894
894
|
}
|
|
895
895
|
```
|
|
896
896
|
|
|
897
|
-
- **Disabled reason.** The reason is a small muted line
|
|
897
|
+
- **Disabled reason.** The reason is a small muted line below the whole group, as "Label: reason" (not
|
|
898
898
|
only a tooltip), and the chip points to it with `aria-describedby`, so a
|
|
899
899
|
screen reader says "09:30, Taken". The chip stays `aria-disabled`, not
|
|
900
900
|
`disabled`: arrows still land on it and a press does nothing. The "up to N"
|
|
@@ -918,7 +918,7 @@ single mode), Home and End jump, and the arrows follow `dir="rtl"`; Space toggle
|
|
|
918
918
|
mode; Enter does nothing, so it never submits a surrounding form. One chip holds
|
|
919
919
|
the tab stop. A disabled option, or an unchosen one once `max` is reached, is
|
|
920
920
|
`aria-disabled`: it stays focusable and an arrow stop, a press does nothing,
|
|
921
|
-
and its `disabledReason` is printed
|
|
921
|
+
and its `disabledReason` is printed below the whole group, outside the chip row (unless
|
|
922
922
|
`showDisabledReason={false}`, then its tooltip) and is its accessible description
|
|
923
923
|
(as is `description`). The group's label shows the required mark when `required`; in
|
|
924
924
|
multiple mode, where a `group` cannot carry `aria-required`, the group is
|
package/docs/getting-started.md
CHANGED
|
@@ -144,7 +144,7 @@ and need nothing.
|
|
|
144
144
|
| `@nxgt/material/editor/viewer` | `RichTextViewer`: shows a saved document (HTML, JSON or Markdown) read-only. **Server-safe**: no editor instance, no `'use client'` ([RichTextViewer](components/rich-widgets.md#richtextviewer)) |
|
|
145
145
|
| `@nxgt/material/editor/collab` | `collaboration()`: several people editing one `DocumentEditor` through a Yjs document and provider your app supplies. Needs `yjs` and `y-protocols` ([Collaboration](components/rich-widgets.md#collaboration)) |
|
|
146
146
|
| `@nxgt/material/editor/pm` | The editor's own tiptap and ProseMirror classes (`Extension`, `Node`, `Plugin`, `PluginKey`, `TextSelection`, `ProseMirrorNode`, `Fragment`, `DecorationSet`, …) for a custom feature's extensions, so `instanceof` agrees with the editor. Shares the editor's code; **server-safe** ([A custom feature](components/rich-widgets.md#a-custom-feature)) |
|
|
147
|
-
| `@nxgt/material/backgrounds` | Background presets: `Background` (a superset of the `/components` one: `preset` (
|
|
147
|
+
| `@nxgt/material/backgrounds` | Background presets: `Background` (a superset of the `/components` one: `preset` (52 ids, `AVAILABLE_BACKGROUND_PRESETS`), `tone`, `intensity`, `animated`, `grain`, `seed`, `fill`, `sky`, `motif` (`shapes`, `density`, `size`, `rotate`, `shapeFill`, `opacitySpread`) and, for `preset="icons"`, `icons`, `iconStyle` and `iconMotif`), and the types and tables re-exported from `@nxgt/material-core/backgrounds`. **Its own entry**, with an opt-in stylesheet ([backgrounds](guides/backgrounds.md)) |
|
|
148
148
|
| `@nxgt/material/media-hls` | `hlsStreaming()`: the HLS adapter for `MediaPlayer`, `hls.js` loaded lazily ([streaming](guides/media-streaming.md)) |
|
|
149
149
|
| `@nxgt/material/vite` | Build-time helper for consumers: `pdfkitStandardFonts()` ([printing](guides/printing.md#bundling-print-into-an-ssr-server)) |
|
|
150
150
|
| `@nxgt/material/hooks` | `useTheme`, `useIsClient`, `useIsMobile`, `useLocalizedDate`, … ([helpers](guides/helpers.md#nxgtmaterialhooks)) |
|
package/docs/guides/app-shell.md
CHANGED
|
@@ -639,7 +639,7 @@ to know the theme on the server (a cookie, passed to `defaultTheme`).
|
|
|
639
639
|
## Use a background preset behind the shell
|
|
640
640
|
|
|
641
641
|
The shell draws the gradient with three blurred blobs behind its frame. To use
|
|
642
|
-
one of the
|
|
642
|
+
one of the 52 presets of `@nxgt/material/backgrounds` instead, pass it as
|
|
643
643
|
`backdrop` (behind the shell its shapes show only in empty space, see [Where the shapes go](#the-themes-background)):
|
|
644
644
|
|
|
645
645
|
```tsx
|