@nxgt/material 3.64.0 → 3.66.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 +46 -0
- package/README.md +4 -1
- package/dist/{alert-dialog-RHm6WtQj.js → alert-dialog-BgQDxcYh.js} +1 -1
- package/dist/{async-slice-DmX59NyA.js → async-slice-BCZyWgGr.js} +48 -25
- package/dist/{background-qdnet2A5.js → background-D1gC4m2R.js} +1 -1
- package/dist/backgrounds.js +1 -1
- package/dist/{button-DfS_amfQ.js → button-2de_lrix.js} +1 -1
- package/dist/charts.js +818 -807
- package/dist/{chip-Cbndukko.js → chip-Cs0uEjpu.js} +2 -2
- package/dist/{code-editor-B0tN8Ha_.js → code-editor-BYhBAfcc.js} +403 -393
- package/dist/code-editor.js +5 -5
- package/dist/{code-language-items-andgHKrM.js → code-language-items-xhmbF6wA.js} +2 -2
- package/dist/components/components.css +2 -0
- package/dist/components/ui/display/chip/chip-soft.css +19 -5
- package/dist/components/ui/layout/app-shell/activity-backdrop.css +4 -3
- package/dist/components/ui/primitives/tabs/tabs-nav-forced.css +27 -0
- package/dist/components/ui/primitives/toggle-group/toggle-group-segmented.css +10 -2
- package/dist/components/ui/primitives/toggle-group/toggle-group.css +5 -1
- package/dist/{components-2kTVPsMg.js → components-DYiyCe6v.js} +16922 -16679
- package/dist/components.js +28 -28
- package/dist/{compose-refs-DZ0iIVF-.js → compose-refs-G4J4kaUA.js} +1 -1
- package/dist/{dialog-Bd7Hid2L.js → dialog-Co5oksU1.js} +4 -4
- package/dist/dnd.js +2 -2
- package/dist/{dropdown-menu-_SyVmSuy.js → dropdown-menu-DdzyvN76.js} +1 -1
- package/dist/editor.js +365 -365
- package/dist/{empty-state-D-2ZMXXJ.js → empty-state-CQ0CV0kA.js} +2 -2
- package/dist/{field-blur-DyOByjzw.js → field-blur-B2y-bNYi.js} +1 -1
- package/dist/{field-blur-context-DPe5_Nv5.js → field-blur-context-BZtwcPUV.js} +1 -1
- package/dist/{hooks-a7_exKWS.js → hooks-DjuYsY-j.js} +2 -2
- package/dist/hooks.js +2 -2
- package/dist/{hover-card-D_0smKBQ.js → hover-card-BeBVy6tB.js} +1 -1
- package/dist/i18n.js +3 -3
- package/dist/{icon-badge-CBCU3ySF.js → icon-badge-DbYsbzwZ.js} +3 -3
- package/dist/{icon-button-hKHsWTAt.js → icon-button-Co6ttdv1.js} +1 -1
- package/dist/lib/components/ui/actions/index.d.ts +1 -0
- package/dist/lib/components/ui/actions/link/link.d.ts +1 -19
- package/dist/lib/components/ui/actions/share-button/index.d.ts +3 -0
- package/dist/lib/components/ui/actions/share-button/share-button.d.ts +17 -0
- package/dist/lib/components/ui/actions/share-button/share-or-copy.d.ts +17 -0
- package/dist/lib/components/ui/collections/dnd/index.d.ts +1 -1
- package/dist/lib/components/ui/collections/filter/use-filter-field-config.d.ts +1 -0
- package/dist/lib/components/ui/collections/server-table/past-end.d.ts +3 -0
- package/dist/lib/components/ui/collections/server-table/types.d.ts +2 -2
- package/dist/lib/components/ui/collections/server-table/use-table-answer.d.ts +1 -1
- package/dist/lib/components/ui/display/enrolment-card/use-enrolment-card-labels.d.ts +1 -1
- package/dist/lib/components/ui/display/recovery-codes/use-recovery-codes-labels.d.ts +1 -1
- package/dist/lib/components/ui/display/recovery-codes-status/use-recovery-codes-status-labels.d.ts +1 -1
- package/dist/lib/components/ui/display/stat-card/partials/stat-card-link-parts.d.ts +7 -0
- package/dist/lib/components/ui/display/stat-card/stat-card.d.ts +6 -1
- package/dist/lib/components/ui/display/stat-card/types.d.ts +14 -0
- package/dist/lib/components/ui/feedback/step-up-dialog/use-step-up-labels.d.ts +1 -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 +4 -1
- package/dist/lib/components/ui/fields/number-field/number-stepper.d.ts +5 -0
- package/dist/lib/components/ui/fields/verification-code/use-verification-labels.d.ts +1 -1
- package/dist/lib/components/ui/layout/app-shell/route-transition.d.ts +1 -1
- package/dist/lib/components/ui/primitives/link/link-component-provider.d.ts +1 -1
- package/dist/lib/components/ui/primitives/link/nav-current.d.ts +22 -0
- package/dist/lib/components/ui/primitives/link/router-link.d.ts +25 -0
- package/dist/lib/components/ui/primitives/tabs/index.d.ts +2 -0
- package/dist/lib/components/ui/primitives/tabs/internal.d.ts +2 -0
- package/dist/lib/components/ui/primitives/tabs/partials/tabs-nav-link.d.ts +22 -0
- package/dist/lib/components/ui/primitives/tabs/tab-param-url.d.ts +53 -0
- package/dist/lib/components/ui/primitives/tabs/tabs-nav.d.ts +22 -0
- package/dist/lib/components/ui/primitives/tabs/tabs-selectors.d.ts +8 -0
- package/dist/lib/components/ui/primitives/tabs/use-tab-param.d.ts +35 -0
- package/dist/lib/components/ui/primitives/tabs/use-tabs-overflow.d.ts +4 -1
- package/dist/lib/components/ui/primitives/toggle-group/index.d.ts +1 -0
- package/dist/lib/components/ui/primitives/toggle-group/segmented-nav-item.d.ts +20 -0
- package/dist/lib/components/ui/primitives/toggle-group/segmented-nav.d.ts +22 -0
- package/dist/lib/components/ui/primitives/toggle-group/toggle-group.d.ts +4 -3
- package/dist/lib/components/ui/widgets/code-editor/code-editor-field.d.ts +7 -6
- package/dist/lib/components/ui/widgets/code-editor/code-editor-types.d.ts +6 -0
- package/dist/lib/components/ui/widgets/code-editor/use-code-editor-instance.d.ts +12 -1
- package/dist/lib/components/ui/widgets/native-charts/core/reference-geometry.d.ts +16 -2
- package/dist/lib/i18n/resources/en/en.d.ts +6 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +7 -1
- package/dist/lib/i18n/resources/fr/fr.d.ts +6 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +7 -1
- package/dist/lib/i18n/resources/resources.d.ts +12 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib.js +31 -54
- 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-D8JGp3PH.js → public-AXwv1h3v.js} +10 -10
- package/dist/{rich-code-block-0R3bidsM.js → rich-code-block-qSvkA5rI.js} +5 -5
- package/dist/{sortable-provider-B75v53sB.js → sortable-provider-DPk-RVEU.js} +3 -3
- package/dist/{spinner-CAnpF3RK.js → spinner-PdSk7zEs.js} +1 -1
- package/dist/theme-picker.js +2 -2
- package/dist/{theme-toggle-pQ5F602V.js → theme-toggle-CZQckLDd.js} +2 -2
- package/dist/{toggle-group-BkH23Jk4.js → toggle-group-DXXlqid2.js} +1 -1
- package/dist/{toolbar-items-_ezD5B0r.js → toolbar-items-BmNYNaRB.js} +3 -3
- package/dist/{translate-material-CvZK8_S6.js → translate-material-BlCQ6AW9.js} +1 -1
- package/dist/{translator-Caf-d96e.js → translator-ChpeKCO3.js} +10 -2
- package/dist/{typography-CEK19bj8.js → typography-C4JSRr2B.js} +1 -1
- package/dist/{use-chat-composer-Dpvi_-CR.js → use-chat-composer-OUa9akfM.js} +1725 -1719
- package/dist/{use-heading-spy-mmcyEGmm.js → use-heading-spy-K-HUp4o_.js} +4 -4
- package/dist/{use-material-translation-C_1cWRzW.js → use-material-translation-DkE9J5NE.js} +1 -1
- package/dist/{use-return-focus-1ZIchrBM.js → use-return-focus-KypnR7vv.js} +1 -1
- package/docs/components/README.md +3 -1
- package/docs/components/buttons-and-actions.md +30 -1
- package/docs/components/data-display.md +14 -1
- package/docs/components/data-table-filter-and-lists.md +16 -3
- package/docs/components/form-fields.md +9 -1
- package/docs/components/navigation-and-layout.md +9 -5
- package/docs/components/primitives.md +209 -0
- package/docs/components/rich-widgets.md +1 -0
- package/docs/guides/charts.md +1 -1
- package/docs/guides/code-editor.md +14 -0
- package/docs/guides/i18n.md +3 -1
- package/docs/previews/buttons-actions-sharebutton.webp +0 -0
- package/docs/previews/primitives-segmentednav.webp +0 -0
- package/docs/previews/primitives-tabsnav.webp +0 -0
- package/docs/roadmap.md +2 -1
- package/docs/troubleshooting.md +35 -7
- package/lib/components/components.css +2 -0
- package/lib/components/ui/display/chip/chip-soft.css +19 -5
- package/lib/components/ui/layout/app-shell/activity-backdrop.css +4 -3
- package/lib/components/ui/primitives/tabs/tabs-nav-forced.css +27 -0
- package/lib/components/ui/primitives/toggle-group/toggle-group-segmented.css +10 -2
- package/lib/components/ui/primitives/toggle-group/toggle-group.css +5 -1
- package/package.json +2 -2
- /package/dist/lib/{components/ui/collections/dnd → lib}/compose-refs.d.ts +0 -0
|
@@ -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-BgQDxcYh.js";
|
|
3
|
+
import { a as l } from "./button-2de_lrix.js";
|
|
4
|
+
import { i as u } from "./translate-material-BlCQ6AW9.js";
|
|
5
|
+
import { t as d } from "./use-material-translation-DkE9J5NE.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 { jsx as C, jsxs as w } 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-PdSk7zEs.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";
|
|
@@ -14,10 +14,12 @@ What the stylesheet gives any element, not a component: the animation classes (`
|
|
|
14
14
|
|
|
15
15
|
## [Primitives](primitives.md)
|
|
16
16
|
|
|
17
|
-
Building blocks with no opinion of their own: card surface, typography, label, input, textarea, input group, icon, kbd, separator, scroll area, table, tabs, toggle, accordion, collapsible.
|
|
17
|
+
Building blocks with no opinion of their own: card surface, typography, label, input, textarea, input group, icon, kbd, separator, scroll area, table, tabs, route tabs, toggle, segmented route links, accordion, collapsible. 22 entries.
|
|
18
18
|
|
|
19
19
|
[<img src="../previews/primitives-card.webp" height="72" alt="Card">](primitives.md#card)
|
|
20
20
|
[<img src="../previews/primitives-tabs.webp" height="72" alt="Tabs">](primitives.md#tabs)
|
|
21
|
+
[<img src="../previews/primitives-tabsnav.webp" height="72" alt="TabsNav">](primitives.md#tabsnav)
|
|
22
|
+
[<img src="../previews/primitives-segmentednav.webp" height="72" alt="SegmentedNav">](primitives.md#segmentednav)
|
|
21
23
|
[<img src="../previews/primitives-inputgroup.webp" height="72" alt="InputGroup">](primitives.md#inputgroup)
|
|
22
24
|
[<img src="../previews/primitives-table.webp" height="72" alt="Table">](primitives.md#table)
|
|
23
25
|
|
|
@@ -16,6 +16,7 @@ and language switches. Everything is imported from `@nxgt/material/components`.
|
|
|
16
16
|
| <img src="../previews/buttons-actions-responsivebutton.webp" width="96" alt=""> | [ResponsiveButton](#responsivebutton) | Label on desktop, icon-only on mobile |
|
|
17
17
|
| <img src="../previews/buttons-actions-linkbutton.webp" width="96" alt=""> | [LinkButton](#linkbutton) | The button styles as `react-router` links |
|
|
18
18
|
| <img src="../previews/buttons-actions-copybutton.webp" width="96" alt=""> | [CopyButton](#copybutton) | Copies a value, shows a check mark |
|
|
19
|
+
| <img src="../previews/buttons-actions-sharebutton.webp" width="96" alt=""> | [ShareButton](#sharebutton) | Opens the system share sheet, copies the link where there is none |
|
|
19
20
|
| <img src="../previews/buttons-actions-highlighttext.webp" width="96" alt=""> | [HighlightText](#highlighttext) | Marks matches of a query inside a string |
|
|
20
21
|
| <img src="../previews/buttons-actions-loadmorebutton.webp" width="96" alt=""> | [LoadMoreButton](#loadmorebutton) | "Load more" for cursor pagination |
|
|
21
22
|
| <img src="../previews/buttons-actions-loadmorefooter.webp" width="96" alt=""> | [LoadMoreFooter](#loadmorefooter) | Shown-of-total line, load button and a failed state with retry, for a paged list |
|
|
@@ -919,6 +920,34 @@ The write is awaited: "Copied" appears only once the clipboard accepted the text
|
|
|
919
920
|
|
|
920
921
|
---
|
|
921
922
|
|
|
923
|
+
## ShareButton
|
|
924
|
+
|
|
925
|
+

|
|
926
|
+
|
|
927
|
+
Shares a link, a text or a title through the system share sheet (`navigator.share`) and copies the link instead where the sheet is missing, or when it fails for a reason other than the user closing it. A closed sheet (`AbortError`) does nothing. It reuses `CopyButton`'s status: "Copied" or "Copy failed" is announced through a visually hidden `role="status"` region, and the button keeps its name.
|
|
928
|
+
|
|
929
|
+
```tsx
|
|
930
|
+
import { ShareButton } from '@nxgt/material/components';
|
|
931
|
+
|
|
932
|
+
<ShareButton title="Join the room" url="https://app.example.com/rooms/42" />;
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
| Prop | Type | Default | Effect |
|
|
936
|
+
| --- | --- | --- | --- |
|
|
937
|
+
| `url`, `text`, `title` | `string` | — | what is shared; the copy fallback puts the `url`, else the `text`, else the `title` on the clipboard |
|
|
938
|
+
| `label` | `string` | `"Share"` (translated, `material.share.share`) | the accessible name; it never changes after a click |
|
|
939
|
+
| `copiedLabel`, `failedLabel` | `string` | `material.copy.copied` / `.failed` | announced after the fallback copy |
|
|
940
|
+
| `icon` | `ReactNode` | `Share2` | the idle icon |
|
|
941
|
+
| `onShare` | `(outcome) => void` | — | `'shared'`, `'cancelled'`, `'copied'` or `'failed'` |
|
|
942
|
+
|
|
943
|
+
Other props pass through to `IconButton` (`variant` defaults to `ghost`, `color` to `default`). The types `ShareData` (`{ title?, text?, url? }`) and `ShareOutcome` (`'shared' | 'cancelled' | 'copied' | 'failed'`) are exported.
|
|
944
|
+
|
|
945
|
+
`navigator` is read when the button is clicked, never while rendering, so the component is safe to server-render. The name is also exported as `SHARE_BUTTON_LABEL` for tests (`getByRole('button', { name: SHARE_BUTTON_LABEL })`).
|
|
946
|
+
|
|
947
|
+
**Storybook:** `Buttons & actions/ShareButton` · **Vue:** not yet (tracked as a `vue` issue)
|
|
948
|
+
|
|
949
|
+
---
|
|
950
|
+
|
|
922
951
|
## HighlightText
|
|
923
952
|
|
|
924
953
|

|
|
@@ -975,7 +1004,7 @@ buttons.
|
|
|
975
1004
|
|
|
976
1005
|

|
|
977
1006
|
|
|
978
|
-
The footer of a paged list: a "Showing N of M" line (a polite live region), the load button and, when the next page failed, an error alert whose "Try again" calls `onLoad`. The rows already shown stay on screen. It renders nothing when there is no next page. When the last page arrives it keeps the status line (so the final "Showing 85 of 85" is announced) and, if the button had the focus as it went away, takes the focus itself (the footer is `tabIndex={-1}`); a list that never had a next page renders nothing, and a list that restarts smaller (a new filter, `shown` or `total` dropping) drops the line. That is a heuristic: a restart at the same size or larger (a new query whose first page ends the list, `total` unknown) cannot be told from a real end, and `shown === 0` (a list being rebuilt) is ignored. Give a standalone footer a `key` that names the query (the search, filters and sort, not the page), `<LoadMoreFooter key={queryIdentity} … />`, so a new query starts it afresh; `ServerTable` does it for you.
|
|
1007
|
+
The footer of a paged list: a "Showing N of M" line (a polite live region), the load button and, when the next page failed, an error alert whose "Try again" calls `onLoad`. The rows already shown stay on screen. It renders nothing when there is no next page. When the last page arrives it keeps the status line (so the final "Showing 85 of 85" is announced) and, if the button had the focus as it went away, takes the focus itself (the footer is `tabIndex={-1}`); a list that never had a next page renders nothing, and a list that restarts smaller (a new filter, `shown` or `total` dropping) drops the line. That is a heuristic: a restart at the same size or larger (a new query whose first page ends the list, `total` unknown) cannot be told from a real end, and `shown === 0` (a list being rebuilt) is ignored. Give a standalone footer a `key` that names the query (the search, filters and sort, not the page), `<LoadMoreFooter key={queryIdentity} … />`, so a new query starts it afresh; `ServerTable` does it for you. `ServerTable` also takes no `totalCount` (an API with no total): its `footer="load-more"` then reads "20 shown" while a next page exists, never a made-up "N of N" ([ServerTable, load-more](./data-table-filter-and-lists.md#servertable)).
|
|
979
1008
|
|
|
980
1009
|
```tsx
|
|
981
1010
|
import { LoadMoreFooter } from '@nxgt/material/components';
|
|
@@ -16,7 +16,7 @@ from `@nxgt/material/components`.
|
|
|
16
16
|
| <img src="../previews/data-display-dayseparator.webp" width="96" alt=""> | [DaySeparator](#dayseparator) | "Today", "Yesterday" or the date between two hairlines |
|
|
17
17
|
| <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
18
|
| <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 |
|
|
19
|
-
| <img src="../previews/data-display-statcard.webp" width="96" alt=""> | [StatCard](#statcard) | KPI tile with value, hint, icon and toned delta |
|
|
19
|
+
| <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 |
|
|
20
20
|
| <img src="../previews/data-display-metric-cards.webp" width="96" alt=""> | [Metric cards](#metric-cards) | Sixteen dashboard tiles |
|
|
21
21
|
| <img src="../previews/data-display-card-presets.webp" width="96" alt=""> | [Card presets](#card-presets) | Twenty-seven ready-made cards |
|
|
22
22
|
| <img src="../previews/data-display-statusindicator.webp" width="96" alt=""> | [StatusIndicator](#statusindicator) | Coloured status dot with a label |
|
|
@@ -591,11 +591,24 @@ import { StatCard } from '@nxgt/material/components';
|
|
|
591
591
|
| `variant` | `'outlined' \| 'tonal' \| 'filled'` | `'outlined'` | `tonal` paints the card with the tone's container and a fine outline in its text colour (3:1 against the page); `filled` paints it with the tone and draws everything on it in the on-colour. Both use `neutral` when there is no `tone`. Every text is 4.5:1 or more. In a [calm zone](../guides/theming.md#calm-zone), a `tonal` or `filled` card is the card's own surface with the tone's ring, and its icon chip a ring |
|
|
592
592
|
| `valueSize` | `'default' \| 'display'` | `'default'` | `display` draws the value larger (`headline-large`) |
|
|
593
593
|
| `size` | `'default' \| 'sm'` | `'default'` | `sm` is a compact tile for a band or a header (see below); it ignores `hint`, `delta` and `valueSize` |
|
|
594
|
+
| `href` | `string` | — | the whole card is one link: an `<a>` around the card (one tab stop), named by its label and value, with a trailing chevron (so it reads as a link without hover), a hover border and a focus-visible ring. Without `href` the card is a plain `div`. An external address (`https://`, `mailto:`) is a plain `<a target="_blank" rel="noreferrer">`. `className` and the other props still go on the card |
|
|
595
|
+
| `linkComponent` | `LinkComponent` | a plain `<a>`, or the [`LinkComponentProvider`](./primitives.md#linkcomponentprovider)'s | with `href`: renders the link with your router's link, given `href`. It must spread every prop it receives (`className`, handlers, `data-*`) and forward `ref` |
|
|
594
596
|
|
|
595
597
|
```tsx
|
|
596
598
|
<StatCard tone="success" variant="tonal" valueSize="display" label="Revenue" value="€48,200" delta={12.4} icon={<CreditCard />} />
|
|
597
599
|
```
|
|
598
600
|
|
|
601
|
+
**A tile that leads somewhere.** `href` makes the whole card one link, so a count per category can open its list; the focus ring, hover border and chevron are the card's own, with nothing to restyle.
|
|
602
|
+
|
|
603
|
+
```tsx
|
|
604
|
+
<StatCard href="/revenue" label="Revenue" value="€48,200" />
|
|
605
|
+
|
|
606
|
+
// With your router: wrap its Link so it takes `href`, once for every link in the app
|
|
607
|
+
// with <LinkComponentProvider component={MyLink}>, or for this card only:
|
|
608
|
+
const MyLink = ({ href, ...props }: LinkComponentProps) => <Link to={href} {...props} />;
|
|
609
|
+
<StatCard linkComponent={MyLink} href="/items?state=a" label="Items" value={12} />
|
|
610
|
+
```
|
|
611
|
+
|
|
599
612
|
**Compact tile (`size="sm"`).** For figures in a header band: a tile 40 to 56 px tall that hugs its content, the optional icon chip, then the value (tabular figures) over its label. It takes the same `tone` and `variant` (`tonal`, `outlined`, `filled`); `hint`, `delta` and `valueSize` are ignored (nothing is drawn for them, and `loading` shows a skeleton in place of the value only). Tiles in a `flex flex-wrap gap-2` row sit side by side and wrap on a phone; for an even layout use `StatCardGroup`.
|
|
600
613
|
|
|
601
614
|
```tsx
|
|
@@ -622,7 +622,12 @@ export default function Students() {
|
|
|
622
622
|
announces "41–60 of 133" when the page changes. With neither `offset` nor
|
|
623
623
|
`totalCount` the region is empty, and is kept in the DOM so a later change
|
|
624
624
|
is still announced; on an empty page too, which never reads "0 rows" (see
|
|
625
|
-
[an empty server page](#an-empty-server-page)).
|
|
625
|
+
[an empty server page](#an-empty-server-page)). `ServerTable` with no
|
|
626
|
+
`totalCount` (a cursor feed) passes no `offset` and no `totalCount`: the
|
|
627
|
+
footer is Previous / Next from `pageInfo` and the cursor trail, and its live
|
|
628
|
+
region says the rows on the page ("20 shown"); a later page that comes back
|
|
629
|
+
empty is not announced as "0 results", since only the first page can say
|
|
630
|
+
there are none. The table already has a visually hidden status for
|
|
626
631
|
"Loading data…" (a `<p role="status" aria-live="polite" aria-atomic="true">`,
|
|
627
632
|
`data-slot="data-table-status"`; the page may have a toast region too), so a test finds the
|
|
628
633
|
range with `getByRole('status', { name: 'Rows shown' })`, not a bare
|
|
@@ -706,6 +711,8 @@ For the TanStack API itself (column helpers, features, row models), see
|
|
|
706
711
|
|
|
707
712
|
One server page of a list in a `manual` `DataTable`, its state in the URL's search params and no router imported: a top band (the title, the [filter bar](#filter-bar) and its active filters in one `search` landmark, the page's actions), the sort, forward cursor paging (`after` plus a `trail`), a selection that follows the user across pages but resets on a new query, the empty page, and the count announced once a new query is answered.
|
|
708
713
|
|
|
714
|
+
`totalCount` is optional: an API that gives no total (an audit log, an event feed) leaves it out, and the paged footer is then Previous / Next from `pageInfo` and the cursor trail, with no "x of y" (the same footer as [PageFooter](#pagefooter) without a total); the live region says "N shown", and a later page that comes back empty is not announced as "0 results".
|
|
715
|
+
|
|
709
716
|
The `filter` prop is the list's [`Filter`](#filter-bar) (over
|
|
710
717
|
`@nxgt/material-core/filter`): `ServerTable` renders it in the band,
|
|
711
718
|
reads its query from the params and writes it back on the first page. The
|
|
@@ -1017,7 +1024,7 @@ Its props are `ServerTableProps<T>`; `filter` is a `ServerTableFilter`,
|
|
|
1017
1024
|
| --- | --- | --- | --- |
|
|
1018
1025
|
| `params` | `URLSearchParams` | — | the URL's search params |
|
|
1019
1026
|
| `onParamsChange` | `(next: URLSearchParams) => void` | — | the router's setter |
|
|
1020
|
-
| `rows`, `totalCount`, `pageInfo` | the connection's page | — | `pageInfo: { endCursor?, hasNextPage }`; `rows` stable per answer |
|
|
1027
|
+
| `rows`, `totalCount`, `pageInfo` | the connection's page | — | `pageInfo: { endCursor?, hasNextPage }`; `rows` stable per answer. `totalCount` is optional: leave it out when the API gives no total (an audit log, an event feed); the paging footer is then Previous / Next from `pageInfo` and the cursor trail, with no "x of y", and the live region says the rows on the page ("N shown") |
|
|
1021
1028
|
| `pageSize` | `number` | — | the footer's range ("21–40 of 45") |
|
|
1022
1029
|
| `getRowId` | `(row) => string` | — | the selection's key |
|
|
1023
1030
|
| `filter` | `ServerTableFilter` | — | `{ model, locked?, fieldProps?, variant?, sheetTrigger?, quick?, density?, shortcuts?, previewCount?, counts?, liveDelayMs?, label?, searchLabel?, searchPlaceholder?, searchApply?, searchMinLength?, codec?, defaults?, errors? }` |
|
|
@@ -1899,6 +1906,10 @@ if the button had the focus as it went away, the footer takes it
|
|
|
1899
1906
|
(`tabIndex={-1}`). `ServerTable` restarts the footer by itself whenever `params` change (the search, filters and sort, not a page), so a new query never keeps the old list's last line.
|
|
1900
1907
|
`footer="load-more"` without `onLoadMore` warns in development.
|
|
1901
1908
|
|
|
1909
|
+
A list whose API returns no total leaves `totalCount` out: while a next page
|
|
1910
|
+
exists the footer says "N shown" (never a made-up "N of N"), and after the
|
|
1911
|
+
last page it keeps that line with the final count.
|
|
1912
|
+
|
|
1902
1913
|
#### An inline chip group
|
|
1903
1914
|
|
|
1904
1915
|
A `chips-single` or `chips-multiple` field declared `inline: true` shows its
|
|
@@ -1913,7 +1924,9 @@ way to narrow a list (the topics of a catalogue).
|
|
|
1913
1924
|
- **Pressing** a chip sets the condition in one query change (back on the
|
|
1914
1925
|
first page); pressing the chosen chip of a single group clears it. A
|
|
1915
1926
|
multiple group toggles each chip. The group is a `radiogroup` (or a `group`
|
|
1916
|
-
of checkboxes) named by the field's label
|
|
1927
|
+
of checkboxes) named by the field's label, and described (`aria-describedby`,
|
|
1928
|
+
visually hidden text, `material.filter.inline-clear-hint`) as "Pressing a
|
|
1929
|
+
selected chip removes it from the filter", since there is no "All" chip.
|
|
1917
1930
|
- **A plain filter.** It is counted, in the URL like any field, and cleared by
|
|
1918
1931
|
"Clear all". It has no chip of its own (the group shows it) and is not in
|
|
1919
1932
|
"Add filter" nor among the quick pills; the phone sheet still lists it.
|
|
@@ -463,6 +463,14 @@ export function Quantity() {
|
|
|
463
463
|
| `onBlur` | `FocusEventHandler` | — | after the typed text is read and `onChange` called |
|
|
464
464
|
| `ref` | `Ref<HTMLInputElement>` | — | reaches the text input (React Hook Form's `field.ref`) |
|
|
465
465
|
|
|
466
|
+
**Stepper names.** Each +/− button is named after its field: "Increase
|
|
467
|
+
Quantity" / "Decrease Quantity" (`material.number-field.increase-named` and
|
|
468
|
+
`decrease-named`, with the `label` interpolated), so a form with several number
|
|
469
|
+
fields never gives a screen reader identical buttons. The name comes from a
|
|
470
|
+
plain-text `label`, else from `aria-label`; with neither (a field named only
|
|
471
|
+
through `aria-labelledby` included), the buttons keep the bare "Increase" /
|
|
472
|
+
"Decrease".
|
|
473
|
+
|
|
466
474
|
**Decimal separator.** After blur (and after +/−) the value is shown with the
|
|
467
475
|
active language's separator, from `I18nProvider`: `1,5` in French, `1.5` in
|
|
468
476
|
English (no grouping, so it stays editable). Typing accepts both: `1,5` and
|
|
@@ -476,7 +484,7 @@ Form's `field.ref` the input itself: `setFocus('amount')` and
|
|
|
476
484
|
**Emits:** `number | null`.
|
|
477
485
|
**Form wrapper:** `NumberFormField` — `control` and `name`, see
|
|
478
486
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
479
|
-
**Storybook:** `Form fields/NumberField` · **Vue:** `NumberField`, `NumberFormField` (the language separator is React only for now, #763)
|
|
487
|
+
**Storybook:** `Form fields/NumberField` · **Vue:** `NumberField`, `NumberFormField` (the language separator is React only for now, #763; the stepper names, #2060)
|
|
480
488
|
|
|
481
489
|
---
|
|
482
490
|
|
|
@@ -373,6 +373,7 @@ the page. It takes a `div`'s props.
|
|
|
373
373
|
the room the others leave, which keep their size. Give it the one filled
|
|
374
374
|
`Button`; a second filled `Button` among the children warns once in
|
|
375
375
|
development, make the others `outlined` or `tonal`.
|
|
376
|
+
- **Surface.** Below `md` the bar paints `--background` (it is stuck over the content); from `md`, and with `sticky={false}`, it paints nothing and takes its parent's surface, such as a `Card`.
|
|
376
377
|
- **Safe areas.** Under `viewport-fit=cover` the bar pads clear of
|
|
377
378
|
`env(safe-area-inset-left | right | bottom)`, as classes.
|
|
378
379
|
- **Keyboard.** From the visual viewport (`visualViewport`), the bar rises by
|
|
@@ -460,12 +461,15 @@ import { ListDetailsLayout, PaneLayout, ThreadLayout } from '@nxgt/material/comp
|
|
|
460
461
|
</ThreadLayout>
|
|
461
462
|
```
|
|
462
463
|
|
|
463
|
-
|
|
464
|
+
At 648 px and narrower `ListDetailsLayout` shows one view at a time and
|
|
464
465
|
`PaneLayout` moves its pane into a sheet; the `…Trigger` parts switch view or
|
|
465
|
-
open the pane (`ListDetailsTrigger` takes the `view` to show), and their
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
466
|
+
open the pane (`ListDetailsTrigger` takes the `view` to show), and their
|
|
467
|
+
`children` / `className` can be functions of the layout state. A server, which
|
|
468
|
+
has no screen width, renders the wide layout, as does a hydrating client; a
|
|
469
|
+
phone switches right after hydrating. `PaneLayout`'s inline pane is hidden by
|
|
470
|
+
CSS at 648 px and narrower, so a phone never sees it flash before the sheet
|
|
471
|
+
takes over. A page rendered only in the browser starts on the real layout.
|
|
472
|
+
`ThreadLayout` is a centred column over the app
|
|
469
473
|
[background](#background) on the page ground (`bg-ground`, so a `bg-card` card stands apart) for focused flows (sign-in, onboarding), with no
|
|
470
474
|
app bar.
|
|
471
475
|
|
|
@@ -24,6 +24,8 @@ other pages are built from these.
|
|
|
24
24
|
| <img src="../previews/primitives-scrollarea.webp" width="96" alt=""> | [ScrollArea](#scrollarea) | Scroll container with a thin themed scrollbar |
|
|
25
25
|
| <img src="../previews/primitives-table.webp" width="96" alt=""> | [Table](#table) | Styled semantic table inside a scrollable wrapper |
|
|
26
26
|
| <img src="../previews/primitives-tabs.webp" width="96" alt=""> | [Tabs](#tabs) | Tabs whose triggers can take an icon |
|
|
27
|
+
| <img src="../previews/primitives-tabsnav.webp" width="96" alt=""> | [TabsNav](#tabsnav) | Route tabs as links, with `aria-current` |
|
|
28
|
+
| <img src="../previews/primitives-segmentednav.webp" width="96" alt=""> | [SegmentedNav](#segmentednav) | Segmented links, with `aria-current` and the pill |
|
|
27
29
|
| <img src="../previews/primitives-toggle.webp" width="96" alt=""> | [Toggle](#toggle) | Pressable on / off button |
|
|
28
30
|
| <img src="../previews/primitives-togglegroup.webp" width="96" alt=""> | [ToggleGroup](#togglegroup) | Connected, segmented or separate toggle group |
|
|
29
31
|
| | [Activity](#activity) | React `<Activity>` switched by a boolean |
|
|
@@ -784,6 +786,8 @@ indicator is placed inside it, measured against it, so the child must render
|
|
|
784
786
|
its children directly: a component child that wraps them in an inner element
|
|
785
787
|
hides the indicator.
|
|
786
788
|
|
|
789
|
+
For tabs that go to routes, use [TabsNav](#tabsnav): real links with `aria-current`, not tabs with panels.
|
|
790
|
+
|
|
787
791
|
`TabsTrigger asChild` makes your link the tab (`<TabsTrigger value="a" asChild><Link to="/a">A</Link></TabsTrigger>`): the indicator and the icon and label spans go inside it. `ToggleGroupItem`, `AccordionTrigger`, `SelectTrigger` and `ExtendedLabel` take `asChild` the same way.
|
|
788
792
|
|
|
789
793
|
`dir="rtl"` on `Tabs` (or Radix's `DirectionProvider`) runs the tabs from the
|
|
@@ -822,6 +826,211 @@ as `segmented`. Both still type-check and render; write `underline` or
|
|
|
822
826
|
|
|
823
827
|
---
|
|
824
828
|
|
|
829
|
+
## TabsNav
|
|
830
|
+
|
|
831
|
+

|
|
832
|
+
|
|
833
|
+
Route tabs as links: a `nav` of links in the `Tabs` look, with `aria-current="page"` on the current one, the sliding indicator and the phone scroll cue.
|
|
834
|
+
|
|
835
|
+
```tsx
|
|
836
|
+
import { TabsNav, TabsNavLink } from '@nxgt/material/components';
|
|
837
|
+
|
|
838
|
+
<TabsNav aria-label="Account sections" value="billing">
|
|
839
|
+
<TabsNavLink to="/profile" value="profile">
|
|
840
|
+
Profile
|
|
841
|
+
</TabsNavLink>
|
|
842
|
+
<TabsNavLink to="/billing" value="billing">
|
|
843
|
+
Billing
|
|
844
|
+
</TabsNavLink>
|
|
845
|
+
</TabsNav>;
|
|
846
|
+
```
|
|
847
|
+
|
|
848
|
+
Use `Tabs` when the panels are on the page and `TabsNav` when each tab is a
|
|
849
|
+
place to go: every tab is a real link, so it opens in a new window and its
|
|
850
|
+
address can be copied. `aria-label` names the navigation and is required.
|
|
851
|
+
|
|
852
|
+
- **Current link**: `TabsNav`'s `value` is matched against each link's
|
|
853
|
+
`value`; a link's `current` boolean overrides it (`current={false}` to
|
|
854
|
+
switch one off). The current link carries `aria-current="page"` and
|
|
855
|
+
`data-state="active"`, which is what the indicator follows.
|
|
856
|
+
- **Keyboard**: the links are plain links, so Tab moves from one to the next;
|
|
857
|
+
the arrow keys do nothing (they are not tabs, there is no `tablist`).
|
|
858
|
+
- **Looks**: `variant="underline"` (the default) or `"segmented"`, as `Tabs`.
|
|
859
|
+
A nav wider than its container scrolls inside itself, with the same edge
|
|
860
|
+
fades and scroll buttons as `Tabs`. On a coarse pointer each link is 44px
|
|
861
|
+
tall at least.
|
|
862
|
+
- **The link**: `TabsNavLink` takes `LinkButton`'s props (`to`, `replace`,
|
|
863
|
+
`state`, `target`, `reloadDocument`, `download`, `linkComponent`) and an
|
|
864
|
+
`icon`. It renders React Router's `Link`, or the `LinkComponentProvider`'s
|
|
865
|
+
component (given `href`). `preventScrollReset` is on by default, so changing
|
|
866
|
+
tab keeps the scroll position; pass `preventScrollReset={false}` to reset.
|
|
867
|
+
- A tab bound to the URL's search params: [`useTabParam`](#route-tabs-in-the-url-usetabparam).
|
|
868
|
+
|
|
869
|
+
**It needs a Router.** Without a `LinkComponentProvider`, `TabsNavLink` renders
|
|
870
|
+
React Router's `Link`, as `LinkButton` does, so it throws outside a Router
|
|
871
|
+
(`useHref() may be used only in the context of a <Router> component`): render
|
|
872
|
+
it inside your router, or set a [`LinkComponentProvider`](#linkcomponentprovider).
|
|
873
|
+
See [the troubleshooting entry](../troubleshooting.md#usehref-may-be-used-only-in-the-context-of-a-router-component-on-a-tabsnav-segmentednav-or-a-linkbutton).
|
|
874
|
+
|
|
875
|
+
| Prop | On | Type | Default | Effect |
|
|
876
|
+
| --- | --- | --- | --- | --- |
|
|
877
|
+
| `aria-label` | `TabsNav` | `string` | — (required) | names the navigation |
|
|
878
|
+
| `value` | `TabsNav` | `string` | — | the current link's `value` |
|
|
879
|
+
| `variant` | `TabsNav` | `'underline' \| 'segmented'` | `'underline'` | the look, as `Tabs` |
|
|
880
|
+
| `to` | `TabsNavLink` | `To` | — (required) | the route, as for `LinkButton` |
|
|
881
|
+
| `value` | `TabsNavLink` | `string` | — | matched against the nav's `value` |
|
|
882
|
+
| `current` | `TabsNavLink` | `boolean` | — | marks the link current or not, over `value` |
|
|
883
|
+
| `icon` | `TabsNavLink` | `ReactNode` | — | shown before the label |
|
|
884
|
+
| `preventScrollReset` | `TabsNavLink` | `boolean` | `true` | keeps the scroll position when the tab changes |
|
|
885
|
+
|
|
886
|
+
### Route tabs in the URL: `useTabParam`
|
|
887
|
+
|
|
888
|
+
`useTabParam({ param = 'tab', values, defaultValue?, explicitDefault?, keepHash? })` keeps
|
|
889
|
+
the current tab in a search param and feeds `TabsNav` or `SegmentedNav`. It
|
|
890
|
+
returns `value` (the param if it is one of `values`, else `defaultValue`, else
|
|
891
|
+
the first value: an unknown value falls back) and `hrefFor(tab)`, the `to` of a
|
|
892
|
+
tab. `hrefFor` keeps the path and the other params; the default tab writes
|
|
893
|
+
**no** param. It **drops the hash**: React Router's `ScrollRestoration` jumps
|
|
894
|
+
to a hash even with `preventScrollReset`, so a kept `#section` would scroll the
|
|
895
|
+
page on every tab click. Pass `keepHash: true` when you want it kept.
|
|
896
|
+
Each tab is a navigation, so Back walks the tabs and a
|
|
897
|
+
router blocker sees them; `preventScrollReset` (on by default) keeps the scroll.
|
|
898
|
+
|
|
899
|
+
`skipTabChange(param = 'tab')` is for the route's `shouldRevalidate`: it returns
|
|
900
|
+
`false` when a navigation changed only that param, so the loader is not read
|
|
901
|
+
again for a tab switch. A same-URL, fetcher or manual revalidation, and any
|
|
902
|
+
submission, still return `defaultShouldRevalidate`. Parent routes' loaders
|
|
903
|
+
still revalidate unless they export `skipTabChange` too. It is typed as a
|
|
904
|
+
`ShouldRevalidateFunction`.
|
|
905
|
+
|
|
906
|
+
`tabIds(value, prefix = 'tab')` returns the stable ids
|
|
907
|
+
`{ tab: '<prefix>-<value>', panel: '<prefix>-panel-<value>' }` for `id` and
|
|
908
|
+
`aria-labelledby`. The value must be id-safe (no spaces); give each tab set on a
|
|
909
|
+
page its own `prefix` to keep them apart.
|
|
910
|
+
|
|
911
|
+
`values` is a non-empty tuple read as literal types (`const` generic): `value`
|
|
912
|
+
and `hrefFor`'s argument are the union of its entries, and a misspelled tab or a
|
|
913
|
+
`defaultValue` outside `values` is a compile error.
|
|
914
|
+
|
|
915
|
+
**A loader must not read the tab param**: a skipped revalidation leaves its
|
|
916
|
+
data as it was, so anything derived from `?tab=` would go stale. Read the tab
|
|
917
|
+
in the component (`useTabParam`) instead.
|
|
918
|
+
|
|
919
|
+
```tsx
|
|
920
|
+
import { type LoaderFunctionArgs, useLoaderData } from 'react-router';
|
|
921
|
+
import { skipTabChange, TabsNav, TabsNavLink, tabIds, useTabParam } from '@nxgt/material/components';
|
|
922
|
+
|
|
923
|
+
const TABS = ['overview', 'history', 'files'] as const;
|
|
924
|
+
|
|
925
|
+
export async function loader({ params }: LoaderFunctionArgs) {
|
|
926
|
+
return getRecord(params.id); // does not read `?tab=`
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// route module: a tab switch does not reload the record
|
|
930
|
+
export const shouldRevalidate = skipTabChange();
|
|
931
|
+
|
|
932
|
+
export default function Record() {
|
|
933
|
+
const record = useLoaderData<typeof loader>();
|
|
934
|
+
// `explicitDefault`: another param implies a tab, so spell the default out
|
|
935
|
+
const { value, hrefFor } = useTabParam({ values: TABS, explicitDefault: !!record.from });
|
|
936
|
+
return (
|
|
937
|
+
<>
|
|
938
|
+
<TabsNav aria-label="Record sections" value={value}>
|
|
939
|
+
{TABS.map((tab) => (
|
|
940
|
+
<TabsNavLink key={tab} id={tabIds(tab).tab} to={hrefFor(tab)} value={tab}>
|
|
941
|
+
{tab}
|
|
942
|
+
</TabsNavLink>
|
|
943
|
+
))}
|
|
944
|
+
</TabsNav>
|
|
945
|
+
<section id={tabIds(value).panel} aria-labelledby={tabIds(value).tab}>
|
|
946
|
+
{/* mount only the shown panel */}
|
|
947
|
+
</section>
|
|
948
|
+
</>
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
| Option | Default | Effect |
|
|
954
|
+
| --- | --- | --- |
|
|
955
|
+
| `param` | `'tab'` | the search param |
|
|
956
|
+
| `values` | — (required) | the tabs a URL may name, in order |
|
|
957
|
+
| `defaultValue` | the first value | the tab shown without a param; writes no param |
|
|
958
|
+
| `explicitDefault` | `false` | writes the default tab's param out too (another param implies a tab) |
|
|
959
|
+
| `keepHash` | `false` | keeps the location's hash in `hrefFor` (it scrolls to it, see above) |
|
|
960
|
+
|
|
961
|
+
**Storybook:** `Primitives/TabsNav` (story `TabParam` for `useTabParam`) · **Vue:** none yet (#2055, #2065)
|
|
962
|
+
|
|
963
|
+
---
|
|
964
|
+
|
|
965
|
+
## SegmentedNav
|
|
966
|
+
|
|
967
|
+

|
|
968
|
+
|
|
969
|
+
The segmented `ToggleGroup` look as a `nav` of links: a period or a view kept in the address, with `aria-current="page"` and the selected pill on the current one.
|
|
970
|
+
|
|
971
|
+
```tsx
|
|
972
|
+
import { SegmentedNav, SegmentedNavItem, useTabParam } from '@nxgt/material/components';
|
|
973
|
+
|
|
974
|
+
const PERIODS = ['day', 'week'] as const;
|
|
975
|
+
|
|
976
|
+
function Period() {
|
|
977
|
+
const { value, hrefFor } = useTabParam({ param: 'p', values: PERIODS });
|
|
978
|
+
return (
|
|
979
|
+
<SegmentedNav aria-label="Period" value={value}>
|
|
980
|
+
<SegmentedNavItem to={hrefFor('day')} value="day">
|
|
981
|
+
Day
|
|
982
|
+
</SegmentedNavItem>
|
|
983
|
+
<SegmentedNavItem to={hrefFor('week')} value="week">
|
|
984
|
+
Week
|
|
985
|
+
</SegmentedNavItem>
|
|
986
|
+
</SegmentedNav>
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
```
|
|
990
|
+
|
|
991
|
+
Use `ToggleGroup` for a choice that stays on the page and `SegmentedNav` when
|
|
992
|
+
each segment is a place to go: every item is a real link, so it opens in a
|
|
993
|
+
new window and its address can be copied. A `ToggleGroup` of links would
|
|
994
|
+
announce each as a radio. `aria-label` names the navigation and is required.
|
|
995
|
+
|
|
996
|
+
- **Current item**: `SegmentedNav`'s `value` is matched against each item's
|
|
997
|
+
`value`; an item's `current` boolean overrides it (`current={false}` to
|
|
998
|
+
switch one off). The current item carries `aria-current="page"` and
|
|
999
|
+
`data-state="active"`, and draws the selected pill (its label in
|
|
1000
|
+
`primary-text`); it does not glide, since the nav may scroll.
|
|
1001
|
+
- **Keyboard**: the items are plain links, so Tab moves from one to the next;
|
|
1002
|
+
the arrow keys do nothing (there is no radio group).
|
|
1003
|
+
- **On a phone**: a nav wider than its container scrolls inside itself (the
|
|
1004
|
+
page does not), with a fade on the side with hidden items, and the current
|
|
1005
|
+
item is scrolled into view. On a coarse pointer each item is 44px tall at
|
|
1006
|
+
least.
|
|
1007
|
+
- **The link**: `SegmentedNavItem` takes `LinkButton`'s props (`to`, `replace`,
|
|
1008
|
+
`state`, `target`, `reloadDocument`, `download`, `linkComponent`). It renders
|
|
1009
|
+
React Router's `Link`, or the `LinkComponentProvider`'s component (given
|
|
1010
|
+
`href`). `preventScrollReset` is on by default, so changing segment keeps
|
|
1011
|
+
the scroll position; pass `preventScrollReset={false}` to reset.
|
|
1012
|
+
- A segment bound to the URL's search params: [`useTabParam`](#route-tabs-in-the-url-usetabparam) (`useTabParam({ param: 'p', values })`), as above.
|
|
1013
|
+
|
|
1014
|
+
**It needs a Router.** Without a `LinkComponentProvider`, `SegmentedNavItem`
|
|
1015
|
+
renders React Router's `Link`, as `LinkButton` does, so it throws outside a
|
|
1016
|
+
Router (`useHref() may be used only in the context of a <Router> component`):
|
|
1017
|
+
render it inside your router, or set a [`LinkComponentProvider`](#linkcomponentprovider).
|
|
1018
|
+
See [the troubleshooting entry](../troubleshooting.md#usehref-may-be-used-only-in-the-context-of-a-router-component-on-a-tabsnav-segmentednav-or-a-linkbutton).
|
|
1019
|
+
|
|
1020
|
+
| Prop | On | Type | Default | Effect |
|
|
1021
|
+
| --- | --- | --- | --- | --- |
|
|
1022
|
+
| `aria-label` | `SegmentedNav` | `string` | — (required) | names the navigation |
|
|
1023
|
+
| `value` | `SegmentedNav` | `string` | — | the current item's `value` |
|
|
1024
|
+
| `size` | `SegmentedNav` | `'sm' \| 'md' \| 'lg'` | `'md'` | 32, 36 or 44 px tall, as `ToggleGroup` |
|
|
1025
|
+
| `to` | `SegmentedNavItem` | `To` | — (required) | the route, as for `LinkButton` |
|
|
1026
|
+
| `value` | `SegmentedNavItem` | `string` | — | matched against the nav's `value` |
|
|
1027
|
+
| `current` | `SegmentedNavItem` | `boolean` | — | marks the item current or not, over `value` |
|
|
1028
|
+
| `preventScrollReset` | `SegmentedNavItem` | `boolean` | `true` | keeps the scroll position when the segment changes |
|
|
1029
|
+
|
|
1030
|
+
**Storybook:** `Primitives/SegmentedNav` · **Vue:** none yet (#2059)
|
|
1031
|
+
|
|
1032
|
+
---
|
|
1033
|
+
|
|
825
1034
|
## Toggle
|
|
826
1035
|
|
|
827
1036
|

|
|
@@ -2207,6 +2207,7 @@ function Config() {
|
|
|
2207
2207
|
| `height` | `string \| number` | `300` | editor height |
|
|
2208
2208
|
| `readOnly` | `boolean` | `false` | a reader: the code can be selected, searched and copied, not changed; no caret and no current line, a quiet gutter, a Read-only badge and the line count in the status bar, Copy filled and labelled, no Format or language menu. Not dimmed (unlike `disabled`) |
|
|
2209
2209
|
| `disabled` | `boolean` | — | dimmed, read-only, its tools off |
|
|
2210
|
+
| `aria-describedby` | `string` | — | the id(s) of what describes the code (a question's description, `QuestionCodeField.describedBy`); set on Monaco's input before the `helperText` id, each id once |
|
|
2210
2211
|
| `lineNumbers` | `boolean` | `true` | the line numbers in the gutter |
|
|
2211
2212
|
| `minimap` | `boolean` | `false` | Monaco's minimap |
|
|
2212
2213
|
| `wordWrap` / `defaultWordWrap` / `onWordWrapChange` | `boolean` | `false` | wraps long lines; the pill's Word wrap toggles it (controlled when `wordWrap` is given) |
|
package/docs/guides/charts.md
CHANGED
|
@@ -241,7 +241,7 @@ const toneOf = (point: ChartPoint) =>
|
|
|
241
241
|
| Prop | Effect |
|
|
242
242
|
| --- | --- |
|
|
243
243
|
| `bands` | `{ from, to, label, tone? }[]`: a wash across the plot behind the lines, with its `label` inside it at the left. `from` and `to` may come in either order, and are a number, a row field or a function of the row (a stepped band; typed `ChartRowValue`). |
|
|
244
|
-
| `thresholds` | `{ value, label, tone?, dash? }[]` (`value`: a number, a row field or a function of the row): a horizontal line behind the series with its `label` above it at the right end; dashed unless `dash: false`. |
|
|
244
|
+
| `thresholds` | `{ value, label, tone?, dash? }[]` (`value`: a number, a row field or a function of the row): a horizontal line behind the series with its `label` above it at the right end (under it when a point of a series sits where the label would be, so it never covers a point or its marker); dashed unless `dash: false`. |
|
|
245
245
|
| `pointTone` | `(point: ChartPoint, series: ChartSeries) => Tone \| undefined`: the tone of one value. It is given the point `onPointClick` gets, so `point.index` is the row of the full `data` even when the chart is zoomed. |
|
|
246
246
|
| `toneLabels` | `{ [tone]: word }`: the word of each tone, in your language and domain. Unset, the library's neutral word ("Error", translated by `material.charts.tone.*`); an app tone you declared falls back to its own name. |
|
|
247
247
|
|
|
@@ -24,12 +24,26 @@ screen ("Open in editor"); `fullscreen` and `onFullscreenChange` let your
|
|
|
24
24
|
own button do the same
|
|
25
25
|
([CodeEditor](../components/rich-widgets.md#codeeditor)).
|
|
26
26
|
|
|
27
|
+
- [Describing the code](#describing-the-code)
|
|
27
28
|
- [Client-only](#client-only)
|
|
28
29
|
- [Themes](#themes)
|
|
29
30
|
- [Your own theme](#your-own-theme)
|
|
30
31
|
- [Self-hosting Monaco](#self-hosting-monaco)
|
|
31
32
|
- [Moving off the barrel import](#moving-off-the-barrel-import)
|
|
32
33
|
|
|
34
|
+
## Describing the code
|
|
35
|
+
|
|
36
|
+
`aria-describedby` takes the id of the element that describes the code, such
|
|
37
|
+
as the description line of a question. It goes on Monaco's input, before the
|
|
38
|
+
id of the field's own `helperText`, so a screen reader reads both.
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
<CodeEditor label="Answer" aria-describedby="question-description" />
|
|
42
|
+
|
|
43
|
+
// In a QuestionCard:
|
|
44
|
+
<QuestionCard renderCode={(field) => <CodeEditor aria-describedby={field.describedBy} />} />
|
|
45
|
+
```
|
|
46
|
+
|
|
33
47
|
## Client-only
|
|
34
48
|
|
|
35
49
|
The entry's built files carry no `'use client'` directive: import the
|
package/docs/guides/i18n.md
CHANGED
|
@@ -303,7 +303,9 @@ value to enter." (an operator such as "is empty")),
|
|
|
303
303
|
`material.filter.op.<id>.*` for each operator (`is`, `is_not`, `in`,
|
|
304
304
|
`between`, `before`, `after`, `contains`, `empty`, `gt`, `lt`, `gte`, `lte`):
|
|
305
305
|
its `label` ("is any of") and its words on a chip, `chip` and, for several
|
|
306
|
-
values, `chip-many`. "Clear all" is `material.filter.clear-all`.
|
|
306
|
+
values, `chip-many`. "Clear all" is `material.filter.clear-all`. An inline chip group
|
|
307
|
+
is described by `material.filter.inline-clear-hint` ("Pressing a selected chip
|
|
308
|
+
removes it from the filter"). The phone sheet
|
|
307
309
|
has `material.filter.sheet.*`: `title` "Filters" (its trigger and its
|
|
308
310
|
title), `show` "Show results", `show-count` (an ICU plural over `count`:
|
|
309
311
|
"No results", "Show # result", "Show # results") and `locked` "Locked
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/roadmap.md
CHANGED
|
@@ -6,6 +6,8 @@ version they shipped in, and planned entries may name the major they target.
|
|
|
6
6
|
|
|
7
7
|
## Now
|
|
8
8
|
|
|
9
|
+
- **Link navigation** — `TabsNav` and `SegmentedNav` render route tabs and segmented navigation as a `nav` of links with `aria-current="page"`, looking like Tabs and the segmented ToggleGroup, plus `useTabParam` to keep the current tab in the URL. Tracked in [#2029](https://github.com/softistx/nxgt-material/issues/2029).
|
|
10
|
+
|
|
9
11
|
- **Barcodes and label sheets** — `@nxgt/material/barcode` draws Code 128
|
|
10
12
|
and Data Matrix as accessible SVG (themed, dark-aware, black on white in
|
|
11
13
|
print) and lays labels out on a sheet to print: label size, columns, rows,
|
|
@@ -241,7 +243,6 @@ version they shipped in, and planned entries may name the major they target.
|
|
|
241
243
|
slot; the bar's `views` slot is planned with it. They are meant to replace
|
|
242
244
|
the legacy `FilterPresets`. Tracked in [#1876](https://github.com/softistx/nxgt-material/issues/1876).
|
|
243
245
|
|
|
244
|
-
- **Link navigation** — `TabsNav` and `SegmentedNav` render route tabs and segmented navigation as a `nav` of links with `aria-current="page"`, looking like Tabs and the segmented ToggleGroup, plus `useTabParam` to keep the current tab in the URL.
|
|
245
246
|
- **RecordActionsMenu** — one menu for a record's lifecycle: archive with an Undo toast (or confirm first with `archivePolicy="confirm"`), restore, and delete that always confirms and shows the backend's refusal, with `ConfirmDialog` gaining `details` and `refusal` slots.
|
|
246
247
|
- **Cursor trail cap** — `ServerTable` and `PageFooter` keep cursor URLs short: the trail is encoded once and capped by `maxTrail` (20 by default) with an exact page count, and `totalCount` becomes optional for APIs that do not count.
|
|
247
248
|
|