@nxgt/material 3.103.0 → 3.105.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 +5 -3
- package/dist/{alert-dialog-NFyAiSqc.js → alert-dialog-CK_47jfm.js} +1 -1
- package/dist/{async-slice-D35bX79S.js → async-slice-1ZNMiwyJ.js} +1 -1
- package/dist/{button-Be7GZzF9.js → button-YfEMZbRO.js} +1 -1
- package/dist/{calculator-BGy1Q7rn.js → calculator-BmYZId4c.js} +11 -11
- package/dist/calculator-field.js +6 -6
- package/dist/{calculator-segment-uixk6lBg.js → calculator-segment-DJ08DJ-n.js} +1 -1
- package/dist/calculator.js +1 -1
- package/dist/charts.js +819 -764
- package/dist/{chip-3rWebZEM.js → chip-DPj6e-3v.js} +2 -2
- package/dist/{code-editor-BqSVMfy-.js → code-editor-CZg3nLZO.js} +4 -4
- package/dist/code-editor.js +4 -4
- package/dist/{code-language-items-Cf2LJVg2.js → code-language-items-Nq1iOGId.js} +2 -2
- package/dist/components/ui/display/chip/chip-soft.css +27 -2
- package/dist/components/ui/primitives/collapsible/collapsible.css +9 -0
- package/dist/{components-D2liJ9Yu.js → components-Cn14BksP.js} +16922 -16630
- package/dist/components.js +38 -38
- package/dist/{conversion-variant-clFZ6ZMJ.js → conversion-variant-C-LPx5ek.js} +4 -4
- package/dist/{currency-field-CBFEVNM1.js → currency-field-DnsRIA80.js} +4 -4
- package/dist/{dialog-CrIIINTk.js → dialog-BKBsDGFc.js} +3 -3
- package/dist/dnd.js +1 -1
- package/dist/{dropdown-menu-BnfLWu8n.js → dropdown-menu-QMDbRidH.js} +1 -1
- package/dist/editor.js +8 -8
- package/dist/{empty-state-CuUaCPY7.js → empty-state-BLIJ6znY.js} +1 -1
- package/dist/{financial-variant-BY_VCUuk.js → financial-variant-Ws0Jp01H.js} +5 -5
- package/dist/{hooks-hHoZbL-4.js → hooks-C3vkQmzh.js} +2 -2
- package/dist/hooks.js +2 -2
- package/dist/{hover-card-Do2-uzlF.js → hover-card-Df649U6I.js} +1 -1
- package/dist/i18n.js +3 -3
- package/dist/{icon-badge-CHaVNEmb.js → icon-badge-Cma1qOOJ.js} +1 -1
- package/dist/{icon-button-BIAubLeJ.js → icon-button-CpKxV264.js} +1 -1
- package/dist/keypad.js +3 -3
- package/dist/lib/components/ui/actions/index.d.ts +1 -0
- package/dist/lib/components/ui/actions/product-tile/index.d.ts +1 -0
- package/dist/lib/components/ui/actions/product-tile/product-tile-link.d.ts +16 -0
- package/dist/lib/components/ui/actions/product-tile/product-tile.d.ts +41 -0
- package/dist/lib/components/ui/collections/data-table/data-table-density.d.ts +1 -1
- package/dist/lib/components/ui/collections/data-table/partials/data-table-scroll-region.d.ts +4 -1
- package/dist/lib/components/ui/collections/data-table/partials/layouts/data-table-list-bar.d.ts +6 -3
- package/dist/lib/components/ui/collections/data-table/partials/layouts/list-bar-in-row.d.ts +2 -1
- package/dist/lib/components/ui/collections/data-table/types.d.ts +11 -8
- package/dist/lib/components/ui/collections/data-table/virtual-option.d.ts +9 -3
- package/dist/lib/components/ui/collections/filter/use-filter-field-config.d.ts +2 -0
- package/dist/lib/components/ui/collections/server-table/partials/server-table-passed-through.d.ts +1 -0
- package/dist/lib/components/ui/collections/server-table/types.d.ts +1 -1
- package/dist/lib/components/ui/display/chip/chip.variants.d.ts +5 -4
- package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
- package/dist/lib/components/ui/fields/index.d.ts +1 -0
- package/dist/lib/components/ui/fields/quantity-field/types.d.ts +1 -1
- package/dist/lib/components/ui/fields/quantity-stepper/index.d.ts +2 -0
- package/dist/lib/components/ui/fields/quantity-stepper/quantity-stepper-model.d.ts +9 -0
- package/dist/lib/components/ui/fields/quantity-stepper/quantity-stepper-value.d.ts +15 -0
- package/dist/lib/components/ui/fields/quantity-stepper/quantity-stepper.d.ts +9 -0
- package/dist/lib/components/ui/fields/quantity-stepper/quantity-stepper.types.d.ts +35 -0
- package/dist/lib/components/ui/fields/quantity-stepper/use-quantity-stepper.d.ts +28 -0
- package/dist/lib/components/ui/layout/app-shell/app-sidebar-entries.d.ts +3 -1
- package/dist/lib/components/ui/layout/app-shell/app-sidebar-group.d.ts +8 -3
- 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 +15 -0
- package/dist/lib/components/ui/layout/pane-layout/pane-layout-phone-frames.d.ts +20 -0
- package/dist/lib/components/ui/layout/pane-layout/pane-layout.d.ts +8 -3
- package/dist/lib/components/ui/layout/pane-layout/partials/pane-layout-classes.d.ts +2 -2
- package/dist/lib/components/ui/layout/pane-layout/partials/pane-layout-context.d.ts +9 -0
- package/dist/lib/components/ui/layout/pane-layout/partials/use-pane-layout-state.d.ts +7 -2
- package/dist/lib/components/ui/layout/panel-card/panel-card.d.ts +3 -1
- package/dist/lib/components/ui/primitives/collapsible/collapsible.d.ts +9 -1
- package/dist/lib/components/ui/primitives/tabs/partials/tabs-list.d.ts +6 -1
- package/dist/lib/components/ui/primitives/tabs/tabs-classes.d.ts +1 -0
- package/dist/lib/components/ui/selection/chip-group-field/chip-group-field.d.ts +8 -0
- package/dist/lib/components/ui/selection/chip-group-field/chip-group-field.types.d.ts +6 -0
- package/dist/lib/components/ui/selection/chip-group-field/partials/chip-group-reasons.d.ts +10 -0
- package/dist/lib/components/ui/selection/chip-group-field/partials/use-chip-rail.d.ts +12 -0
- package/dist/lib/components/ui/selection/choice-group/choice-group-root.d.ts +5 -2
- package/dist/lib/components/ui/widgets/native-charts/core/label-placement.d.ts +25 -0
- package/dist/lib/components/ui/widgets/native-charts/core/reference-geometry.d.ts +3 -2
- package/dist/lib/i18n/resources/en/en.d.ts +3 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +4 -1
- package/dist/lib/i18n/resources/fr/fr.d.ts +3 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +4 -1
- package/dist/lib/i18n/resources/resources.d.ts +6 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib/print/output/csv.d.ts +37 -0
- package/dist/lib/print/output/index.d.ts +1 -0
- package/dist/lib.js +1 -1
- package/dist/main.js +54 -54
- package/dist/map.js +8 -8
- package/dist/material.css +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/{percent-field-DU8eIMD3.js → percent-field-sAq7BaxN.js} +2 -2
- package/dist/{picker-surface-XmA--JVI.js → picker-surface--6E-nvpU.js} +4 -4
- package/dist/{popover-D8iHiAbl.js → popover-bEB8I_-w.js} +1 -1
- package/dist/{pos-types-BEAY27Rm.js → pos-types-Cpdc4E33.js} +1 -1
- package/dist/{pos-variant-ClGI92Lt.js → pos-variant-ClMGu59I.js} +5 -5
- package/dist/print.js +146 -127
- package/dist/{programmer-variant-Boq2O8hB.js → programmer-variant-ChzgyA7x.js} +2 -2
- package/dist/{public-DOrXKmcL.js → public-BY8N5NDT.js} +9 -9
- package/dist/{rich-code-block-Djj2XE_u.js → rich-code-block-D2GiWuqD.js} +5 -5
- package/dist/{scientific-variant-BzF_aMQP.js → scientific-variant-31_93gcZ.js} +2 -2
- package/dist/{sortable-provider-DCZjsIIH.js → sortable-provider-DGiVn72t.js} +2 -2
- package/dist/{spinner-Cg0y_E_K.js → spinner-CEVomQ5K.js} +1 -1
- package/dist/{tabs-DpessV5s.js → tabs-B8MXBbPW.js} +14 -14
- package/dist/{text-field-Dxk9lAKM.js → text-field-DN88qD0Q.js} +1 -1
- package/dist/theme-picker.js +2 -2
- package/dist/{theme-toggle-B6Jl5Kfn.js → theme-toggle-C7pnu1Xh.js} +1 -1
- package/dist/{toolbar-items-Cs1XrZsH.js → toolbar-items-BDfoh8QB.js} +4 -4
- package/dist/{translate-material-Du0j0t97.js → translate-material-s_lxIV43.js} +1 -1
- package/dist/{translator-BVs4n0Fz.js → translator-Cgql9srO.js} +4 -2
- package/dist/{use-calculator-keyboard-BZ1CA4Ut.js → use-calculator-keyboard-DysnFK8m.js} +1 -1
- package/dist/{use-chat-composer-B4m3OEm1.js → use-chat-composer-xCcp9fVc.js} +2050 -1996
- package/dist/{use-heading-spy-CO3_AtY9.js → use-heading-spy-FIZlf9qj.js} +4 -4
- package/dist/{use-material-translation-uNc1FyC8.js → use-material-translation-xNzbSVgS.js} +1 -1
- package/dist/{use-return-focus-AfLRrUm-.js → use-return-focus-CW9KkDkp.js} +1 -1
- package/docs/README.md +1 -1
- package/docs/components/README.md +1 -0
- package/docs/components/buttons-and-actions.md +47 -0
- package/docs/components/data-display.md +2 -2
- package/docs/components/data-table-filter-and-lists.md +19 -6
- package/docs/components/form-fields.md +60 -7
- package/docs/components/navigation-and-layout.md +21 -3
- package/docs/components/primitives.md +44 -2
- package/docs/components/selection-and-choice.md +56 -1
- package/docs/guides/app-shell.md +22 -2
- package/docs/guides/cards-and-tiles.md +10 -0
- package/docs/guides/csp.md +4 -3
- package/docs/guides/data-grid.md +5 -4
- package/docs/guides/field-look-refined.md +2 -2
- package/docs/guides/keypad.md +9 -1
- package/docs/guides/printing.md +51 -1
- package/docs/previews/buttons-actions-producttile.webp +0 -0
- package/docs/previews/form-fields-quantitystepper.webp +0 -0
- package/docs/previews/navigation-layout-panelcard.webp +0 -0
- package/docs/roadmap.md +10 -10
- package/docs/troubleshooting.md +5 -4
- package/lib/components/ui/display/chip/chip-soft.css +27 -2
- package/lib/components/ui/primitives/collapsible/collapsible.css +9 -0
- package/lib/components/ui/widgets/calculator/calculator-keys.css +3 -0
- package/lib/components/ui/widgets/calculator/calculator.css +38 -7
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
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-
|
|
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-CK_47jfm.js";
|
|
3
3
|
import { t as l } from "./label-DR-zZyGQ.js";
|
|
4
|
-
import { a as u } from "./button-
|
|
5
|
-
import { i as d } from "./translate-material-
|
|
6
|
-
import { t as f } from "./use-material-translation-
|
|
4
|
+
import { a as u } from "./button-YfEMZbRO.js";
|
|
5
|
+
import { i as d } from "./translate-material-s_lxIV43.js";
|
|
6
|
+
import { t as f } from "./use-material-translation-xNzbSVgS.js";
|
|
7
7
|
import { _ as p, g as m, h, t as g, v as _, y as v } from "./date.utils-JTEIo3mM.js";
|
|
8
8
|
import { t as y } from "./input-CC2joTjB.js";
|
|
9
9
|
import { useCallback as b, useEffect as x, useId as S, useMemo as C, useRef as w, useState as T } from "react";
|
|
@@ -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-CEVomQ5K.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";
|
package/docs/README.md
CHANGED
|
@@ -22,7 +22,7 @@ detail: every option, the second example, and the traps.
|
|
|
22
22
|
| [Layouts](guides/layouts.md) | You are choosing a navigation preset, an app bar variant or a page layout, and want one working example of each |
|
|
23
23
|
| [Animations](guides/animations.md) | You animate with `animate-*` classes: setup next to `tw-animate-css`, every animation with its defaults, the modifiers, stagger, text splitting, scroll-driven animation, appear/disappear, reduced motion, recipes and browser support |
|
|
24
24
|
| [Theming](guides/theming.md) | You want to change colours or radius, use a shipped theme (`@nxgt/material/themes/<name>.css`) or a themed zone, add dark mode without a flash, use tones (the `data-tone` channel and app tones), or use the compiled CSS without Tailwind |
|
|
25
|
-
| [Printing](guides/printing.md) | You render PDF documents with `@nxgt/material/print`, turn them into a download, a new tab or a server response, write them as plain data,
|
|
25
|
+
| [Printing](guides/printing.md) | You render PDF documents with `@nxgt/material/print`, turn them into a download, a new tab or a server response, write them as plain data, bundle them into an SSR server, or export a list as CSV with `toCsv` and `csvResponse` |
|
|
26
26
|
| [PDF reader](guides/pdf-reader.md) | You render PDFs with `@nxgt/material/pdf-reader`: the pdf.js assets and worker, `configurePdfReader`, server rendering, the Content-Security-Policy and moving from the classic reader (removed in 3.0) |
|
|
27
27
|
| [Media players](guides/media.md) | You play video or audio: embedded, audio cards, playlists, chapters and transcript, sound across pages with `MediaPlayerHost`, the lightbox, CSP and keyboard |
|
|
28
28
|
| [Streaming (HLS)](guides/media-streaming.md) | You play `.m3u8` streams in `MediaPlayer` with `@nxgt/material/media-hls` |
|
|
@@ -34,6 +34,7 @@ Anything you press that is not a field: buttons, split buttons, floating action
|
|
|
34
34
|
[<img src="../previews/buttons-actions-toolbar.webp" height="72" alt="Toolbar">](buttons-and-actions.md#toolbar)
|
|
35
35
|
[<img src="../previews/buttons-actions-iconbutton.webp" height="72" alt="IconButton">](buttons-and-actions.md#iconbutton)
|
|
36
36
|
[<img src="../previews/buttons-actions-actioncard.webp" height="72" alt="ActionCard">](buttons-and-actions.md#actioncard)
|
|
37
|
+
[<img src="../previews/buttons-actions-producttile.webp" height="72" alt="ProductTile">](buttons-and-actions.md#producttile)
|
|
37
38
|
|
|
38
39
|
## [Form fields](form-fields.md)
|
|
39
40
|
|
|
@@ -24,6 +24,7 @@ and language switches. Everything is imported from `@nxgt/material/components`.
|
|
|
24
24
|
| <img src="../previews/buttons-actions-scrolltotop.webp" width="96" alt=""> | [ScrollToTop](#scrolltotop) | Appears past a scroll threshold |
|
|
25
25
|
| <img src="../previews/buttons-actions-scrolltolatestbutton.webp" width="96" alt=""> | [ScrollToLatestButton](#scrolltolatestbutton) | "Jump to latest" over a scroll box, counting what arrived |
|
|
26
26
|
| <img src="../previews/buttons-actions-actioncard.webp" width="96" alt=""> | [ActionCard](#actioncard) | Clickable card, a button (`aria-pressed`) when it chooses or reacts, with an active indicator; `status`, `meta`, a trailing `action` and a `tile` variant |
|
|
27
|
+
| <img src="../previews/buttons-actions-producttile.webp" width="96" alt=""> | [ProductTile](#producttile) | Large tap tile for a till or kiosk catalogue: picture or icon, name, price, stock state |
|
|
27
28
|
| <img src="../previews/buttons-actions-themetoggle.webp" width="96" alt=""> | [ThemeToggle](#themetoggle) | Light / dark / system switch |
|
|
28
29
|
| <img src="../previews/buttons-actions-languageswitcher.webp" width="96" alt=""> | [LanguageSwitcher](#languageswitcher) | Menu of the `I18nProvider` languages |
|
|
29
30
|
|
|
@@ -1335,6 +1336,52 @@ A whole board built on it, with sections (each its own h2 and free count) and, w
|
|
|
1335
1336
|
|
|
1336
1337
|
---
|
|
1337
1338
|
|
|
1339
|
+
## ProductTile
|
|
1340
|
+
|
|
1341
|
+

|
|
1342
|
+
|
|
1343
|
+
A large, even tap tile for a point-of-sale or kiosk catalogue: a picture (or a toned shape with an icon), the item's name, a prominent price and a stock state on its own line. `ActionCard` has no media or price slot; this is the tile for a catalogue.
|
|
1344
|
+
|
|
1345
|
+
```tsx
|
|
1346
|
+
import { Badge, ProductTile, ResponsiveGrid } from '@nxgt/material/components';
|
|
1347
|
+
|
|
1348
|
+
<ResponsiveGrid minItem="9rem" gap="md">
|
|
1349
|
+
<ProductTile
|
|
1350
|
+
title="Orange juice 1 L"
|
|
1351
|
+
price="1,500"
|
|
1352
|
+
icon="bottle-water"
|
|
1353
|
+
selected={basket.has(id)}
|
|
1354
|
+
onClick={() => add(id)}
|
|
1355
|
+
/>
|
|
1356
|
+
<ProductTile
|
|
1357
|
+
title="Free-range eggs, tray of thirty"
|
|
1358
|
+
price="4,800"
|
|
1359
|
+
image="/eggs.jpg"
|
|
1360
|
+
disabled
|
|
1361
|
+
status={<Badge variant="soft" tone="error">Out of stock</Badge>}
|
|
1362
|
+
/>
|
|
1363
|
+
</ResponsiveGrid>
|
|
1364
|
+
```
|
|
1365
|
+
|
|
1366
|
+
| Prop | Type | Default | Effect |
|
|
1367
|
+
| --- | --- | --- | --- |
|
|
1368
|
+
| `title` | `ReactNode` | required | The name, up to two lines |
|
|
1369
|
+
| `price` | `ReactNode` | none | Large and tabular (`tabular-nums`), at the foot of the tile; format it with the app's locale |
|
|
1370
|
+
| `status` | `ReactNode` | none | The stock state, on its own line under the price (a `Badge`) |
|
|
1371
|
+
| `image` | `string` | none | The photo's URL (a square one fits best), in a tinted 4:3 well, lazy loaded, with an empty `alt` (the title names the tile). If it fails to load, the toned `icon` shape takes its place |
|
|
1372
|
+
| `imageFit` | `'contain' \| 'cover'` | `contain` | `contain` for a cut-out PNG or a packshot on white, `cover` for a full-bleed photo |
|
|
1373
|
+
| `icon`, `tone`, `shape` | `IconName`, `Tone`, `ShapeName` | `primary`, `squircle` | Without an `image` (or when it fails), a toned shape with this icon, in the same 4:3 well, so tiles with and without a photo share one height |
|
|
1374
|
+
| `media` | `ReactNode` | none | Your own media in place of the box; replaces `image` and `icon` |
|
|
1375
|
+
| `selected` | `boolean` | `false` | `aria-pressed`, a tint and a bar at the start (the item is in the basket) |
|
|
1376
|
+
| `disabled` | `boolean` | `false` | Dimmed and not tappable; its text, status included, is still read |
|
|
1377
|
+
| `asChild` | `boolean` | `false` | Render inside the one child (a router `Link`): `<ProductTile asChild title="…"><Link to="…" /></ProductTile>`. `selected` is then `aria-current`, `disabled` is `aria-disabled`, `tabIndex={-1}` and a cancelled click, so a link leaves the Tab order and Enter does not follow it; `onClick` is not called |
|
|
1378
|
+
|
|
1379
|
+
The tile is one `button` named by its text: `getByRole('button', { name: /Orange juice.*1,500/ })`. It is a hairline card, at least 44 px high under a coarse pointer, and fills its grid cell (`h-full`): the name takes two lines and the status line is always reserved, so tiles of a row share one height. `minItem="9rem"` gives two columns at 360 px. It has a pressed state, a focus ring and a dark mode. It takes the `button`'s props, so `className` adds to it.
|
|
1380
|
+
|
|
1381
|
+
**Storybook:** `Buttons & actions/ProductTile` · **Vue:** later
|
|
1382
|
+
|
|
1383
|
+
---
|
|
1384
|
+
|
|
1338
1385
|
## ThemeToggle
|
|
1339
1386
|
|
|
1340
1387
|

|
|
@@ -601,7 +601,7 @@ text colour, 4.5:1 or more on the page and in a card; the `outlined` hairline
|
|
|
601
601
|
and the `tag` dot still read `--chip-tone`, not the tone channel); `tag`
|
|
602
602
|
a smaller tag led by a colour dot; `dashed` a muted "add" pill. 28px tall,
|
|
603
603
|
24px with `size="sm"`, 44px with `size="lg"`. Selected (`active`, or `aria-pressed` on a toggle
|
|
604
|
-
chip) takes a primary tint and a primary edge, and a pressed toggle with
|
|
604
|
+
chip) takes a primary tint and a primary edge (a pressed toggle chip with a `color` shows the filled tone under its on-colour instead), and a pressed toggle with
|
|
605
605
|
nothing leading draws a check. An unselected neutral toggle chip keeps a
|
|
606
606
|
boundary of at least 3:1 against the page and the card. The label is truncated as one piece; the ✕
|
|
607
607
|
and `trailing` stay whole.
|
|
@@ -631,7 +631,7 @@ import { Avatar, Chip } from '@nxgt/material/components';
|
|
|
631
631
|
| `active` | `boolean` | — | selected: primary tint and edge |
|
|
632
632
|
| `aria-pressed` | `boolean` | — | a toggle chip: selected while pressed, with a check when nothing leads |
|
|
633
633
|
| `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | 28px, 24px, or `lg`, a 44px box at every pointer (the touch target); every target stays at least 24px |
|
|
634
|
-
| `color` | `'default'
|
|
634
|
+
| `color` | `'default'` or any `Tone` (the eight named tones, or one declared in `AppTones`) | neutral | tone: the edge (outlined), the fill and label (tonal, `filled`: a status look) or the dot and tint (tag). A `tag` without `color` (or with `default`) draws no dot |
|
|
635
635
|
| `onDismiss` | `() => void` | — | adds the ✕ button, named "Remove {label}" (`material.chip.dismiss-label`), `{label}` being the text `label` shows, a node's included; "Remove" when it shows none; the ✕ is drawn 18px (16px small) in its own 24x24 box (44x44 on a coarse pointer) |
|
|
636
636
|
| `dismissLabel` | `string \| ((label: string) => string)` | — | names the ✕ instead of "Remove {label}": a string as is, or a function of the text the chip shows |
|
|
637
637
|
| `onClick` | `MouseEventHandler` | — | makes the chip (or its body, when dismissible) a button; without it the chip is a `<span>` |
|
|
@@ -107,9 +107,11 @@ table enables, for typing your own column defs.
|
|
|
107
107
|
| `onLoadMore` / `isLoadingMore` | `() => void` / `boolean` | — | adds a "load more" button under the rows |
|
|
108
108
|
| `options` | `{ showPage?: boolean; showSelected?: boolean }` | both `true` | parts of the footer |
|
|
109
109
|
| `showHeader` | `boolean` | shown | header row (column names, sorting, "Select all") at every width; `false` hides it |
|
|
110
|
+
| `showSort` | `boolean` | shown | `false` drops the sort control of the `cards` and `tiles` list bar (on a phone too); columns stay sortable and a `table` keeps its header sorting ([Cards and tiles](../guides/cards-and-tiles.md#sorting)) |
|
|
110
111
|
| `maxHeight` | `number \| string` | none | caps the table's height (`400`, `'60vh'`); the rows scroll inside it |
|
|
112
|
+
| `maxHeightClassName` | class | none | the same cap as a Tailwind class (`'max-h-[28rem]'`), with no inline `style` on the scroll region, server included; wins over `maxHeight` |
|
|
111
113
|
| `scrollLabel` | `string` | "Scrollable table" | the name of the scroll region the table becomes while it overflows |
|
|
112
|
-
| `virtual` | `boolean \| { estimateSize?, overscan?, ssrRows? }` | off | renders only the rows in view, for long lists; needs `maxHeight`. Client mode shows every filtered and sorted row with no paging and no pagination footer; `manual` virtualizes the rows given. Defaults: `estimateSize` 36 (px, a comfortable row without the gap between rows), `overscan` 8, `ssrRows` 50. `renderExpandedRow` is ignored while it is on ([long lists](#long-lists)) |
|
|
114
|
+
| `virtual` | `boolean \| { estimateSize?, overscan?, ssrRows? }` | off | renders only the rows in view, for long lists; needs `maxHeight` or `maxHeightClassName`. Client mode shows every filtered and sorted row with no paging and no pagination footer; `manual` virtualizes the rows given. Defaults: `estimateSize` 36 (px, a comfortable row without the gap between rows), `overscan` 8, `ssrRows` 50. `renderExpandedRow` is ignored while it is on ([long lists](#long-lists)) |
|
|
113
115
|
| `loading` | `boolean` | `false` | skeleton rows, or the current rows dimmed; `aria-busy`; controls disabled |
|
|
114
116
|
| `loadingRows` | `number` | the page size | how many skeleton rows |
|
|
115
117
|
| `density` | `'comfortable' \| 'compact'` | `'comfortable'` | `compact` tightens the cell padding, the row and header heights and the gap between rows, the selection column included; it is the table's `data-density` attribute, styled with classes (no inline style). The toolbar band and the empty state keep their size; the print variant is unchanged |
|
|
@@ -527,12 +529,22 @@ Rows stay rows on a phone unless you ask for `layout="cards"`, `layout="tiles"`
|
|
|
527
529
|
virtual
|
|
528
530
|
maxHeight="70vh"
|
|
529
531
|
/>
|
|
532
|
+
|
|
533
|
+
// Under a strict CSP (`style-src-attr 'none'`): a class, no inline style.
|
|
534
|
+
<DataTable
|
|
535
|
+
data={people}
|
|
536
|
+
columns={columns}
|
|
537
|
+
getRowId={(person) => person.id}
|
|
538
|
+
virtual
|
|
539
|
+
maxHeightClassName="max-h-[28rem]"
|
|
540
|
+
/>
|
|
530
541
|
```
|
|
531
542
|
|
|
532
543
|
`virtual` renders only the rows in view and a few around them (`overscan`),
|
|
533
544
|
between two `aria-hidden` spacer rows that stand for the rest. It needs
|
|
534
|
-
`maxHeight`, since the rows scroll inside the table
|
|
535
|
-
|
|
545
|
+
`maxHeight` or `maxHeightClassName`, since the rows scroll inside the table
|
|
546
|
+
(the virtualizer measures the scroll region, so the class needs no number):
|
|
547
|
+
without either, `virtual` is off and a development warning says so. In client mode every filtered and
|
|
536
548
|
sorted row is in one scrolling list: no paging and no pagination footer
|
|
537
549
|
(`onLoadMore` still shows its button). In `manual` mode the rows given are
|
|
538
550
|
virtualized as they are, with the server footer.
|
|
@@ -556,8 +568,9 @@ virtualized as they are, with the server footer.
|
|
|
556
568
|
takes over after mount. The spacers' heights are custom properties
|
|
557
569
|
(`--vr-before`, `--vr-after`) written on the `<tbody>` through CSSOM, which
|
|
558
570
|
`style-src-attr` does not govern: the rows add no `style` attribute to the
|
|
559
|
-
server markup. The scroll region's
|
|
560
|
-
`style` there
|
|
571
|
+
server markup. The scroll region's `maxHeight` is an inline
|
|
572
|
+
`style` there; pass `maxHeightClassName` instead and it has none
|
|
573
|
+
([csp.md](../guides/csp.md)).
|
|
561
574
|
|
|
562
575
|
`@nxgt/material-vue`'s `DataTable` has no `virtual` yet.
|
|
563
576
|
|
|
@@ -1070,7 +1083,7 @@ export default function Students() {
|
|
|
1070
1083
|
and its "Clear search and filters" button
|
|
1071
1084
|
(`onParamsChange(withoutFilters(params))`, then `onClearFilters`, or the
|
|
1072
1085
|
table, or the list of cards or tiles, takes the focus). Your search box and switches stay outside.
|
|
1073
|
-
- **Passed through** to `DataTable`: `columns`, `maxHeight`, `scrollLabel`,
|
|
1086
|
+
- **Passed through** to `DataTable`: `columns`, `maxHeight`, `maxHeightClassName`, `scrollLabel`,
|
|
1074
1087
|
`showViewOptions`, `renderExpandedRow`, `options`, `loading`,
|
|
1075
1088
|
`loadingRows`, `getRowHref`, `onRowClick`, `linkComponent`, `density`,
|
|
1076
1089
|
`layout`, `enableRowSelection`, `rowSelectionDisabledReason`,
|
|
@@ -27,6 +27,7 @@ order and roles) and the tokens that tune it.
|
|
|
27
27
|
| <img src="../previews/form-fields-numberfield.webp" width="96" alt=""> | [NumberField](#numberfield) | `number \| null` with clamping and +/− buttons |
|
|
28
28
|
| <img src="../previews/form-fields-percentfield.webp" width="96" alt=""> | [PercentField](#percentfield) | `NumberField` with a `%` suffix |
|
|
29
29
|
| <img src="../previews/form-fields-quantityfield.webp" width="96" alt=""> | [QuantityField](#quantityfield) | One labelled field holding an amount and its unit |
|
|
30
|
+
| <img src="../previews/form-fields-quantitystepper.webp" width="96" alt=""> | [QuantityStepper](#quantitystepper) | Inline −/value/+ control for a cart or ticket line |
|
|
30
31
|
| <img src="../previews/form-fields-currencyfield.webp" width="96" alt=""> | [CurrencyField](#currencyfield) | Locale-formatted amount with a currency suffix |
|
|
31
32
|
| <img src="../previews/form-fields-phonefield.webp" width="96" alt=""> | [PhoneField](#phonefield) | International number, emits E.164 |
|
|
32
33
|
| <img src="../previews/form-fields-urlfield.webp" width="96" alt=""> | [UrlField](#urlfield) | Validates on blur, open and copy buttons |
|
|
@@ -802,7 +803,7 @@ phone: the chip truncates before the value does (see The unit chip below).
|
|
|
802
803
|
| `control`, `name` | `Control<T>`, `Path<T>` | — | the form and its `{ value, unit }` (`QuantityFormField`) |
|
|
803
804
|
| `value`, `defaultValue`, `onChange` | `QuantityValue`, `(value) => void` | empty | the amount and its unit; `onChange` gets both on each change |
|
|
804
805
|
| `label` | `ReactNode` | none | the one label; it names the group |
|
|
805
|
-
| `units` | `{ value, label }[]` | none | the units offered |
|
|
806
|
+
| `units` | `{ value, label }[]` | none | the units offered; without `units` and `allowCustomUnit` there is no unit to pick, so no chip is drawn (a plain count) |
|
|
806
807
|
| `allowCustomUnit` | `boolean` | `false` | the unit menu gets a "Search or type a unit" input and a "Use “x”" item, so a unit outside `units` can be typed; otherwise it is a plain listbox of `units` |
|
|
807
808
|
| `min`, `max`, `step`, `hideSteppers` | | `step` 1 | as `NumberField` |
|
|
808
809
|
| `unitLabel`, `unitPlaceholder` | `string` | "<label> unit" | `unitLabel` names the unit part (the menu's name; the chip reads "<unitLabel>: mg"); `unitPlaceholder` shows while no unit is chosen |
|
|
@@ -812,15 +813,16 @@ phone: the chip truncates before the value does (see The unit chip below).
|
|
|
812
813
|
| `required`, `disabled`, `placeholder`, `id`, `ref` | | | `required` applies to the number only; to require a unit, validate `unit` in the form schema. `placeholder`, `id` and `ref` reach the number input (React Hook Form's `field.ref` focuses it on an error) |
|
|
813
814
|
|
|
814
815
|
- **Semantics:** a `<fieldset>` (role `group`) named by the label; the number
|
|
815
|
-
is a textbox named after the label, the unit chip a button
|
|
816
|
-
(`aria-expanded`; it opens a popover), the menu a listbox
|
|
817
|
-
"Dose unit". The message describes the group, the number and the chip,
|
|
818
|
-
the number is `aria-invalid` on an error.
|
|
819
|
-
|
|
816
|
+
is a textbox named after the label, the unit chip (when drawn) a button
|
|
817
|
+
named "Unit: mg" (`aria-expanded`; it opens a popover), the menu a listbox
|
|
818
|
+
named "Dose unit". The message describes the group, the number and the chip,
|
|
819
|
+
and the number is `aria-invalid` on an error. Without a chip there is no
|
|
820
|
+
unit button or listbox.
|
|
821
|
+
- **Keyboard:** Tab goes value, unit chip (when drawn), Decrease, Increase. Enter, Space or
|
|
820
822
|
ArrowDown on the chip opens the menu (focus lands on the list, or on the
|
|
821
823
|
search with `allowCustomUnit`); the arrows move, Enter picks, Esc closes and
|
|
822
824
|
returns focus to the chip.
|
|
823
|
-
- **The unit chip
|
|
825
|
+
- **The unit chip** (drawn only with `units` or `allowCustomUnit`; a plain `<QuantityField label="Count" />` has none, and Tab goes value, Decrease, Increase): one focus ring for the whole box, whichever part has
|
|
824
826
|
focus. The chip is capped at 52% of the box and truncates a long label with
|
|
825
827
|
an ellipsis (the full label is its `title`); the value keeps room for about
|
|
826
828
|
4 digits at 360px. A coarse pointer gets 44px targets. Disabled, the chip
|
|
@@ -836,6 +838,57 @@ phone: the chip truncates before the value does (see The unit chip below).
|
|
|
836
838
|
|
|
837
839
|
---
|
|
838
840
|
|
|
841
|
+
## QuantityStepper
|
|
842
|
+
|
|
843
|
+

|
|
844
|
+
|
|
845
|
+
A compact inline −/value/+ control for a cart or ticket line, where a
|
|
846
|
+
[`NumberField`](#numberfield) or [`QuantityField`](#quantityfield) (a labelled
|
|
847
|
+
box) is too heavy. It has no label and no border: a `group` named by your
|
|
848
|
+
`aria-label` ("Quantity of Orange juice") holding a minus button, the value and
|
|
849
|
+
a plus button. It is an uncontrolled or controlled number, not a form field.
|
|
850
|
+
|
|
851
|
+
```tsx
|
|
852
|
+
import { QuantityStepper } from '@nxgt/material/components';
|
|
853
|
+
import { useState } from 'react';
|
|
854
|
+
|
|
855
|
+
export function Line() {
|
|
856
|
+
const [qty, setQty] = useState(2);
|
|
857
|
+
return (
|
|
858
|
+
<QuantityStepper
|
|
859
|
+
aria-label="Quantity of Orange juice"
|
|
860
|
+
value={qty}
|
|
861
|
+
onValueChange={setQty}
|
|
862
|
+
min={1}
|
|
863
|
+
max={20}
|
|
864
|
+
onRemove={() => removeLine()}
|
|
865
|
+
/>
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
| Prop | Type | Default | What it does |
|
|
871
|
+
| --- | --- | --- | --- |
|
|
872
|
+
| `aria-label` | `string` | required | Names the group and the value; the buttons are "Decrease …", "Increase …" and "Remove …" followed by it |
|
|
873
|
+
| `value` / `defaultValue` | `number` | `min` | Controlled / uncontrolled value |
|
|
874
|
+
| `onValueChange` | `(value: number) => void` | none | Called with the new value, always within `min` and `max` |
|
|
875
|
+
| `min`, `max` | `number` | `0`, none | The limits; the minus (or plus) button is `aria-disabled` at its limit |
|
|
876
|
+
| `step` | `number` | `1` | What a button or an arrow key adds; Page Up/Down use ten steps, Home/End jump to `min`/`max` |
|
|
877
|
+
| `size` | `'sm' \| 'md'` | `'md'` | 32px or 36px buttons; both are 44px under a coarse pointer |
|
|
878
|
+
| `editable` | `boolean` | `false` | The value is a text input: committed on Enter or blur (clamped; text that is not a number is dropped), Escape cancels; only Up/Down and Page Up/Down step it, Left/Right/Home/End move the caret |
|
|
879
|
+
| `onRemove` | `() => void` | none | At `min`, the minus button becomes a trash button named "Remove …" that calls this instead of stepping |
|
|
880
|
+
| `disabled` | `boolean` | `false` | Disables every control |
|
|
881
|
+
|
|
882
|
+
The value is a `spinbutton` (`aria-valuenow`, `aria-valuemin`, `aria-valuemax`)
|
|
883
|
+
in tabular figures, so the row does not shift as digits change. The stepper is
|
|
884
|
+
inline (`inline-flex`): put it in a `ListTile`'s `trailing` or `content`. The
|
|
885
|
+
text is translated through `material.number-field.*` and
|
|
886
|
+
`material.quantity-stepper.remove`.
|
|
887
|
+
|
|
888
|
+
**Storybook:** `Form fields/QuantityStepper` · **Vue:** later
|
|
889
|
+
|
|
890
|
+
---
|
|
891
|
+
|
|
839
892
|
## CurrencyField
|
|
840
893
|
|
|
841
894
|

|
|
@@ -82,7 +82,7 @@ phone's menu sheet pad clear of `env(safe-area-inset-*)`, as classes; see the
|
|
|
82
82
|
|
|
83
83
|
| Prop | Type | Default | Effect |
|
|
84
84
|
| --- | --- | --- | --- |
|
|
85
|
-
| `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`, `defaultExpanded` (the expanded sidebar folds the entry's children from a chevron button, open at first when `true`, folded when `false`; unset, they are always listed), `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
|
+
| `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`, `defaultExpanded` (the expanded sidebar folds the entry's children from a chevron button, open at first when `true`, folded when `false`; unset, they are always listed), `persistKey` (remembers a collapsible group's fold state in this browser: a string key, or `true` to derive it from `navigation.persistKey` and the `url`), `badgeWhen` (`'collapsed'`: a collapsible group's own `badge` shows only while it is folded), `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) |
|
|
86
86
|
| `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. |
|
|
87
87
|
| `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 |
|
|
88
88
|
| `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)) |
|
|
@@ -670,7 +670,7 @@ import { ListDetailsLayout, PaneLayout, ThreadLayout } from '@nxgt/material/comp
|
|
|
670
670
|
</ListDetailsLayout.Details>
|
|
671
671
|
</ListDetailsLayout>
|
|
672
672
|
|
|
673
|
-
<PaneLayout defaultOpen paneSide="right">
|
|
673
|
+
<PaneLayout defaultOpen="desktop" paneSide="right">
|
|
674
674
|
<PaneLayout.Content>
|
|
675
675
|
<PaneLayout.Trigger>Folders</PaneLayout.Trigger>
|
|
676
676
|
{messages}
|
|
@@ -716,10 +716,25 @@ takes over. A page rendered only in the browser starts on the real layout.
|
|
|
716
716
|
container and the flex row moves to an inner `data-slot="pane-layout-body"`.
|
|
717
717
|
- `paneWidth`: `'sm'` (16rem), `'md'` (20rem, default) or `'lg'` (24rem) for
|
|
718
718
|
the inline pane; no style attribute, so it holds under a strict CSP.
|
|
719
|
+
- `defaultOpen`: `true` / `false` for every viewport, `'desktop'` (open wide,
|
|
720
|
+
shut on a phone) or `{ desktop, phone }` (a missing key is shut). The state
|
|
721
|
+
follows the viewport until the user opens or closes the pane, so a cart that
|
|
722
|
+
starts open on desktop never opens its sheet over a phone, with no effect
|
|
723
|
+
and no flash: the server renders the desktop value and the inline pane is
|
|
724
|
+
hidden by CSS on a phone. With `phone="stack"` the pane has no overlay to
|
|
725
|
+
hide, so a stacked pane that is open on desktop only shows for one frame
|
|
726
|
+
before closing on a phone.
|
|
727
|
+
- `paneSide`: `'left'` (default) or `'right'`. The pane takes its side with a
|
|
728
|
+
CSS `order`, never by reversing the row, so the DOM and tab order are the
|
|
729
|
+
ones you write: put `PaneLayout.Content` first and the pane after it and
|
|
730
|
+
Tab reaches the content first, whichever side the pane is on.
|
|
731
|
+
Before this change a right pane written after the content sat on the left; check
|
|
732
|
+
screens that relied on it.
|
|
719
733
|
- With `phone` or `breakpoint` set, the inline pane is an `<aside>` named by
|
|
720
734
|
`sheetTitle` (default `Side Panel`), a landmark; the historical markup is a
|
|
721
735
|
`div`. `usePaneLayout()` and the function children also get `phone`,
|
|
722
736
|
`breakpoint` and `paneWidth`.
|
|
737
|
+
|
|
723
738
|
`ThreadLayout` is a centred column over the app
|
|
724
739
|
[background](#background) on the page ground (`bg-ground`, so a `bg-card` card stands apart) for focused flows (sign-in, onboarding), with no
|
|
725
740
|
app bar. `width`, `stepperPlacement` and a sticky `footer` make it a centred form or a wizard with a side rail (see "A centred form or a wizard" below).
|
|
@@ -736,7 +751,8 @@ app bar. `width`, `stepperPlacement` and a sticky `footer` make it a centred for
|
|
|
736
751
|
| `history` / `historyParam` | `'push' \| 'replace'` / `string` | — / `'view'` | `ListDetailsLayout`: keep the view in the URL (`?view=details`), through React Router |
|
|
737
752
|
| `listWidth` / `resizable` / `onListWidthChange` | `number` / `boolean` / `(width) => void` | `320` / `false` | `ListDetailsLayout`: the list pane's width in px; `resizable` adds a keyboard and pointer separator, 240 to 480 px |
|
|
738
753
|
| `back` | `boolean \| ReactNode` | — | `ListDetailsLayout.Details`: a bar with the way back to the list, on a phone only; `true` is the translated "Back to the list" (`material['list-details'].back`), a node is its label |
|
|
739
|
-
| `open` / `
|
|
754
|
+
| `open` / `onOpenChange` | `boolean` | `false` | `PaneLayout`: pane shown |
|
|
755
|
+
| `defaultOpen` | `boolean \| 'desktop' \| { desktop?, phone? }` | `false` | `PaneLayout`: initial pane state per viewport |
|
|
740
756
|
| `paneSide` | `'left' \| 'right'` | `'left'` | `PaneLayout` |
|
|
741
757
|
| `sheetTitle` | `string` | "Side Panel" (translated, `material['pane-layout']['sheet-title']`) | `PaneLayout.Pane`: screen-reader title of the sheet on phones |
|
|
742
758
|
| `header` | `ReactNode` | — | `ThreadLayout`: above the column |
|
|
@@ -1223,6 +1239,8 @@ import { PanelCard } from '@nxgt/material/components';
|
|
|
1223
1239
|
|
|
1224
1240
|
The card is a `role="region"` labelled by its title, so `getByRole('region', { name })` finds it. Children are the card's content (`CardContent`); with none, the card is its header only. It takes the `div`'s props, so `className` adds to it.
|
|
1225
1241
|
|
|
1242
|
+
The title is set in the card-title step (`title-sm` emphasized) whatever its heading level, one step below an `ActivityContent` page title (`title-md` emphasized), so an aside never outranks the page. The level only sets the document outline, not the size.
|
|
1243
|
+
|
|
1226
1244
|
**Storybook:** `Navigation & layout/PanelCard` · **Vue:** later, [#2866](https://github.com/softistx/nxgt-material/issues/2866)
|
|
1227
1245
|
|
|
1228
1246
|
---
|
|
@@ -83,7 +83,7 @@ opens at once.
|
|
|
83
83
|
|
|
84
84
|

|
|
85
85
|
|
|
86
|
-
A single show / hide region and its trigger. The content springs open to its height and folds away (clipped only while it moves, so a focus ring inside is never cut). Give `CollapsibleTrigger` a `variant` for a styled trigger with a turning chevron: `tonal`, a pill that squares its corners while open, or `text`.
|
|
86
|
+
A single show / hide region and its trigger. The content springs open to its height and folds away (clipped only while it moves, so a focus ring inside is never cut). Give `CollapsibleTrigger` a `variant` for a styled trigger with a turning chevron: `tonal`, a pill that squares its corners while open, or `text`. Add `flush` to a `text` trigger to drop its inline padding and take the parent's font, so a section disclosure inside `<Typography as="h3">` lines up with the headings around it.
|
|
87
87
|
|
|
88
88
|
```tsx
|
|
89
89
|
import {
|
|
@@ -110,6 +110,33 @@ import {
|
|
|
110
110
|
</Collapsible>;
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
A flush trigger inside a heading, aligned with the headings around it:
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
import {
|
|
117
|
+
Collapsible,
|
|
118
|
+
CollapsibleContent,
|
|
119
|
+
CollapsibleTrigger,
|
|
120
|
+
Typography,
|
|
121
|
+
} from '@nxgt/material/components';
|
|
122
|
+
|
|
123
|
+
<div className="flex w-80 flex-col gap-3">
|
|
124
|
+
<Typography as="h3" variant="title-sm" emphasized>
|
|
125
|
+
Question
|
|
126
|
+
</Typography>
|
|
127
|
+
<Collapsible>
|
|
128
|
+
<Typography as="h3" variant="title-sm" emphasized>
|
|
129
|
+
<CollapsibleTrigger variant="text" flush>
|
|
130
|
+
Details
|
|
131
|
+
</CollapsibleTrigger>
|
|
132
|
+
</Typography>
|
|
133
|
+
<CollapsibleContent className="text-muted-foreground text-sm">
|
|
134
|
+
Hidden text that opens under the heading.
|
|
135
|
+
</CollapsibleContent>
|
|
136
|
+
</Collapsible>
|
|
137
|
+
</div>;
|
|
138
|
+
```
|
|
139
|
+
|
|
113
140
|
Parts: `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent`.
|
|
114
141
|
|
|
115
142
|
| Prop | Type | Default | Effect |
|
|
@@ -117,6 +144,7 @@ Parts: `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent`.
|
|
|
117
144
|
| `defaultOpen` / `open` | `boolean` | `false` | whether the content shows, uncontrolled / controlled |
|
|
118
145
|
| `onOpenChange` | `(open: boolean) => void` | — | called when the trigger toggles it |
|
|
119
146
|
| `disabled` | `boolean` | `false` | the trigger does nothing |
|
|
147
|
+
| `flush` (`CollapsibleTrigger`) | `boolean` | `false` | with `variant="text"`, no inline padding and the parent's font: the label starts at the left edge and can sit in a heading; ignored on other variants |
|
|
120
148
|
| `variant` (`CollapsibleTrigger`) | `'tonal' \| 'text'` | — | a styled trigger with a chevron; unset, it is unstyled as before (with `asChild`, the child takes the look and brings its own icon) |
|
|
121
149
|
|
|
122
150
|
To keep the former instant toggle, pass `className="animate-none"` to
|
|
@@ -705,7 +733,7 @@ For sorting, paging and selection use `DataTable` on the
|
|
|
705
733
|
|
|
706
734
|

|
|
707
735
|
|
|
708
|
-
Tabs whose triggers can take an icon, in two looks: `underline` (the default) and the `segmented` pill.
|
|
736
|
+
Tabs whose triggers can take an icon, in two looks: `underline` (the default) and the `segmented` pill. `<TabsList fullWidth>` on a `segmented` list fills its container and shares the width between the tabs in equal parts (a payment-method switch); `underline` ignores it.
|
|
709
737
|
|
|
710
738
|
```tsx
|
|
711
739
|
import {
|
|
@@ -767,6 +795,19 @@ few short choices, such as a view switch.
|
|
|
767
795
|
</Tabs>
|
|
768
796
|
```
|
|
769
797
|
|
|
798
|
+
**`fullWidth`** on a `segmented` `TabsList` makes the track fill its container,
|
|
799
|
+
the tabs sharing the width in equal parts, whatever their labels. It suits a
|
|
800
|
+
two-way switch such as a payment method.
|
|
801
|
+
|
|
802
|
+
```tsx
|
|
803
|
+
<Tabs defaultValue="cash" variant="segmented">
|
|
804
|
+
<TabsList fullWidth aria-label="Payment method">
|
|
805
|
+
<TabsTrigger value="cash">Cash</TabsTrigger>
|
|
806
|
+
<TabsTrigger value="card">Card</TabsTrigger>
|
|
807
|
+
</TabsList>
|
|
808
|
+
</Tabs>
|
|
809
|
+
```
|
|
810
|
+
|
|
770
811
|
`orientation="vertical"` lays the tabs in a column, the indicator on their
|
|
771
812
|
left side; the arrow keys become Up and Down.
|
|
772
813
|
|
|
@@ -825,6 +866,7 @@ as `segmented`. Both still type-check and render; write `underline` or
|
|
|
825
866
|
| `variant` (`Tabs`) | `'underline' \| 'segmented' \| 'bar' \| 'line'` | `'underline'` | the look; `bar` and `line` are deprecated aliases of `underline` and `segmented` |
|
|
826
867
|
| `defaultValue` / `value` (`Tabs`) | `string` | — | the selected tab, uncontrolled / controlled |
|
|
827
868
|
| `onValueChange` (`Tabs`) | `(value: string) => void` | — | called when a trigger is selected |
|
|
869
|
+
| `fullWidth` (`TabsList`) | `boolean` | `false` | `segmented` only: the track fills its container and the tabs share the width in equal parts; `underline` ignores it |
|
|
828
870
|
| `orientation` (`Tabs`) | `'horizontal' \| 'vertical'` | `'horizontal'` | a row or a column, and the arrow keys' direction |
|
|
829
871
|
| `dir` (`Tabs`) | `'ltr' \| 'rtl'` | `'ltr'` | the reading direction: which way the tabs run and the arrow keys move |
|
|
830
872
|
| `icon` (`TabsTrigger`) | `ReactNode` | — | shown before the label |
|
|
@@ -840,6 +840,61 @@ export function Topics() {
|
|
|
840
840
|
}
|
|
841
841
|
```
|
|
842
842
|
|
|
843
|
+
<a id="chipgroupfield-tones"></a>
|
|
844
|
+
|
|
845
|
+
An option's `tone` colours its chip with the tones `Badge` takes (`primary`,
|
|
846
|
+
`secondary`, `tertiary`, `success`, `info`, `warning`, `error`, `neutral`, and
|
|
847
|
+
an app's own from `AppTones`), so a category filter can match the toned tiles
|
|
848
|
+
below it. An unchosen chip shows a soft tint of the tone with the tone's text
|
|
849
|
+
colour; a chosen chip shows the filled tone under its on-colour, both at AA
|
|
850
|
+
contrast in light and dark. The leading check, `aria-checked`, the roving
|
|
851
|
+
focus and the boundary of an unchosen chip are unchanged. No `tone` keeps the
|
|
852
|
+
neutral chip.
|
|
853
|
+
|
|
854
|
+
```tsx
|
|
855
|
+
<ChipGroupField
|
|
856
|
+
label="Categories"
|
|
857
|
+
options={[
|
|
858
|
+
{ value: 'drinks', label: 'Drinks', icon: 'glass', tone: 'info' },
|
|
859
|
+
{ value: 'bakery', label: 'Bakery', tone: 'warning' },
|
|
860
|
+
{ value: 'other', label: 'Other' },
|
|
861
|
+
]}
|
|
862
|
+
value={value}
|
|
863
|
+
onValueChange={setValue}
|
|
864
|
+
/>
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
`overflow="scroll"` keeps the chips on one line that scrolls sideways (touch,
|
|
868
|
+
trackpad, the wheel; a focused chip scrolls into view), with a fade at each
|
|
869
|
+
edge that hides more chips and no page scroll. The roving focus and the
|
|
870
|
+
selection are the same. The default `overflow="wrap"` lets the chips flow onto
|
|
871
|
+
several lines; `columns` and `groups` draw a grid and ignore `overflow`.
|
|
872
|
+
|
|
873
|
+
`overflow="scroll"` for one sideways-scrolling line:
|
|
874
|
+
|
|
875
|
+
```tsx
|
|
876
|
+
import { ChipGroupField } from '@nxgt/material/components';
|
|
877
|
+
import { useState } from 'react';
|
|
878
|
+
|
|
879
|
+
const subjects = ['Algebra', 'Biology', 'Chemistry', 'Drama', 'Economics', 'French'].map(
|
|
880
|
+
(label) => ({ value: label.toLowerCase(), label }),
|
|
881
|
+
);
|
|
882
|
+
|
|
883
|
+
export function Subjects() {
|
|
884
|
+
const [value, setValue] = useState<string[]>([]);
|
|
885
|
+
return (
|
|
886
|
+
<ChipGroupField
|
|
887
|
+
label="Subjects"
|
|
888
|
+
type="multiple"
|
|
889
|
+
overflow="scroll"
|
|
890
|
+
options={subjects}
|
|
891
|
+
value={value}
|
|
892
|
+
onValueChange={setValue}
|
|
893
|
+
/>
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
```
|
|
897
|
+
|
|
843
898
|
Balanced by default, and the unbalanced fill:
|
|
844
899
|
|
|
845
900
|
```tsx
|
|
@@ -858,7 +913,7 @@ const four = ['a', 'b', 'c', 'd'].map((value) => ({ value, title: value.toUpperC
|
|
|
858
913
|
| `value` / `onValueChange` | `string \| null` or `string[]` | — | the chosen value, by `type` |
|
|
859
914
|
| `deselectable` | `boolean` | `false` | `single` only: pressing the chosen chip clears it |
|
|
860
915
|
| `max` / `min` | `number` | — | `multiple` only: `max` blocks the other chips once reached and speaks "{count} of {max} chosen"; `min` is a hint only |
|
|
861
|
-
| `options` | `{ value; label; icon?; count?; description?; disabled?; disabledReason? }[]` | — | the chips, in one flow; `count`: see [option counts](#option-counts) |
|
|
916
|
+
| `options` | `{ value; label; icon?; tone?; count?; description?; disabled?; disabledReason? }[]` | — | the chips, in one flow; `count`: see [option counts](#option-counts); `tone`: see [tones](#chipgroupfield-tones) |
|
|
862
917
|
| `groups` | `{ label: string; options }[]` | — | titled sections instead of `options` (it wins when both are given): see [slot picker](#slot-picker-columns-groups-and-reasons) |
|
|
863
918
|
| `columns` | `number \| { base?; sm?; md?; lg? }` | — | equal-width columns (1 to 12; one count per breakpoint, each holding to the next); Up and Down then move by row. Unset, the chips flow and wrap |
|
|
864
919
|
| `showDisabledReason` | `boolean` | `true` | prints a disabled option's `disabledReason` below the whole group ("Label: reason"), so the chip row keeps its wrapping; `false` leaves it to the tooltip and the accessible description |
|
package/docs/guides/app-shell.md
CHANGED
|
@@ -129,6 +129,8 @@ type MenuItem = {
|
|
|
129
129
|
`badge` marks an entry that has something new (the Material 3 navigation
|
|
130
130
|
badge):
|
|
131
131
|
|
|
132
|
+
(A collapsible sidebar group can show its own `badge` only while folded: see `badgeWhen` with `defaultExpanded`, below.)
|
|
133
|
+
|
|
132
134
|
```tsx
|
|
133
135
|
const items: MenuItem[] = [
|
|
134
136
|
{ label: 'Home', url: '/', icon: 'house', badge: true }, // a dot
|
|
@@ -418,8 +420,26 @@ its `url`, with its badge, for a parent page that already lists them. Unset
|
|
|
418
420
|
chevron button after the entry (named "Collapse the group" / "Expand the group",
|
|
419
421
|
`material.navigation.group-collapse` and `group-expand`) shows and hides them,
|
|
420
422
|
`true` opening them at first and `false` folding them (a folded group still opens at first when the current page is one of its children). It is ignored with `display: 'icon'`. Unset, they are always
|
|
421
|
-
listed, as before. The entry stays a link to its `url
|
|
422
|
-
|
|
423
|
+
listed, as before. The entry stays a link to its `url`, and the phone's menu sheet and the other presets ignore it.
|
|
424
|
+
|
|
425
|
+
By default the fold state is local and resets on reload. `persistKey` remembers it
|
|
426
|
+
in this browser: a string is the storage key, `true` derives the key from
|
|
427
|
+
`navigation.persistKey` and the entry's `url` (nothing is saved without that key).
|
|
428
|
+
Like the sidebar's own collapse, the saved choice is read after hydration, so a
|
|
429
|
+
server render and the first client render both follow `defaultExpanded`; a saved
|
|
430
|
+
choice wins over the "current page is a child" opening. `badgeWhen: 'collapsed'`
|
|
431
|
+
draws the parent's `badge` only while the group is folded, so its children's own
|
|
432
|
+
badges are not signalled twice (default `'always'`).
|
|
433
|
+
|
|
434
|
+
```tsx
|
|
435
|
+
{
|
|
436
|
+
label: 'Groups', url: '/groups', icon: 'users', badge: true,
|
|
437
|
+
children: [{ label: 'Item A', url: '/a', badge: 2 }],
|
|
438
|
+
defaultExpanded: true,
|
|
439
|
+
persistKey: true, // under navigation.persistKey + '/groups'
|
|
440
|
+
badgeWhen: 'collapsed', // the dot shows only while folded
|
|
441
|
+
}
|
|
442
|
+
```
|
|
423
443
|
|
|
424
444
|
```tsx
|
|
425
445
|
{
|
|
@@ -265,6 +265,16 @@ focus. Below 30rem of list width the "Sort:" prefix is visually hidden (the name
|
|
|
265
265
|
stays, and the button's accessible name is still "Sort: Name, Ascending").
|
|
266
266
|
With no sortable column there is no control, and with neither a sortable column nor a `select` column there is no list bar.
|
|
267
267
|
|
|
268
|
+
For a short list the band is noise: `showSort={false}` drops the button (on a
|
|
269
|
+
phone too) and leaves every column sortable, so a
|
|
270
|
+
`layout={{ phone: 'cards', wide: 'table' }}` keeps its header sorting on desktop.
|
|
271
|
+
A list with a `select` column keeps its bar for "Select all". `DataTable` only:
|
|
272
|
+
`ServerTable` does not pass it through.
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
<DataTable layout="cards" showSort={false} renderCard={…} … />
|
|
276
|
+
```
|
|
277
|
+
|
|
268
278
|
## Columns
|
|
269
279
|
|
|
270
280
|
Tiles are always one column. For cards, the list's own width, not the window's, picks the columns: one, two from 36rem,
|
package/docs/guides/csp.md
CHANGED
|
@@ -84,9 +84,10 @@ A virtual `DataTable` (`virtual`) adds no `style` attribute either: the
|
|
|
84
84
|
server renders its first rows as plain rows, and the spacer rows' heights
|
|
85
85
|
are custom properties written through `style.setProperty` after mount,
|
|
86
86
|
which `style-src-attr` does not govern (`StrictCspVirtual` renders one under
|
|
87
|
-
`style-src-attr 'none'`). Its scroll region's `maxHeight` is
|
|
88
|
-
`style` on the server, one of #596's
|
|
89
|
-
`
|
|
87
|
+
`style-src-attr 'none'`). Its scroll region's `maxHeight` is an inline
|
|
88
|
+
`style` on the server, one of #596's: give `maxHeightClassName`
|
|
89
|
+
(`maxHeightClassName="max-h-[28rem]"`, as `DataGrid` has) instead, and the
|
|
90
|
+
region has no `style` attribute.
|
|
90
91
|
|
|
91
92
|
A keyline `Carousel` (`multi-browse`, `hero`) renders no `style` attribute
|
|
92
93
|
either: its first frame comes from container-query CSS, and the masks that
|
package/docs/guides/data-grid.md
CHANGED
|
@@ -1123,10 +1123,11 @@ A strict policy works with no `style-src-attr 'unsafe-inline'` for the grid:
|
|
|
1123
1123
|
the CSSOM, which `style-src-attr` does not govern. A frozen column's drag
|
|
1124
1124
|
rewrites `--dg-start-*` the same way.
|
|
1125
1125
|
- A custom editor must follow the same rule: classes only.
|
|
1126
|
-
- `DataTable`'s own `maxHeight`
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
`
|
|
1126
|
+
- `DataTable`'s own `maxHeight` is an inline `style={{ maxHeight }}` on its
|
|
1127
|
+
scroll region, so it is not covered by this list; the grid takes a class
|
|
1128
|
+
instead. A virtual `DataTable` under a strict CSP takes
|
|
1129
|
+
`maxHeightClassName` (a class such as `max-h-[28rem]`, no inline style;
|
|
1130
|
+
[#800](https://github.com/softistx/nxgt-material/issues/800)).
|
|
1130
1131
|
|
|
1131
1132
|
The grid's popups (the select's list, the date's calendar, the menus) are
|
|
1132
1133
|
library components and add nothing to the policy: see the
|