@nxgt/material 3.88.1 → 3.91.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 +50 -0
- package/README.md +8 -5
- package/dist/{alert-dialog-CEGs7LMN.js → alert-dialog-BFk4ye-3.js} +1 -1
- package/dist/{async-slice-CR0M95Jq.js → async-slice-gGyfyUw2.js} +1 -1
- package/dist/{button-DuTNpo28.js → button-C5OEq9bi.js} +1 -1
- package/dist/{calculator-DsGk_fCw.js → calculator-Dg_L6d3R.js} +16 -16
- package/dist/calculator-field.js +6 -6
- package/dist/{calculator-segment-DMFptsDf.js → calculator-segment-CvAaDqKf.js} +1 -1
- package/dist/calculator.js +1 -1
- package/dist/charts.js +3 -3
- package/dist/{chip-C-RAjar5.js → chip-DOxFA4BI.js} +2 -2
- package/dist/{code-editor-DApeIOZ3.js → code-editor-tiAjhXW-.js} +4 -4
- package/dist/code-editor.js +4 -4
- package/dist/{code-language-items-CGtYfc1l.js → code-language-items-CcdzQvLT.js} +2 -2
- package/dist/{components-DiEY0nrG.js → components-G0nEVa52.js} +19744 -19404
- package/dist/components.js +38 -37
- package/dist/{conversion-variant-DfQiOwYC.js → conversion-variant-4gCdFWV-.js} +3 -3
- package/dist/currency-field-CIqe7Wg4.js +289 -0
- package/dist/{dialog-Bul68q8u.js → dialog-d6Kdf5wc.js} +3 -3
- package/dist/dnd.js +1 -1
- package/dist/{dropdown-menu-xUUE5X6T.js → dropdown-menu-DO5kUvof.js} +1 -1
- package/dist/editor.js +139 -139
- package/dist/{empty-state-D7ofGDU_.js → empty-state-DQnICuun.js} +1 -1
- package/dist/{financial-variant-CYgRZzKT.js → financial-variant-D80C1hVD.js} +14 -14
- package/dist/{hooks-CpvvZCs7.js → hooks-C-yLDF39.js} +2 -2
- package/dist/hooks.js +2 -2
- package/dist/{hover-card-CZLFQwZv.js → hover-card-DU3TAME4.js} +1 -1
- package/dist/i18n.js +3 -3
- package/dist/{icon-badge-0Xo9q7Ey.js → icon-badge-BGczZ4Hc.js} +1 -1
- package/dist/{icon-button-DE1EcO2-.js → icon-button-D0xjBCst.js} +1 -1
- package/dist/input-group-CSlXMFxu.js +70 -0
- package/dist/lib/components/ui/actions/back-link/back-link.d.ts +10 -2
- package/dist/lib/components/ui/collections/data-grid/grid-cell-tone.d.ts +12 -0
- package/dist/lib/components/ui/collections/data-grid/partials/grid-cell-access.d.ts +4 -3
- package/dist/lib/components/ui/collections/data-grid/partials/grid-cell-flags.d.ts +12 -0
- package/dist/lib/components/ui/collections/data-grid/partials/grid-cell-value.d.ts +4 -1
- package/dist/lib/components/ui/collections/data-table/partials/data-table-cell-content.d.ts +11 -0
- package/dist/lib/components/ui/collections/data-table/table-meta.d.ts +50 -2
- package/dist/lib/components/ui/collections/filter/use-filter-field-config.d.ts +1 -0
- package/dist/lib/components/ui/collections/list-pane/list-pane-item.d.ts +3 -1
- package/dist/lib/components/ui/collections/list-pane/partials/list-pane-item-content.d.ts +2 -1
- package/dist/lib/components/ui/dates/age-field/age-field.d.ts +8 -0
- package/dist/lib/components/ui/dates/age-field/age-form-field.d.ts +8 -0
- package/dist/lib/components/ui/dates/age-field/age-reading.d.ts +11 -0
- package/dist/lib/components/ui/dates/age-field/age-value.d.ts +9 -0
- package/dist/lib/components/ui/dates/age-field/index.d.ts +4 -0
- package/dist/lib/components/ui/dates/age-field/types.d.ts +40 -0
- package/dist/lib/components/ui/dates/index.d.ts +1 -0
- package/dist/lib/components/ui/display/badge/badge.d.ts +4 -1
- package/dist/lib/components/ui/display/entity-header/entity-header.d.ts +8 -1
- package/dist/lib/components/ui/display/index.d.ts +2 -0
- package/dist/lib/components/ui/display/person-cell/index.d.ts +1 -0
- package/dist/lib/components/ui/display/person-cell/person-cell.d.ts +23 -0
- package/dist/lib/components/ui/display/tone-value/index.d.ts +2 -0
- package/dist/lib/components/ui/display/tone-value/tone-value.d.ts +23 -0
- package/dist/lib/components/ui/feedback/error-state/partials/error-state-actions.d.ts +6 -1
- package/dist/lib/components/ui/feedback/page-load-failed/page-load-failed.d.ts +8 -1
- package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
- package/dist/lib/components/ui/fields/number-field/number-control.d.ts +2 -2
- package/dist/lib/components/ui/fields/switch/partials/switch-row.d.ts +4 -2
- package/dist/lib/components/ui/fields/switch/switch.d.ts +7 -1
- package/dist/lib/components/ui/layout/app-shell/activity-content-aside-parts.d.ts +68 -0
- package/dist/lib/components/ui/layout/app-shell/activity-content-aside-toggle.d.ts +22 -0
- package/dist/lib/components/ui/layout/app-shell/activity-content-aside.d.ts +5 -2
- package/dist/lib/components/ui/layout/app-shell/activity-content-aside.types.d.ts +13 -0
- package/dist/lib/components/ui/layout/app-shell/activity-content.d.ts +3 -2
- package/dist/lib/components/ui/layout/app-shell/app-shell.utils.d.ts +3 -1
- package/dist/lib/components/ui/layout/app-shell/app-sidebar-entries.d.ts +5 -2
- package/dist/lib/components/ui/layout/app-shell/app-sidebar-icon-item.d.ts +2 -1
- package/dist/lib/components/ui/layout/app-shell/app-sidebar.d.ts +3 -1
- package/dist/lib/components/ui/layout/app-shell/types.d.ts +14 -5
- package/dist/lib/components/ui/layout/sticky-action-bar/sticky-action-bar.d.ts +3 -1
- package/dist/lib/components/ui/layout/sticky-action-bar/use-bar-clearance.d.ts +10 -0
- package/dist/lib/components/ui/primitives/tabs/partials/tabs-list.d.ts +8 -2
- package/dist/lib/components/ui/primitives/tabs/tabs-nav.d.ts +5 -1
- package/dist/lib/components/ui/primitives/tabs/tabs-overflow-classes.d.ts +3 -0
- package/dist/lib/components/ui/selection/sortable-list/index.d.ts +1 -1
- package/dist/lib/components/ui/selection/sortable-list/sortable-list-context.d.ts +3 -1
- package/dist/lib/components/ui/selection/sortable-list/sortable-list.d.ts +6 -2
- package/dist/lib/components/ui/selection/sortable-list/types.d.ts +1 -0
- package/dist/lib/components/ui/selection/sortable-list/use-sortable-list-labels.d.ts +3 -0
- package/dist/lib/i18n/resources/en/en.d.ts +11 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +12 -1
- package/dist/lib/i18n/resources/fr/fr.d.ts +11 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +12 -1
- package/dist/lib/i18n/resources/resources.d.ts +22 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib.js +1 -1
- package/dist/main.js +54 -53
- package/dist/map.js +7 -7
- package/dist/material.css +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/{percent-field-BFwxazf1.js → percent-field-DyJ2yOH4.js} +1 -1
- package/dist/{picker-surface-DkfTv_ft.js → picker-surface-CnlOKQ3D.js} +4 -4
- package/dist/{popover-BM6xApJd.js → popover-BevReSHi.js} +1 -1
- package/dist/{pos-types-CpTpT4vE.js → pos-types-BrZBX0Zg.js} +1 -1
- package/dist/{pos-variant-Bs5BUB1L.js → pos-variant-G12HCSfQ.js} +4 -4
- package/dist/print.js +1 -1
- package/dist/{programmer-variant-B1eI1fd0.js → programmer-variant-WWLwTGRF.js} +2 -2
- package/dist/{public-CzLMnSsS.js → public-C1a6wQ6o.js} +19 -19
- package/dist/{rich-code-block-CdVtxD75.js → rich-code-block-Dh9J-VZ8.js} +5 -5
- package/dist/{scientific-variant-rfvJKxB4.js → scientific-variant-BvutlF8a.js} +2 -2
- package/dist/{sortable-provider-BopzsciJ.js → sortable-provider-BrA5tfJX.js} +2 -2
- package/dist/{spinner-DcW21pN1.js → spinner-JnLLiLIO.js} +1 -1
- package/dist/{tabs-CdoBFtWx.js → tabs-dXCZGShv.js} +63 -61
- package/dist/theme-picker.js +2 -2
- package/dist/{theme-toggle-Ch3GkDDU.js → theme-toggle-BmcK6mXV.js} +1 -1
- package/dist/{toolbar-items-CZ1lJXZZ.js → toolbar-items-B-kMYsDT.js} +4 -4
- package/dist/{translate-material-BrYBBZ66.js → translate-material-BmpcP1p1.js} +1 -1
- package/dist/{translator-DoVsjUff.js → translator-CfoXTrZd.js} +24 -2
- package/dist/{use-chat-composer-C9DG7O1Z.js → use-chat-composer-DFD_eF-r.js} +4197 -4246
- package/dist/{use-heading-spy-kyOSe9rb.js → use-heading-spy-BcNK1qvq.js} +4 -4
- package/dist/{use-material-translation-BdrItuwY.js → use-material-translation-DoymV6IP.js} +1 -1
- package/dist/{use-return-focus-Cqzvj9R0.js → use-return-focus-CLEnhfka.js} +1 -1
- package/docs/components/README.md +2 -1
- package/docs/components/buttons-and-actions.md +10 -1
- package/docs/components/data-display.md +80 -4
- package/docs/components/data-table-filter-and-lists.md +56 -3
- package/docs/components/date-and-time.md +53 -1
- package/docs/components/feedback-and-status.md +20 -3
- package/docs/components/form-fields.md +11 -2
- package/docs/components/navigation-and-layout.md +7 -2
- package/docs/components/primitives.md +7 -0
- package/docs/components/selection-and-choice.md +4 -2
- package/docs/guides/app-shell.md +46 -11
- package/docs/guides/data-grid.md +12 -0
- package/docs/guides/forms.md +1 -0
- package/docs/guides/i18n.md +1 -1
- package/docs/previews/data-display-personcell.webp +0 -0
- package/docs/previews/data-display-tonevalue.webp +0 -0
- package/docs/previews/date-time-agefield.webp +0 -0
- package/docs/roadmap.md +23 -0
- package/docs/troubleshooting.md +4 -0
- package/package.json +2 -2
- package/dist/currency-field-cwC7cqUE.js +0 -286
|
@@ -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-BFk4ye-3.js";
|
|
3
|
+
import { a as l } from "./button-C5OEq9bi.js";
|
|
4
|
+
import { i as u } from "./translate-material-BmpcP1p1.js";
|
|
5
|
+
import { t as d } from "./use-material-translation-DoymV6IP.js";
|
|
6
6
|
import { _ as f, g as p, h as m, t as h, v as g, y as _ } from "./date.utils-JTEIo3mM.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 { i as n, n as r, r as i, t as a } from "./dist-DWZXlCZR.js";
|
|
3
|
-
import { C as o, S as s, x as c } from "./spinner-
|
|
3
|
+
import { C as o, S as s, x as c } from "./spinner-JnLLiLIO.js";
|
|
4
4
|
import { a as l, i as u, r as d } from "./dist-DHdUUM1w.js";
|
|
5
5
|
import { c as f, i as p, l as m, o as h, r as g, s as ee } from "./in-dialog-D61Yeaol.js";
|
|
6
6
|
import { r as te } from "./escape-owner-DpaP0lm1.js";
|
|
@@ -46,11 +46,12 @@ Labelled inputs with a value, each with its form-library wrapper, and the compos
|
|
|
46
46
|
|
|
47
47
|
## [Date & time](date-and-time.md)
|
|
48
48
|
|
|
49
|
-
Calendar, date, range, half-day range, time, duration, a month navigator, the now line.
|
|
49
|
+
Calendar, date, range, half-day range, time, duration, age, a month navigator, the now line. 10 entries.
|
|
50
50
|
|
|
51
51
|
[<img src="../previews/date-time-datefield.webp" height="72" alt="DateField">](date-and-time.md#datefield)
|
|
52
52
|
[<img src="../previews/date-time-daterangefield.webp" height="72" alt="DateRangeField">](date-and-time.md#daterangefield)
|
|
53
53
|
[<img src="../previews/date-time-timefield.webp" height="72" alt="TimeField">](date-and-time.md#timefield)
|
|
54
|
+
[<img src="../previews/date-time-agefield.webp" height="72" alt="AgeField">](date-and-time.md#agefield)
|
|
54
55
|
[<img src="../previews/date-time-durationfield.webp" height="72" alt="DurationField">](date-and-time.md#durationfield)
|
|
55
56
|
[<img src="../previews/date-time-halfdayrangefield.webp" height="72" alt="HalfDayRangeField">](date-and-time.md#halfdayrangefield)
|
|
56
57
|
[<img src="../previews/date-time-minimonth.webp" height="72" alt="MiniMonth">](date-and-time.md#minimonth)
|
|
@@ -939,7 +939,16 @@ In a page header with other actions, `collapse="phone"` keeps only the arrow bel
|
|
|
939
939
|
</BackLink>
|
|
940
940
|
```
|
|
941
941
|
|
|
942
|
-
|
|
942
|
+
At the top of a page, `flush` drops the start padding so the arrow lines up with the title and content under it, in left-to-right and right-to-left pages. It suits the `link` and `ghost` looks (no box); the hit area keeps the button's height. Without it the arrow is inset by the button's padding. Default `false`.
|
|
943
|
+
|
|
944
|
+
```tsx
|
|
945
|
+
<BackLink to="/items" variant="link" flush>
|
|
946
|
+
All items
|
|
947
|
+
</BackLink>
|
|
948
|
+
<h1>Item</h1>
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
**Storybook:** `Buttons & actions/BackLink` (`Default`, `LinkStyle`, `Flush`, `FlushRtl`, `Rtl`, `CollapsedOnPhone`) · **Vue:** none yet
|
|
943
952
|
|
|
944
953
|
---
|
|
945
954
|
|
|
@@ -8,16 +8,18 @@ from `@nxgt/material/components`.
|
|
|
8
8
|
| --- | --- | --- |
|
|
9
9
|
| <img src="../previews/data-display-avatar.webp" width="96" alt=""> | [Avatar](#avatar) | Image with a fallback and an optional online / offline dot |
|
|
10
10
|
| <img src="../previews/data-display-avatargroup.webp" width="96" alt=""> | [AvatarGroup](#avatargroup) | Overlapping avatars with a `+N` overflow |
|
|
11
|
+
| — | [PersonCell](#personcell) | A decorative initials avatar before a truncating name, for the first column of a table or a card title |
|
|
11
12
|
| <img src="../previews/data-display-seatring.webp" width="96" alt=""> | [SeatRing](#seatring) | Participants as seats around a circle or in a grid, optionally pickable |
|
|
12
13
|
| <img src="../previews/data-display-secretcard.webp" width="96" alt=""> | [SecretCard](#secretcard) | A card whose secret is not rendered until it is revealed |
|
|
13
14
|
| <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
|
-
| <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
|
+
| <img src="../previews/data-display-badge.webp" width="96" alt=""> | [Badge](#badge) | Status pill, `sm` (20 px) or `md` (24 px), in seven tones, soft or outlined, with an optional decorative `icon` before the words |
|
|
15
16
|
| <img src="../previews/data-display-countbadge.webp" width="96" alt=""> | [CountBadge](#countbadge) | Count in the corner of its children |
|
|
16
17
|
| <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>` |
|
|
17
18
|
| <img src="../previews/data-display-dayseparator.webp" width="96" alt=""> | [DaySeparator](#dayseparator) | "Today", "Yesterday" or the date between two hairlines |
|
|
18
19
|
| <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 |
|
|
19
20
|
| <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 |
|
|
20
21
|
| <img src="../previews/data-display-statcard.webp" width="96" alt=""> | [StatCard](#statcard) | KPI tile with value, hint, icon and toned delta; `href` makes it one link |
|
|
22
|
+
| | [ToneValue](#tonevalue) | A value in its tone's text colour after a shape (diamond, triangle, circle, square), so colour is never the only signal; the cells of a `meta.tone` column |
|
|
21
23
|
| | [TotalsList](#totalslist) | Labelled figures in a `dl`, amounts right-aligned, a ruled grand-total row; the screen twin of `PrintTotals` |
|
|
22
24
|
| <img src="../previews/data-display-metric-cards.webp" width="96" alt=""> | [Metric cards](#metric-cards) | Sixteen dashboard tiles |
|
|
23
25
|
| <img src="../previews/data-display-card-presets.webp" width="96" alt=""> | [Card presets](#card-presets) | Twenty-seven ready-made cards |
|
|
@@ -368,7 +370,7 @@ and no lazy loading: give `src` the size you need.
|
|
|
368
370
|
|
|
369
371
|

|
|
370
372
|
|
|
371
|
-
Status badge: a 20 px
|
|
373
|
+
Status badge: a pill in two sizes (`sm`, 20 px with the `label-sm` text, by default; `md`, 24 px with `label-md`), compact padding and no hover or pointer affordance. Use `soft` (a tonal fill from the tone tokens) for a state; `outlined` is a thin 1 px line at 40 % of the tone. Every tone stays at 4.5:1 or more in the light and dark themes and follows the theme presets.
|
|
372
374
|
|
|
373
375
|
```tsx
|
|
374
376
|
import { Badge } from '@nxgt/material/components';
|
|
@@ -377,20 +379,64 @@ import { Badge } from '@nxgt/material/components';
|
|
|
377
379
|
<Badge variant="outlined" tone="error">Late</Badge>
|
|
378
380
|
<Badge variant="success">New</Badge>
|
|
379
381
|
<Badge variant="soft" tone="warning" icon="box-archive">Archived</Badge>
|
|
382
|
+
<Badge size="md" variant="soft" tone="success">Paid</Badge>
|
|
380
383
|
```
|
|
381
384
|
|
|
382
|
-
With an `icon`, a status reads as icon plus words, so colour is never the only signal. The icon is decorative (`aria-hidden`: the words name the status), 12 px, and takes the tone's text colour in `soft` and `outlined`; nothing changes without it.
|
|
385
|
+
With an `icon`, a status reads as icon plus words, so colour is never the only signal. The icon is decorative (`aria-hidden`: the words name the status), 12 px (16 px at `md`), and takes the tone's text colour in `soft` and `outlined`; nothing changes without it.
|
|
383
386
|
|
|
384
387
|
| Prop | Type | Default | Effect |
|
|
385
388
|
| --- | --- | --- | --- |
|
|
386
389
|
| `icon` | `IconName \| ReactNode` | — | a decorative icon before the label: a string is a Font Awesome name (drawn solid), a node is rendered as given. Ignored with `asChild` |
|
|
387
390
|
| `variant` | `'default' \| 'secondary' \| 'error' \| 'success' \| 'info' \| 'warning' \| 'soft' \| 'outlined'` | `'default'` | the filled tones, `soft` (recommended for a state) or `outlined` |
|
|
388
391
|
| `tone` | the eight named tones, or a tone of your own declared in `AppTones` | `'neutral'` | the colour of a `soft` or `outlined` badge; ignored by the filled variants |
|
|
392
|
+
| `size` | `'sm' \| 'md'` | `'sm'` | `sm` is 20 px with `label-sm` text and a 12 px icon; `md` is 24 px with `label-md` text and a 16 px icon, to read next to body text in a list or table row |
|
|
389
393
|
| `asChild` | `boolean` | `false` | renders its child (a link) with the badge styles |
|
|
390
394
|
|
|
391
395
|
A `Badge` link (`asChild`) no longer fades on hover: a badge is a status, not a button. Put a `Chip` or a `Button` where the user acts.
|
|
392
396
|
|
|
393
|
-
**Storybook:** `Data display/Badge` (Soft, SoftContrast), `Data display/Badge/Icon` · **Vue:** `Badge` (without `soft` and `tone` yet)
|
|
397
|
+
**Storybook:** `Data display/Badge` (Soft, SoftContrast, Sizes), `Data display/Badge/Icon` · **Vue:** `Badge` (without `soft` and `tone` yet, and without `size`: React-only until Vue parity, #2590)
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## PersonCell
|
|
402
|
+
|
|
403
|
+
An avatar before a name, for the first column of a table of people or the title of a card. The avatar is decorative (`aria-hidden`) and neutral by default; the name is the text a screen reader and a row link read. The name (and the optional description) truncate with an ellipsis (`min-w-0`) while their full text stays in the DOM, so it holds on a phone.
|
|
404
|
+
|
|
405
|
+
In the first column of a table ([DataTable guide](./data-table-filter-and-lists.md)):
|
|
406
|
+
|
|
407
|
+
```tsx
|
|
408
|
+
import { createColumnHelper } from '@tanstack/react-table';
|
|
409
|
+
import { PersonCell, type DataTableFeatures } from '@nxgt/material/components';
|
|
410
|
+
|
|
411
|
+
const helper = createColumnHelper<DataTableFeatures, Person>();
|
|
412
|
+
|
|
413
|
+
const columns = [
|
|
414
|
+
helper.accessor('name', {
|
|
415
|
+
header: 'Name',
|
|
416
|
+
cell: ({ getValue }) => <PersonCell name={getValue()} />,
|
|
417
|
+
}),
|
|
418
|
+
];
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
As the title of a card, in its 32 px size:
|
|
422
|
+
|
|
423
|
+
```tsx
|
|
424
|
+
import { PersonCell } from '@nxgt/material/components';
|
|
425
|
+
|
|
426
|
+
<PersonCell size="md" name="Ada Lovelace" description="Analyst" />
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
| Prop | Type | Default | Effect |
|
|
430
|
+
| --- | --- | --- | --- |
|
|
431
|
+
| `name` | `string` | — | the person's name, truncated when it does not fit |
|
|
432
|
+
| `src` | `string` | — | a picture; the initials show until it loads or when it fails |
|
|
433
|
+
| `initials` | `string` | derived from `name` | the letters shown without a picture (`'Ada Lovelace'` gives `AL`) |
|
|
434
|
+
| `tone` | `Tone` | neutral | tone of the initials |
|
|
435
|
+
| `description` | `string` | — | a muted, truncating line under the name |
|
|
436
|
+
| `size` | `'sm' \| 'md'` | `'sm'` | `sm`: 24 px avatar, for a table row; `md`: 32 px, for a card or a list |
|
|
437
|
+
| `className` | `string` | — | on the root, which is `flex min-w-0` |
|
|
438
|
+
|
|
439
|
+
**Storybook:** `Data display/PersonCell` · Not in `@nxgt/material-vue` yet.
|
|
394
440
|
|
|
395
441
|
---
|
|
396
442
|
|
|
@@ -604,6 +650,26 @@ pass `disabled` with a `Spinner` in `leading`, or use a `Button`.
|
|
|
604
650
|
|
|
605
651
|
---
|
|
606
652
|
|
|
653
|
+
## ToneValue
|
|
654
|
+
|
|
655
|
+
A value in the text colour of its tone, after a small `aria-hidden` shape fixed per tone: a diamond for `error`, a triangle for `warning`, a circle for `success`, a square for any other tone, none for `neutral`. The tone survives forced colours, colour blindness and a black and white print. The cells of a [`meta.tone` column](data-table-filter-and-lists.md#cells-toned-by-their-value) of `DataTable`, `ServerTable` and `DataGrid` wear it; import it for a `renderCard` or any figure of your own.
|
|
656
|
+
|
|
657
|
+
```tsx
|
|
658
|
+
import { ToneValue } from '@nxgt/material/components';
|
|
659
|
+
|
|
660
|
+
<ToneValue tone="error" label="Low">6.5</ToneValue>
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
| Prop | Type | Default | Effect |
|
|
664
|
+
| --- | --- | --- | --- |
|
|
665
|
+
| `tone` | `Tone` | — | a named tone or one the app declared in `AppTones`; `undefined` renders the value in a plain span, with your `className` and props, and no mark |
|
|
666
|
+
| `label` | `string` | — | words after the value for a screen reader, visually hidden; the mark is never read |
|
|
667
|
+
| `children` | `ReactNode` | — | the value |
|
|
668
|
+
|
|
669
|
+
**Storybook:** `Data display/ToneValue` · Not in `@nxgt/material-vue` yet (#2569).
|
|
670
|
+
|
|
671
|
+
---
|
|
672
|
+
|
|
607
673
|
## TotalsList
|
|
608
674
|
|
|
609
675
|
On-screen counterpart of `PrintTotals`: a `dl` of labelled figures, labels at the start, amounts right-aligned in tabular figures, one column at any width (a side rail, a phone). Rows have the shape of `PrintTotalRow` (`label`, `value`, `last`), so one array feeds the screen and its print.
|
|
@@ -990,9 +1056,19 @@ import { Badge, EntityHeader } from '@nxgt/material/components';
|
|
|
990
1056
|
| `metadata` | `{ label: string; value: ReactNode }[]` | `[]` | facts under the title, a description list: a muted label beside its value, no colon; a link in a value is drawn in the primary text colour (`text-primary-text`), not the prose link's info blue, and `nxgt-plain-link` still opts out |
|
|
991
1057
|
| `stats` | `EntityStat[]` | `[]` | figures (counts, a mean) as compact `size="sm"` [StatCard](#statcard) tiles in a `StatCardGroup` (2×2 on a phone, a wrapping row beside) under the metadata; a `tone` paints a tile, `0` is a figure, an empty value drops the tile (see Stats below) |
|
|
992
1058
|
| `actions` | `ResponsiveButtonProps[] \| ReactNode` | `[]` | an array becomes responsive buttons (icon-only on phones) |
|
|
1059
|
+
| `surface` | `'card' \| 'none'` | `'card'` | `'none'` draws the header flat (no background, padding or shadow) for a header that sits directly on a page band or inside a surface of its own, so surfaces do not nest; the layout is unchanged |
|
|
993
1060
|
|
|
994
1061
|
The root carries `data-slot="entity-header"`; the metadata is a `dl` with `data-slot="entity-metadata"` (a muted `dt` label beside its `dd` value, no colon), and the actions sit in `data-slot="entity-header-actions"` (absent when there are none). Inside an EntityHeader that replaces the old `summary-data` slot and its `Label:` text, so update a selector or a test that read "Label: value".
|
|
995
1062
|
|
|
1063
|
+
**Flat, on a page band.** `surface="none"` drops the header's own card; `className` still merges.
|
|
1064
|
+
|
|
1065
|
+
```tsx
|
|
1066
|
+
<main className="bg-background">
|
|
1067
|
+
<EntityHeader surface="none" title="John Doe" metadata={[{ label: 'Email', value: 'john.doe@example.com' }]} />
|
|
1068
|
+
<Card>…</Card>
|
|
1069
|
+
</main>
|
|
1070
|
+
```
|
|
1071
|
+
|
|
996
1072
|
**A control at the start.** `leading` takes a control, never `icon`: the icon badge is `aria-hidden`, which squashes a `Switcher` and makes axe report `aria-hidden-focus` (see [Troubleshooting](../troubleshooting.md#a-control-in-an-entityheaders-icon-is-hidden-from-assistive-tech-squashed-and-reports-aria-hidden-focus)).
|
|
997
1073
|
|
|
998
1074
|
```tsx
|
|
@@ -93,7 +93,7 @@ table enables, for typing your own column defs.
|
|
|
93
93
|
|
|
94
94
|
| Prop | Type | Default | Effect |
|
|
95
95
|
| --- | --- | --- | --- |
|
|
96
|
-
| `columns` | `ColumnDef<DataTableFeatures, T>[]` | — | `meta.className` styles a column's cells; `meta.numeric` sets a column of figures flush to the end in tabular digits (`text-end tabular-nums`, header and cells), so decimals line up; `meta.label` names the column in the cards sort menu |
|
|
96
|
+
| `columns` | `ColumnDef<DataTableFeatures, T>[]` | — | `meta.className` styles a column's cells; `meta.cellClassName(value, row)` adds classes to a body cell from the value it shows (below); `meta.tone(value, row)` tones the cell ([below](#cells-toned-by-their-value)); `meta.numeric` sets a column of figures flush to the end in tabular digits (`text-end tabular-nums`, header and cells), so decimals line up; `meta.label` names the column in the cards sort menu |
|
|
97
97
|
| `data` | `T[]` | — | rows; sorting, filtering and pagination run client-side |
|
|
98
98
|
| `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` |
|
|
99
99
|
| `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)) |
|
|
@@ -135,6 +135,59 @@ table enables, for typing your own column defs.
|
|
|
135
135
|
| `announceLoading` | `boolean` | `true` | `false` keeps the visually hidden "Loading data…" region silent, for a page whose own region tells the answer |
|
|
136
136
|
| `announceEmpty` | `boolean` | `true` | `false` keeps the same region silent on an empty page (once the page empties after a change, it says "No results…" when filtered, "No data available" for an empty `manual` page; never on the first render), for a page whose own region tells it |
|
|
137
137
|
|
|
138
|
+
### Cell classes from the value
|
|
139
|
+
|
|
140
|
+
`meta.cellClassName(value, row)` returns classes for a body cell (or `undefined`), merged after `meta.className`. `DataTable` and `ServerTable` pass `cell.getValue()` and the row's original; `DataGrid` passes the value the cell shows now, so the class follows an edit, a save and an undo. The text is untouched and the header is not concerned.
|
|
141
|
+
|
|
142
|
+
```tsx
|
|
143
|
+
helper.accessor('balance', {
|
|
144
|
+
header: 'Balance',
|
|
145
|
+
meta: {
|
|
146
|
+
numeric: true,
|
|
147
|
+
cellClassName: (value) =>
|
|
148
|
+
typeof value === 'number' && value < 0 ? 'text-error-text' : undefined,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Cells toned by their value
|
|
154
|
+
|
|
155
|
+
`meta.tone(value, row)` returns a tone (`'error'`, `'warning'`, `'success'`, any tone of the library or of your `AppTones`) or `undefined`. A toned cell gets `data-tone`, and its content is wrapped in [`ToneValue`](data-display.md#tonevalue): the value in the tone's text colour after a small shape (diamond for `error`, triangle for `warning`, circle for `success`, square for another tone, none for `neutral`), so the tone holds in dark, on a selected row and in forced colours, where the shapes tell the tones apart without colour. No tone, no mark: the value is plain. `DataTable` and `ServerTable` pass `cell.getValue()` and the row's original, and the default card of the `cards` and `tiles` layouts shows the first cell the same way; a `renderCard` imports `ToneValue`.
|
|
156
|
+
|
|
157
|
+
`DataGrid` passes the value the cell shows now, so the tone follows an edit, a pending save, a refused save put back and an undo (and the mark appears at once on the focused cell). While a cell is edited its editor's ring stays the focus indicator, and a text kept as an error is not toned. On a phone the list summary and the row panel's read-only values wear `ToneValue` too.
|
|
158
|
+
|
|
159
|
+
`toneBands` from `@nxgt/material/components` (also `@nxgt/material-core/cell-tone`) builds the function from bands on a number: `{ below: 8 }` is under 8, `{ from: 14 }` is 14 and over, the first band that holds wins, a gap between bands has no tone, and so has a value that is not a number. `max` divides the value first, so fractional bands serve any scale. The mark is `aria-hidden`; to have a screen reader say the band, give the bands a `label` and set `meta.toneLabel` (off by default).
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { toneBands } from '@nxgt/material/components';
|
|
163
|
+
|
|
164
|
+
const level = toneBands([
|
|
165
|
+
{ below: 8, tone: 'error', label: 'Low' },
|
|
166
|
+
{ below: 10, tone: 'warning', label: 'Borderline' },
|
|
167
|
+
{ from: 14, tone: 'success', label: 'High' },
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
helper.accessor('score', {
|
|
171
|
+
header: 'Score',
|
|
172
|
+
meta: {
|
|
173
|
+
numeric: true,
|
|
174
|
+
tone: level,
|
|
175
|
+
toneLabel: level.label, // optional: "6.5 Low" for a screen reader
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
A first column of people takes [`PersonCell`](./data-display.md#personcell) as its `cell`: a decorative avatar, then the name, which truncates on a phone.
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
import { PersonCell } from '@nxgt/material/components';
|
|
184
|
+
|
|
185
|
+
helper.accessor('name', {
|
|
186
|
+
header: 'Name',
|
|
187
|
+
cell: ({ getValue }) => <PersonCell name={getValue()} />,
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
138
191
|
### Loading and empty states
|
|
139
192
|
|
|
140
193
|
```tsx
|
|
@@ -1596,7 +1649,7 @@ props that matter:
|
|
|
1596
1649
|
|
|
1597
1650
|
| Prop | Type | Default | Notes |
|
|
1598
1651
|
| --- | --- | --- | --- |
|
|
1599
|
-
| `data`, `columns`, `getRowId` | `TData[]`, `ColumnDef[]`, `(row) => string` | — | Required. `meta.editor` makes a column editable: `'text'`, `'number'` (`number`, `codes`), `'select'` (`options`), `'date'` (`date`), `'checkbox'`, or [your own component](../guides/data-grid.md#a-custom-editor). `meta.parse`, `format`, `validate`, `schema`, `value` (computed), `aggregate`, `readOnly`, `locked`, `headerMenu`, `headerWrap` (two-line header titles; a cut title gets a tooltip), `frozen
|
|
1652
|
+
| `data`, `columns`, `getRowId` | `TData[]`, `ColumnDef[]`, `(row) => string` | — | Required. `meta.editor` makes a column editable: `'text'`, `'number'` (`number`, `codes`), `'select'` (`options`), `'date'` (`date`), `'checkbox'`, or [your own component](../guides/data-grid.md#a-custom-editor). `meta.parse`, `format`, `validate`, `schema`, `value` (computed), `aggregate`, `readOnly`, `locked`, `headerMenu`, `headerWrap` (two-line header titles; a cut title gets a tooltip), `frozen`, `className` (give a `w-*` width) and `cellClassName(value, row)` (classes from the value the cell shows now, so they follow an edit, a save and an undo), `tone(value, row)` and `toneLabel` (the same, for the cell's tone) are in [the guide](../guides/data-grid.md#columns-and-meta) |
|
|
1600
1653
|
| `save` | `'batch' \| 'cell' \| 'row'` | `'batch'` | When `onSave` is called: on Save, 300 ms after each change, or when a row is left. `'row'` needs `getRowVersion`. In cell and row modes a call carries at most `maxEntriesPerSave` (200) entries |
|
|
1601
1654
|
| `onSave` | `(entries, { signal }) => Promise<answer>` | — | `{ ok: true, versions }`, or `{ ok: false, refused }` with an `error` (and `revert: true` to put the cell back) or a `conflict` per entry. Pass `signal` to your `fetch` |
|
|
1602
1655
|
| `getRowVersion`, `getCellVersion` | `(row) => version`, `(row, columnId) => version` | — | The version each value was read at; a mismatch on reload is a conflict ([versions](../guides/data-grid.md#versions-and-conflicts)) |
|
|
@@ -3093,7 +3146,7 @@ import { Avatar, ListPane, ListPaneItem } from '@nxgt/material/components';
|
|
|
3093
3146
|
| `height` | `number \| string` | `20rem` | the body's height, capped at 65% of the viewport |
|
|
3094
3147
|
| `over` | `boolean` | — | a drag is over the pane: a primary outline and tint |
|
|
3095
3148
|
|
|
3096
|
-
`ListPaneItem` takes `title`, `description`, `media`, `leading`, `badge`,
|
|
3149
|
+
`ListPaneItem` takes `title`, `titleLines` (`1` or `2`, default `1`; `2` clamps the title to two lines, the full text stays in the `title` attribute), `description`, `media`, `leading`, `badge`,
|
|
3097
3150
|
`trailing`, `selected`, `focused` (a ring while its `group/listbox` parent has
|
|
3098
3151
|
visible focus), `disabled`, `density` (`comfortable` or `compact`, one line),
|
|
3099
3152
|
`appearance` (`plain` or `card`) and `lifted` (raised, as under a drag). It is
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Date & time
|
|
2
2
|
|
|
3
|
-
Calendars and the fields that pick a day, a range, a time or
|
|
3
|
+
Calendars and the fields that pick a day, a range, a time, a duration or an age.
|
|
4
4
|
Every field comes with a React Hook Form wrapper (`DateFormField`…) that
|
|
5
5
|
takes `control` and `name` — see the [forms guide](../guides/forms.md) for
|
|
6
6
|
wiring, and [what each field emits](../guides/forms.md#what-each-field-emits)
|
|
@@ -19,6 +19,7 @@ each change and, where one is left, the prop that keeps the old behaviour.
|
|
|
19
19
|
| <img src="../previews/date-time-daterangefield.webp" width="96" alt=""> | [DateRangeField](#daterangefield) | From / to range, typed or picked in a calendar (two months on a desktop, a bottom sheet on a phone) |
|
|
20
20
|
| <img src="../previews/date-time-timefield.webp" width="96" alt=""> | [TimeField](#timefield) | A time typed in segments or picked in a `TimePicker` (a bottom sheet on a phone) |
|
|
21
21
|
| <img src="../previews/date-time-timepicker.webp" width="96" alt=""> | [TimePicker](#timepicker) | Hour and minute tiles (or columns) with AM / PM |
|
|
22
|
+
| <img src="../previews/date-time-agefield.webp" width="96" alt=""> | [AgeField](#agefield) | An age as a number and a unit (days, weeks, months, years), read aloud by locale: "2 months" |
|
|
22
23
|
| <img src="../previews/date-time-durationfield.webp" width="96" alt=""> | [DurationField](#durationfield) | Hours and minutes as a total in minutes, typed on one line: "1 h 29 min" |
|
|
23
24
|
| <img src="../previews/date-time-halfdayrangefield.webp" width="96" alt=""> | [HalfDayRangeField](#halfdayrangefield) | A range whose two ends are each a day plus morning or afternoon, ordered on the half |
|
|
24
25
|
| <img src="../previews/date-time-minimonth.webp" width="96" alt=""> | [MiniMonth](#minimonth) | Compact month navigator with event dots and a keyboard date grid |
|
|
@@ -807,6 +808,57 @@ await user.click(within(screen.getByRole('listbox', { name: 'Minutes' })).getByR
|
|
|
807
808
|
|
|
808
809
|
---
|
|
809
810
|
|
|
811
|
+
## AgeField
|
|
812
|
+
|
|
813
|
+

|
|
814
|
+
|
|
815
|
+
An age as one number and one unit, for what is scheduled by age (a dose due at
|
|
816
|
+
2 months, a check at 11 years) or by interval. It is a `QuantityField` whose
|
|
817
|
+
unit is day, week, month or year: one label, a number with its steppers, a unit
|
|
818
|
+
picker, and one message under the pair.
|
|
819
|
+
|
|
820
|
+
```tsx
|
|
821
|
+
import { AgeField, type AgeValue } from '@nxgt/material/components';
|
|
822
|
+
import { useState } from 'react';
|
|
823
|
+
|
|
824
|
+
export function DueAt() {
|
|
825
|
+
const [age, setAge] = useState<AgeValue>({ value: 2, unit: 'month' });
|
|
826
|
+
return <AgeField label="Due at" units={['week', 'month', 'year']} value={age} onChange={setAge} />;
|
|
827
|
+
}
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
| Prop | Type | Default | Effect |
|
|
831
|
+
| --- | --- | --- | --- |
|
|
832
|
+
| `value` / `defaultValue` | `AgeValue` = `{ value: number \| null; unit: AgeUnit }` | `{ value: null, unit: <first unit> }` | the number (`null` while empty) and its unit |
|
|
833
|
+
| `onChange` | `(value: AgeValue) => void` | — | fires on a number or a unit change |
|
|
834
|
+
| `units` | `AgeUnit[]` (`'day' \| 'week' \| 'month' \| 'year'`) | all four | the units offered, in this order; the first is the unit of an empty field |
|
|
835
|
+
| `min` / `max` | `number \| Partial<Record<AgeUnit, number>>` | `min` 0, no `max` | one bound for every unit, or one per unit (`max={{ month: 23, year: 120 }}`); the bound of the chosen unit applies |
|
|
836
|
+
| `name` | `string` | — | a native form posts `${name}.value` and `${name}.unit` |
|
|
837
|
+
| `required`, `disabled`, `error`, `helperText`, `hideSteppers` | | | as in `QuantityField` |
|
|
838
|
+
| `ref` | `Ref<HTMLInputElement>` | — | reaches the number input (`AgeFormField` passes React Hook Form's `field.ref`) |
|
|
839
|
+
|
|
840
|
+
**Read aloud.** The unit names are plural-aware and translated
|
|
841
|
+
(`material.age-field.unit-day`, `unit-week`, `unit-month`, `unit-year`: "1 month",
|
|
842
|
+
"2 months", « 2 mois »), and a polite status says the whole age when it changes
|
|
843
|
+
(`material.age-field.reading`). `ageReading(t, 2, 'month')` gives the same text
|
|
844
|
+
for your own use. The unit picker is named "Age unit" (`material.age-field.unit`).
|
|
845
|
+
|
|
846
|
+
**In tests.** One `group` named by the label, a `spinbutton` for the number
|
|
847
|
+
and a `combobox` named "Age unit":
|
|
848
|
+
|
|
849
|
+
```ts
|
|
850
|
+
await user.click(screen.getByRole('combobox', { name: 'Age unit' }));
|
|
851
|
+
await user.click(await screen.findByRole('option', { name: 'years' }));
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
**Emits:** `{ value: number | null, unit: AgeUnit }`.
|
|
855
|
+
**Form wrapper:** `AgeFormField` — `control` and `name`; an error on the field,
|
|
856
|
+
on `<name>.value` or on `<name>.unit` shows as the one message. See
|
|
857
|
+
[forms](../guides/forms.md#what-each-field-emits).
|
|
858
|
+
**Storybook:** `Date & time/AgeField` · **Vue:** none yet
|
|
859
|
+
|
|
860
|
+
---
|
|
861
|
+
|
|
810
862
|
## DurationField
|
|
811
863
|
|
|
812
864
|

|
|
@@ -660,7 +660,7 @@ The default description is the companion of its title: with your own `title` and
|
|
|
660
660
|
|
|
661
661
|

|
|
662
662
|
|
|
663
|
-
An `EmptyState` for a list page opened at a cursor that failed to load (a stale link, a lost page): a title and a tonal "Back to the start" link. The link is rendered by the `LinkComponentProvider`'s component. For the next page of a list that is already shown, use [`LoadMoreFooter`](./buttons-and-actions.md#loadmorefooter), which keeps the rows.
|
|
663
|
+
An `EmptyState` for a list page opened at a cursor that failed to load (a stale link, a lost page): a title and a tonal "Back to the start" link, with an optional filled Retry. The link is rendered by the `LinkComponentProvider`'s component. For the next page of a list that is already shown, use [`LoadMoreFooter`](./buttons-and-actions.md#loadmorefooter), which keeps the rows.
|
|
664
664
|
|
|
665
665
|
```tsx
|
|
666
666
|
import { PageLoadFailed } from '@nxgt/material/components';
|
|
@@ -668,13 +668,22 @@ import { PageLoadFailed } from '@nxgt/material/components';
|
|
|
668
668
|
<PageLoadFailed href="/orders" />;
|
|
669
669
|
```
|
|
670
670
|
|
|
671
|
+
With a retry (the promise keeps Retry loading until it settles; the link turns ghost):
|
|
672
|
+
|
|
673
|
+
```tsx
|
|
674
|
+
<PageLoadFailed href="/orders" onRetry={() => refetch()} />;
|
|
675
|
+
```
|
|
676
|
+
|
|
671
677
|
| Prop | Type | Default | Effect |
|
|
672
678
|
| --- | --- | --- | --- |
|
|
673
679
|
| `href` | `string` | — | where "Back to the start" goes: the list without its cursor |
|
|
674
680
|
| `linkLabel` | `string` | `"Back to the start"` (translated) | the link's text |
|
|
681
|
+
| `onRetry` | `() => unknown` | — | shows a filled Retry before the link (the link turns ghost); a returned promise keeps it loading until settled |
|
|
682
|
+
| `retrying` | `boolean` | from the promise | forces Retry's loading state |
|
|
683
|
+
| `retryLabel` | `ReactNode` | `"Retry"` (translated) | Retry's text |
|
|
675
684
|
| `title` | `ReactNode` | `"This page could not be loaded"` (translated) | the title |
|
|
676
685
|
|
|
677
|
-
It takes the other `EmptyState` props (`size`, `illustration`, `description`, `headingLevel`) except `action`. Texts are `material.load-more.page-failed-title`
|
|
686
|
+
It takes the other `EmptyState` props (`size`, `illustration`, `description`, `headingLevel`) except `action`. Texts are `material.load-more.page-failed-title`, `material.load-more.back-to-start` and, for Retry, `material.error-state.retry`.
|
|
678
687
|
|
|
679
688
|
**Storybook:** `Feedback & status/PageLoadFailed` · **Vue:** not yet (tracked as a `vue` issue)
|
|
680
689
|
|
|
@@ -1139,7 +1148,15 @@ of a [`MediaPlayerHost`](./media-and-files.md#mediaplayerhost)'s docked bar
|
|
|
1139
1148
|
corner (`--nxgt-media-float`: a host's, or a `floatOnScroll` player's, with
|
|
1140
1149
|
or without a host), so toasts never cover them. An
|
|
1141
1150
|
`offset` or `mobileOffset` you pass replaces that default: add the variables
|
|
1142
|
-
yourself, e.g. `offset={{ bottom: 'calc(32px + var(--nxgt-media-dock, 0px) + var(--nxgt-media-float, 0px))' }}`.
|
|
1151
|
+
yourself, e.g. `offset={{ bottom: 'calc(32px + var(--nxgt-media-dock, 0px) + var(--nxgt-media-float, 0px) + var(--nxgt-sticky-bar, 0px))' }}`.
|
|
1152
|
+
|
|
1153
|
+
**Above a `StickyActionBar`:** the bottom offsets, on a desktop and on a
|
|
1154
|
+
phone, also add the height of a mounted
|
|
1155
|
+
[`StickyActionBar`](./navigation-and-layout.md#stickyactionbar)
|
|
1156
|
+
(`--nxgt-sticky-bar`, 0 when none is mounted), so a toast never covers the
|
|
1157
|
+
bar's primary action. An `offset` or `mobileOffset` you pass drops that too:
|
|
1158
|
+
add `var(--nxgt-sticky-bar, 0px)` yourself, e.g.
|
|
1159
|
+
`offset={{ bottom: 'calc(32px + var(--nxgt-sticky-bar, 0px))' }}`.
|
|
1143
1160
|
|
|
1144
1161
|
```tsx
|
|
1145
1162
|
<Toaster /> // bottom-right on a desktop, top-center on a phone
|
|
@@ -573,7 +573,7 @@ export function Quantity() {
|
|
|
573
573
|
| `min`, `max` | `number` | — | clamps typed and stepped values |
|
|
574
574
|
| `step` | `number` | `1` | the +/− increment |
|
|
575
575
|
| `hideSteppers` | `boolean` | `false` | hides the +/− buttons |
|
|
576
|
-
| `suffix` | `ReactNode` | — | unit
|
|
576
|
+
| `suffix` | `ReactNode` | — | unit shown inside the input box, at its end |
|
|
577
577
|
| `aria-label` | `string` | — | names the input when it has no `label` (a part of a larger field) |
|
|
578
578
|
| `aria-labelledby` | `string` | — | names the input by another element's id (a part of a larger field) |
|
|
579
579
|
| `aria-describedby` | `string` | — | more ids describing the input, after its `helperText` (an error the larger field shows) |
|
|
@@ -1662,6 +1662,7 @@ import { Switch } from '@nxgt/material/components';
|
|
|
1662
1662
|
| `helperText` | `ReactNode` | — | line under the row (or under the bare switch), with or without `placeholder`; `SwitchFormField` puts the validation message here |
|
|
1663
1663
|
| `error` | `boolean` | — | error border on the row, error helper text, `aria-invalid` on the `switch` (a passed `aria-invalid` wins) |
|
|
1664
1664
|
| `thumbIcons` | `boolean` | `false` | a 24px thumb in both states, a cross when off and a check when on |
|
|
1665
|
+
| `variant` | `'box' \| 'plain'` | `'box'` | `plain` draws a labelled switch with no border and no tint: the label beside the switch. No effect on a bare switch |
|
|
1665
1666
|
| `className` | `string` | — | on the outermost element: the field, else the row, else the switch |
|
|
1666
1667
|
|
|
1667
1668
|
```tsx
|
|
@@ -1670,6 +1671,14 @@ import { Switch } from '@nxgt/material/components';
|
|
|
1670
1671
|
|
|
1671
1672
|
`SwitchField`, `SwitchFormField` and `<Field type="switch">` take `thumbIcons` too.
|
|
1672
1673
|
|
|
1674
|
+
```tsx
|
|
1675
|
+
<SwitchFormField control={control} name="notify" label="Notify me" variant="plain" />
|
|
1676
|
+
```
|
|
1677
|
+
|
|
1678
|
+
`SwitchField` and `<Field type="switch">` take `variant` too. `plain` keeps the
|
|
1679
|
+
row's layout, tap target and `<label for>`; an `error` still shows in the
|
|
1680
|
+
helper text and `aria-invalid`.
|
|
1681
|
+
|
|
1673
1682
|
The switch is a 52x32 track with a 2px outline when off and the accent fill when
|
|
1674
1683
|
on; the thumb grows from 16px to 24px and carries a check, and a round state
|
|
1675
1684
|
layer follows it. Everything is instant under reduced motion and uses system
|
|
@@ -1703,7 +1712,7 @@ libraries; neither name is deprecated, and both keep working.
|
|
|
1703
1712
|
**Emits:** `boolean`.
|
|
1704
1713
|
**Form wrapper:** `SwitchFormField` — `control` and `name`, see
|
|
1705
1714
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
1706
|
-
**Storybook:** `Form fields/Switch` · **Vue:** `Switch`, `SwitchField`, `SwitchFormField`
|
|
1715
|
+
**Storybook:** `Form fields/Switch` · **Vue:** `Switch`, `SwitchField`, `SwitchFormField` (no `variant` yet)
|
|
1707
1716
|
|
|
1708
1717
|
---
|
|
1709
1718
|
|
|
@@ -81,7 +81,7 @@ phone's menu sheet pad clear of `env(safe-area-inset-*)`, as classes; see the
|
|
|
81
81
|
|
|
82
82
|
| Prop | Type | Default | Effect |
|
|
83
83
|
| --- | --- | --- | --- |
|
|
84
|
-
| `items` | `MenuItem[]` | — | `label` (already translated), `url`, `icon`, `footer`, `display` (`'icon'`: a round icon-only button, the label as tooltip and name; see the [app shell guide](../guides/app-shell.md)), `badge` (`true` a dot, a number a count up to "99+"), `badgeLabel` (see [Badges](../guides/app-shell.md#badges)), and `children` (read by `navigation="top"`, which opens them in a dropdown under a parent that is then only a trigger; by `navigation="two-level"`, which lists them in the panel beside the rail, one level deep; by the phone's menu sheet and the expanded sidebar, which nest them under the parent's own link), `group`, `bar`, `priority` for the navigation presets that read them (the drawer reads `group`, the bar `bar` and `priority`; the floating toolbar, not drawn yet, is the only preset still to read them) |
|
|
84
|
+
| `items` | `MenuItem[]` | — | `label` (already translated), `url`, `icon`, `footer`, `display` (`'icon'`: a round icon-only button, the label as tooltip and name, in the rail and the expanded sidebar; the phone's menu sheet always shows icon and label; see the [app shell guide](../guides/app-shell.md)), `badge` (`true` a dot, a number a count up to "99+"), `badgeLabel` (see [Badges](../guides/app-shell.md#badges)), and `children` (read by `navigation="top"`, which opens them in a dropdown under a parent that is then only a trigger; by `navigation="two-level"`, which lists them in the panel beside the rail, one level deep; by the phone's menu sheet and the expanded sidebar, which nest them under the parent's own link, unless the entry sets `childrenIn: 'sidebar'`, which keeps them to the sidebar and the top dropdown), `childrenIn`, `group`, `bar`, `priority` for the navigation presets that read them (the drawer reads `group`, the bar `bar` and `priority`; the floating toolbar, not drawn yet, is the only preset still to read them) |
|
|
85
85
|
| `linkComponent` | `AppShellLinkComponent` | React Router `Link` | The link each menu entry renders, given `href`; see the [app shell guide](../guides/app-shell.md). Defaults to the [`LinkComponentProvider`](./primitives.md#linkcomponentprovider)'s link when one is set. |
|
|
86
86
|
| `logo`, `name` | `AppLogo` (`ReactNode \| string \| AppLogoImage`, `AppLogoImage` = `{ src, alt? }`), `string` | — | the app's logo alone in the collapsed rail, named by `name` (also its tooltip); logo and name side by side in the phone's menu sheet |
|
|
87
87
|
| `context` | `ReactNode` | — | a line at the top of the phone's menu sheet, above the brand: the current tenant's name; `name` stays the app's brand; `AppHeader` and `AppSidebar` take it too (see the [app shell guide](../guides/app-shell.md#the-brand-logo-and-name)) |
|
|
@@ -104,7 +104,7 @@ phone's menu sheet pad clear of `env(safe-area-inset-*)`, as classes; see the
|
|
|
104
104
|
| `width` | `'full' \| 'form' \| 'reading'` | `'full'` | `ActivityContent`: caps the main column at 48rem (`'form'`) or 72ch (`'reading'`), the side panel right beside it |
|
|
105
105
|
| `notice` | `ReactNode` | — | `ActivityContent`: a notice above the column and the side panel, at their width, taking its own height; first on a phone |
|
|
106
106
|
| `aside`, `asideLabel` / `asideLabelledBy` | `ReactNode`, `string` | — | `ActivityContent`: a side panel, a named `complementary` landmark, beside the content from 1280px (CSS only), stacked below; sticky under the app bar |
|
|
107
|
-
| `asideSide` / `asideWidth` / `asideFrom` / `asideSticky` / `asideStack` | `'start' \| 'end'` / `'sm' \| 'md' \| 'lg' \| string` / `'lg' \| 'xl' \| '2xl'` / `boolean` / `'before' \| 'after'` | `'end'` / `'md'` / `'xl'` / `true` / `'after'` | `ActivityContent`: the panel's side, width (16, 20, 24rem), breakpoint, stickiness and stacking order (see the [app shell guide](../guides/app-shell.md#a-side-panel-and-a-readable-width)) |
|
|
107
|
+
| `asideSide` / `asideWidth` / `asideFrom` / `asideSticky` / `asideStack` | `'start' \| 'end'` / `'sm' \| 'md' \| 'lg' \| string` / `'lg' \| 'xl' \| '2xl'` / `boolean` / `'before' \| 'after'` | `'end'` / `'md'` / `'xl'` / `true` / `'after'` | `ActivityContent` (`asidePhone`: the panel stacks on a phone, or with `"toggle"` sits behind a Content / Preview switch, texts `asideMainLabel` / `asidePreviewLabel`; known limit: a save bar inside the content is hidden while the preview shows): the panel's side, width (16, 20, 24rem), breakpoint, stickiness and stacking order (see the [app shell guide](../guides/app-shell.md#a-side-panel-and-a-readable-width)) |
|
|
108
108
|
| `navigation` | `ShellNavigation \| { phone?, tablet?, desktop?, hideOnScroll?, persistKey? }` | `{ phone: 'modal-drawer', tablet: 'rail', desktop: 'rail' }` | `ActivityLayout`: the navigation per window size (phone below 768px, tablet to 1279px, desktop from 1280px). Every `ShellNavigation` name is drawn: `'modal-drawer'`, `'rail'`, `'rail-expanded'`, `'drawer'`, `'sidebar'`, `'top'`, `'two-level'`, `'floating'`, `'bar'` and `'none'`. `hideOnScroll` (a field of this object, `false` by default) slides the `'floating'` toolbar away on scroll; no other preset reads it. `persistKey` (a field of this object) remembers the `'rail-expanded'` rail's, the `'sidebar'`'s and the `'two-level'` panel's state. A name alone (`navigation="sidebar"`, `"drawer"`, `"top"`, `"two-level"`) leaves a phone its default navigation; `{ phone: 'sidebar' }` draws it there. A phone never draws `'rail'` or `'rail-expanded'`: it keeps its menu sheet. `navigation="bar"` alone is phone-only (hidden from 768px, where the default rail shows; `{ tablet: 'bar' }` keeps it there). `'floating'` is drawn at every size, the phone included. See [`navigation` and `header`](../guides/app-shell.md#navigation-and-header) |
|
|
109
109
|
| `navigationFab` | `ReactNode \| ({ expanded }) => ReactNode` | — | `ActivityLayout`: the screen's primary action, at the top of the `'rail-expanded'` rail (a `Fab`; the function form receives the rail's state). The other presets do not draw it |
|
|
110
110
|
| `sidebarExpanded` / `defaultSidebarExpanded` / `onSidebarExpandedChange` | `boolean` / `boolean` / `(expanded: boolean) => void` | — / `false` / — | `ActivityLayout`: the `'sidebar'`'s expanded state (labels beside the icons; it starts collapsed), controlled or not; with `navigation.persistKey` the last choice is kept and read after hydration |
|
|
@@ -599,6 +599,11 @@ the page. It takes a `div`'s props.
|
|
|
599
599
|
(`interactive-widget=resizes-content`) needs no lift, and none is applied.
|
|
600
600
|
Pinch-zoom is not treated as a keyboard.
|
|
601
601
|
- **Name.** With `aria-label` or `aria-labelledby` the bar is a `group`.
|
|
602
|
+
- **Toasts clear it.** While mounted and sticky, the bar publishes its height as
|
|
603
|
+
`--nxgt-sticky-bar` on the document root and removes it on unmount; the
|
|
604
|
+
[`Toaster`](./feedback-and-status.md#toaster)'s bottom offsets add it. With
|
|
605
|
+
several bars the latest wins. From `md` the bar is inline, so a desktop
|
|
606
|
+
toast is lifted by the bar's height.
|
|
602
607
|
|
|
603
608
|
| Prop | Type | Default | Effect |
|
|
604
609
|
| --- | --- | --- | --- |
|
|
@@ -743,6 +743,12 @@ tab order; on a touch screen 44px wide and as tall as the list, so it never cove
|
|
|
743
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
|
+
**No buttons: `scrollButtons="none"`** (on `TabsList` and `TabsNav`; the default is `"auto"`, as above). The list stays a plain row that scrolls by touch, wheel and keyboard, with no chevron buttons and no tab turned transparent. The cue stays: each side with hidden tabs still sets `data-overflow-left` / `data-overflow-right` and fades softly over 1.5rem, so a half-shown tab shows that more lies beyond it, and the selected or focused tab is still scrolled into view. The list carries `data-scroll-buttons="none"`.
|
|
747
|
+
|
|
748
|
+
```tsx
|
|
749
|
+
<TabsList scrollButtons="none">{/* … */}</TabsList>
|
|
750
|
+
```
|
|
751
|
+
|
|
746
752
|
**`segmented`** is a neutral pill track: the active tab sits on a raised
|
|
747
753
|
surface that slides between tabs, and its label turns to the foreground
|
|
748
754
|
colour and semibold — the state never rests on the surface alone. It suits a
|
|
@@ -876,6 +882,7 @@ See [the troubleshooting entry](../troubleshooting.md#usehref-may-be-used-only-i
|
|
|
876
882
|
| `aria-label` | `TabsNav` | `string` | — (required) | names the navigation |
|
|
877
883
|
| `value` | `TabsNav` | `string` | — | the current link's `value` |
|
|
878
884
|
| `variant` | `TabsNav` | `'underline' \| 'segmented'` | `'underline'` | the look, as `Tabs` |
|
|
885
|
+
| `scrollButtons` | `TabsNav`, `TabsList` | `'auto' \| 'none'` | `'auto'` | `'none'` leaves out the scroll buttons: a plain scrollable row with a soft edge fade |
|
|
879
886
|
| `to` | `TabsNavLink` | `To` | — (required) | the route, as for `LinkButton` |
|
|
880
887
|
| `value` | `TabsNavLink` | `string` | — | matched against the nav's `value` |
|
|
881
888
|
| `current` | `TabsNavLink` | `boolean` | — | marks the link current or not, over `value` |
|
|
@@ -26,7 +26,7 @@ value type each one writes. Everything is imported from
|
|
|
26
26
|
| <img src="../previews/selection-choice-transferlist.webp" width="96" alt=""> | [TransferList](#transferlist) | Two-pane picker |
|
|
27
27
|
| <img src="../previews/selection-choice-sortablelist.webp" width="96" alt=""> | [SortableList](#sortablelist) | Reorderable list of strings |
|
|
28
28
|
| <img src="../previews/selection-choice-transferlist.webp" width="96" alt=""> | [TransferList](#transferlist) | Two-pane picker: search, counts, select-all, drag between panes |
|
|
29
|
-
| <img src="../previews/selection-choice-sortablelist.webp" width="96" alt=""> | [SortableList](#sortablelist) | Reorderable list: drag or keyboard, announced |
|
|
29
|
+
| <img src="../previews/selection-choice-sortablelist.webp" width="96" alt=""> | [SortableList](#sortablelist) | Reorderable list: drag or keyboard, announced; two-line labels, row menu |
|
|
30
30
|
| <img src="../previews/selection-choice-treeselectfield.webp" width="96" alt=""> | [TreeSelectField](#treeselectfield) | Pick from a tree, single or multiple |
|
|
31
31
|
| <img src="../previews/selection-choice-switcher.webp" width="96" alt=""> | [Switcher](#switcher) | Workspace, establishment or class switcher: menu, search, groups, links; `size="compact"` an icon-only round trigger for the app bar's `titleAfter` slot |
|
|
32
32
|
|
|
@@ -1182,6 +1182,8 @@ export function Ranking() {
|
|
|
1182
1182
|
| `appearance` | `'cards' \| 'plain'` | `'cards'` | raised cards, or flat rows in one surface |
|
|
1183
1183
|
| `density` | `'comfortable' \| 'compact'` | `'comfortable'` | `compact`: one-line rows |
|
|
1184
1184
|
| `dragActivator` | `'row' \| 'handle'` | `'row'` | what starts a pointer drag: the whole row or only its grip |
|
|
1185
|
+
| `labelLines` | `1 \| 2` | `1` | lines a row's label may take before an ellipsis; `2` for long labels at phone width (the full text stays in the `title`). Ignored with `renderItem` |
|
|
1186
|
+
| `rowActions` | `(item) => SortableListRowAction[] \| undefined` | — | a "..." menu on each row, before the remove button, named "Actions for {label}" (44 px on touch); `SortableListRowAction` is `RowMenuItem`; `[]` or nothing means no menu. The trigger stays enabled on a `locked` row (edit or duplicate do not move it): return `[]` for a locked item to leave it out. On a touch pointer the trigger is 44 px, so a `compact` row grows to fit it; with a fine pointer it stays 36 px. Ignored with `renderItem` |
|
|
1185
1187
|
|
|
1186
1188
|
`SortableListItem` also takes `avatar`, `badge` and `locked` (the row stays
|
|
1187
1189
|
where it is: no grip, not draggable, not movable from the keyboard, not
|
|
@@ -1196,7 +1198,7 @@ await user.click(within(rows[0]).getByRole('button', { name: /^Move down/ }));
|
|
|
1196
1198
|
```
|
|
1197
1199
|
|
|
1198
1200
|
**Emits:** `string[]`, in the new order.
|
|
1199
|
-
**Form wrapper:** `SortableListFormField` — `control` and `name` plus the same props, `hideLabel` included (the label's `required` asterisk is hidden with it; the asterisk only: a `list` takes no `aria-required`, so a required list is described by a visually hidden "This field is required."), see
|
|
1201
|
+
**Form wrapper:** `SortableListFormField` — `control` and `name` plus the same props, `hideLabel`, `labelLines` and `rowActions` included (the label's `required` asterisk is hidden with it; the asterisk only: a `list` takes no `aria-required`, so a required list is described by a visually hidden "This field is required."), see
|
|
1200
1202
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
1201
1203
|
**Storybook:** `Selection & choice/SortableList` · **Vue:** `SortableList`, `SortableListFormField`
|
|
1202
1204
|
|