@nxgt/material 3.101.0 → 3.104.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 +82 -0
- package/README.md +37 -5
- package/dist/{alert-dialog-DYzxchiS.js → alert-dialog-CK_47jfm.js} +1 -1
- package/dist/{async-slice-drJyPvuM.js → async-slice-1ZNMiwyJ.js} +1 -1
- package/dist/{bubble-input-CQwVSoU3.js → bubble-input-BxI1WmNs.js} +17 -5
- package/dist/{button-GeIsUekU.js → button-YfEMZbRO.js} +2 -2
- package/dist/{calculator-BS4PAm6l.js → calculator-BmYZId4c.js} +261 -460
- package/dist/calculator-field.js +7 -7
- package/dist/{calculator-segment-CEdwE24M.js → calculator-segment-DJ08DJ-n.js} +1 -1
- package/dist/calculator.js +1 -1
- package/dist/charts.js +819 -764
- package/dist/{chip-BdEtFQ85.js → chip-DPj6e-3v.js} +2 -2
- package/dist/{code-editor-BByXZOGy.js → code-editor-CZg3nLZO.js} +178 -177
- package/dist/code-editor.js +5 -5
- package/dist/{code-language-items-CSZX5zxD.js → code-language-items-Nq1iOGId.js} +14 -5
- package/dist/components/ui/actions/action-card/action-card-tile.css +47 -0
- package/dist/components/ui/display/chip/chip-soft.css +27 -2
- package/dist/components/ui/display/code/code-moonlight.css +10 -10
- package/dist/components/ui/primitives/collapsible/collapsible.css +9 -0
- package/dist/{components-DQ4MljCr.js → components-B2wrH_W0.js} +19302 -18583
- package/dist/components.js +38 -37
- package/dist/{conversion-variant-DTQqAyDt.js → conversion-variant-C-LPx5ek.js} +5 -5
- package/dist/{currency-field-Dnju7ss2.js → currency-field-DnsRIA80.js} +24 -23
- package/dist/{dialog-5_2__tu8.js → dialog-BKBsDGFc.js} +3 -3
- package/dist/dnd.js +1 -1
- package/dist/{dropdown-menu-FdGC5kIP.js → dropdown-menu-QMDbRidH.js} +1 -1
- package/dist/editor.js +9 -9
- package/dist/{empty-state-BoepPG5v.js → empty-state-BLIJ6znY.js} +1 -1
- package/dist/{field-blur-DkiZOVNV.js → field-blur-DMJwPLsg.js} +9 -9
- package/dist/field-blur-context-3rKussBQ.js +29 -0
- package/dist/{financial-variant-BhqJp5C-.js → financial-variant-Ws0Jp01H.js} +5 -5
- package/dist/{hooks-DjcbJZiO.js → hooks-C3vkQmzh.js} +106 -61
- package/dist/hooks.js +3 -3
- package/dist/{hover-card-CmHJeBeY.js → hover-card-Df649U6I.js} +1 -1
- package/dist/i18n.js +3 -3
- package/dist/{icon-badge-LGUPQh_c.js → icon-badge-Cma1qOOJ.js} +1 -1
- package/dist/{icon-button-BSVpO1jP.js → icon-button-CpKxV264.js} +1 -1
- package/dist/key-layout-D2goCDkD.js +31 -0
- package/dist/keypad.js +551 -0
- package/dist/label-DR-zZyGQ.js +47 -0
- 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/display/code/code-block-parts.d.ts +1 -1
- package/dist/lib/components/ui/display/code/code-block.d.ts +4 -1
- package/dist/lib/components/ui/display/code/languages.d.ts +2 -0
- package/dist/lib/components/ui/display/code/moonlight-tokens.d.ts +10 -10
- package/dist/lib/components/ui/feedback/confirm-dialog/confirm-dialog-props.d.ts +73 -0
- package/dist/lib/components/ui/feedback/confirm-dialog/confirm-dialog-rules.d.ts +12 -0
- package/dist/lib/components/ui/feedback/confirm-dialog/confirm-dialog.d.ts +5 -61
- package/dist/lib/components/ui/feedback/confirm-dialog/confirm-footer.d.ts +12 -0
- package/dist/lib/components/ui/feedback/confirm-dialog/confirm-phrase.d.ts +26 -0
- package/dist/lib/components/ui/feedback/confirm-dialog/use-confirm-action.d.ts +1 -1
- 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/index.d.ts +1 -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/index.d.ts +1 -0
- package/dist/lib/components/ui/layout/panel-card/panel-card.d.ts +23 -0
- 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/selection/index.d.ts +1 -0
- package/dist/lib/components/ui/selection/status-menu/index.d.ts +3 -0
- package/dist/lib/components/ui/selection/status-menu/partials/status-confirm.d.ts +12 -0
- package/dist/lib/components/ui/selection/status-menu/partials/status-icon.d.ts +6 -0
- package/dist/lib/components/ui/selection/status-menu/partials/status-items.d.ts +18 -0
- package/dist/lib/components/ui/selection/status-menu/partials/status-menu-list.d.ts +14 -0
- package/dist/lib/components/ui/selection/status-menu/partials/status-sheet-row.d.ts +11 -0
- package/dist/lib/components/ui/selection/status-menu/partials/status-sheet.d.ts +21 -0
- package/dist/lib/components/ui/selection/status-menu/partials/status-trigger.d.ts +18 -0
- package/dist/lib/components/ui/selection/status-menu/status-menu-data.d.ts +12 -0
- package/dist/lib/components/ui/selection/status-menu/status-menu-form-field.d.ts +20 -0
- package/dist/lib/components/ui/selection/status-menu/status-menu.d.ts +13 -0
- package/dist/lib/components/ui/selection/status-menu/types.d.ts +76 -0
- package/dist/lib/components/ui/selection/status-menu/use-status-change.d.ts +18 -0
- package/dist/lib/components/ui/selection/status-menu/use-status-menu.d.ts +30 -0
- package/dist/lib/components/ui/widgets/calculator/partials/calculator-keypad.d.ts +1 -5
- package/dist/lib/components/ui/widgets/calculator/variants/key-layout.d.ts +3 -49
- package/dist/lib/components/ui/widgets/keypad/cash-model.d.ts +16 -0
- package/dist/lib/components/ui/widgets/keypad/index.d.ts +5 -0
- package/dist/lib/components/ui/widgets/keypad/key-layout.d.ts +56 -0
- package/dist/lib/components/ui/widgets/keypad/keypad-binding.d.ts +32 -0
- package/dist/lib/components/ui/widgets/keypad/keypad-cash.d.ts +28 -0
- package/dist/lib/components/ui/widgets/keypad/keypad-display.d.ts +11 -0
- package/dist/lib/components/ui/widgets/keypad/keypad-entry.d.ts +39 -0
- package/dist/lib/components/ui/widgets/keypad/keypad-grid.d.ts +27 -0
- package/dist/lib/components/ui/widgets/{calculator/partials/calculator-key.d.ts → keypad/keypad-key.d.ts} +6 -3
- package/dist/lib/components/ui/widgets/keypad/keypad-layout.d.ts +19 -0
- package/dist/lib/components/ui/widgets/{calculator/partials → keypad}/keypad-navigation.d.ts +1 -1
- package/dist/lib/components/ui/widgets/keypad/keypad.d.ts +9 -0
- package/dist/lib/components/ui/widgets/keypad/keypad.types.d.ts +91 -0
- package/dist/lib/components/ui/widgets/keypad/use-keypad-cash.d.ts +14 -0
- package/dist/lib/components/ui/widgets/keypad/use-keypad-keyboard.d.ts +7 -0
- package/dist/lib/components/ui/widgets/keypad/use-keypad-number.d.ts +10 -0
- package/dist/lib/components/ui/widgets/keypad/use-keypad-quantity.d.ts +16 -0
- package/dist/lib/components/ui/widgets/keypad/use-keypad-value.d.ts +17 -0
- package/dist/lib/components/ui/widgets/keypad/use-keypad.d.ts +23 -0
- 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/hooks/index.d.ts +1 -0
- package/dist/lib/hooks/use-barcode-scanner.d.ts +39 -0
- package/dist/lib/i18n/resources/en/en.d.ts +28 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +30 -2
- package/dist/lib/i18n/resources/fr/fr.d.ts +28 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +30 -2
- package/dist/lib/i18n/resources/resources.d.ts +56 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib/keypad/index.d.ts +20 -0
- 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 -53
- package/dist/map.js +9 -9
- package/dist/material.css +1 -1
- package/dist/pdf-reader.js +1 -1
- package/dist/{percent-field-DGlU_xii.js → percent-field-sAq7BaxN.js} +15 -14
- package/dist/{picker-surface-CGhqNR8a.js → picker-surface--6E-nvpU.js} +4 -4
- package/dist/{popover-ZxL7ktKk.js → popover-bEB8I_-w.js} +1 -1
- package/dist/{pos-types-BQ0QpYMp.js → pos-types-Cpdc4E33.js} +1 -1
- package/dist/{pos-variant-CKy8h_5i.js → pos-variant-ClMGu59I.js} +25 -25
- package/dist/print.js +146 -127
- package/dist/{programmer-variant-CbHstE28.js → programmer-variant-ChzgyA7x.js} +30 -30
- package/dist/{public-D-tdOz4b.js → public-BY8N5NDT.js} +9 -9
- package/dist/{rich-code-block-CTPj1LuD.js → rich-code-block-D2GiWuqD.js} +18 -18
- package/dist/{scientific-variant-B8U9h3II.js → scientific-variant-31_93gcZ.js} +8 -8
- package/dist/{sortable-provider-CSoWE7Ce.js → sortable-provider-DGiVn72t.js} +2 -2
- package/dist/{spinner-ffuwBXHl.js → spinner-CEVomQ5K.js} +1 -1
- package/dist/standard-layout-DTr9yy8Z.js +48 -0
- package/dist/{tabs-JgRq6ZJx.js → tabs-B8MXBbPW.js} +14 -14
- package/dist/{text-field-DURkIP-v.js → text-field-DN88qD0Q.js} +16 -15
- package/dist/theme-picker.js +13 -13
- package/dist/{theme-toggle-n_Zg7JAA.js → theme-toggle-C7pnu1Xh.js} +1 -1
- package/dist/{toolbar-items-IANZdcw4.js → toolbar-items-BDfoh8QB.js} +4 -4
- package/dist/{translate-material-CMHUH5Ha.js → translate-material-s_lxIV43.js} +1 -1
- package/dist/{translator-9zA-Um3W.js → translator-Cgql9srO.js} +56 -4
- package/dist/use-calculator-keyboard-DysnFK8m.js +222 -0
- package/dist/{use-chat-composer-BoIN7RCA.js → use-chat-composer-xCcp9fVc.js} +2250 -2195
- package/dist/{use-heading-spy-Ciw9GBHL.js → use-heading-spy-FIZlf9qj.js} +235 -154
- package/dist/{use-material-translation-CLNaMhUf.js → use-material-translation-xNzbSVgS.js} +1 -1
- package/dist/{use-return-focus-DWhbTGVe.js → use-return-focus-CW9KkDkp.js} +1 -1
- package/docs/README.md +2 -1
- package/docs/components/README.md +2 -0
- package/docs/components/buttons-and-actions.md +81 -2
- package/docs/components/data-display.md +6 -3
- package/docs/components/data-table-filter-and-lists.md +19 -6
- package/docs/components/feedback-and-status.md +20 -0
- package/docs/components/form-fields.md +60 -7
- package/docs/components/navigation-and-layout.md +48 -2
- package/docs/components/primitives.md +44 -2
- package/docs/components/rich-widgets.md +28 -0
- package/docs/components/selection-and-choice.md +176 -1
- package/docs/guides/calculator.md +1 -1
- package/docs/guides/cards-and-tiles.md +31 -24
- 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/forms.md +1 -0
- package/docs/guides/helpers.md +49 -0
- package/docs/guides/keypad.md +233 -0
- package/docs/guides/printing.md +51 -1
- package/docs/previews/buttons-actions-producttile.webp +0 -0
- package/docs/previews/data-display-codeblock.webp +0 -0
- package/docs/previews/data-display-codegroup.webp +0 -0
- package/docs/previews/data-display-jsonviewer.webp +0 -0
- package/docs/previews/form-fields-quantitystepper.webp +0 -0
- package/docs/previews/navigation-layout-panelcard.webp +0 -0
- package/docs/previews/rich-widgets-keypad.webp +0 -0
- package/docs/previews/selection-choice-statusmenu.webp +0 -0
- package/docs/roadmap.md +21 -0
- package/docs/troubleshooting.md +68 -4
- package/lib/components/ui/actions/action-card/action-card-tile.css +47 -0
- package/lib/components/ui/display/chip/chip-soft.css +27 -2
- package/lib/components/ui/display/code/code-moonlight.css +10 -10
- 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 +118 -0
- package/package.json +7 -2
- package/dist/field-blur-context-BLGgf_3z.js +0 -73
- package/dist/key-layout-cR79xfGE.js +0 -38
- package/dist/standard-layout-BuSRr3TY.js +0 -40
|
@@ -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
|
@@ -11,6 +11,7 @@ detail: every option, the second example, and the traps.
|
|
|
11
11
|
| [Themes for your app](guides/themes.md) | You define your own themes: `defineTheme` modules, the Vite plugin's `themes` option, the `nxgt-theme` command and its CI check, the tone warnings |
|
|
12
12
|
| [Barcodes and label sheets](guides/barcode.md) | You draw a Code 128 or Data Matrix code and print a sheet of labels with `@nxgt/material/barcode`: `Barcode`, `LabelSheet` and `Label`, label size, columns, rows, gaps and a start offset, dark mode, print and accessibility |
|
|
13
13
|
| [Calculator](guides/calculator.md) | You add an exact-decimal calculator with `@nxgt/material/calculator` (and `CalculatorField` with `@nxgt/material/calculator-field`): `Calculator`, the keyboard, the tape and memory, copy and paste, what is stored, feedback, sizing and accessibility |
|
|
14
|
+
| [Keypad](guides/keypad.md) | You add a numeric keypad outside the calculator with `@nxgt/material/keypad`: quantity, price, percentage and cash-tendered entry, decimal or minor-unit digits, calculation keys, field bindings (`useKeypadNumber`, `useKeypadQuantity`), the cash variant with change and bill shortcuts, the keyboard and accessibility |
|
|
14
15
|
| [Charts](guides/charts.md) | You draw charts with `@nxgt/material/charts`: each chart, the shared props, reference bands, thresholds and point tones, zoom and phone scroll, the keyboard, accessibility, theming the `--chart-1..5` tokens, and moving off the Nivo charts, removed in 3.0 |
|
|
15
16
|
| [Cards and tiles](guides/cards-and-tiles.md) | You want a `DataTable` as a list of cards or tiles: `layout`, `renderCard`, `renderTile`, the card's one link, selection, the sort control, columns by the list's width |
|
|
16
17
|
| [EventCalendar](guides/event-calendar.md) | You mark periods with `spans`, label days and weeks with `dayBadges` and `weekBadges`, colour events with `tone`, cancel them with `status`, or make them read-only with `readOnlyEvents` |
|
|
@@ -21,7 +22,7 @@ detail: every option, the second example, and the traps.
|
|
|
21
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 |
|
|
22
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 |
|
|
23
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 |
|
|
24
|
-
| [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` |
|
|
25
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) |
|
|
26
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 |
|
|
27
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
|
|
|
@@ -65,6 +66,7 @@ Picking among options: select, combobox, autocomplete, checkbox, radio, card and
|
|
|
65
66
|
[<img src="../previews/selection-choice-autocomplete.webp" height="72" alt="Autocomplete">](selection-and-choice.md#autocomplete)
|
|
66
67
|
[<img src="../previews/selection-choice-choicecardgroup.webp" height="72" alt="ChoiceCardGroup">](selection-and-choice.md#choicecardgroup)
|
|
67
68
|
[<img src="../previews/selection-choice-transferlist.webp" height="72" alt="TransferList">](selection-and-choice.md#transferlist)
|
|
69
|
+
[<img src="../previews/selection-choice-statusmenu.webp" height="72" alt="StatusMenu">](selection-and-choice.md#statusmenu)
|
|
68
70
|
|
|
69
71
|
## [Data display](data-display.md)
|
|
70
72
|
|
|
@@ -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
|
|
|
@@ -1199,7 +1200,7 @@ import { Banknote, CreditCard } from 'lucide-react';
|
|
|
1199
1200
|
| `title`, `description`, `icon` | `ReactNode` | — | the content |
|
|
1200
1201
|
| `status` | `ReactNode` | — | a line under the title; a string or number becomes a soft `Badge`, a node is shown as given |
|
|
1201
1202
|
| `meta` | `ReactNode` | — | a secondary line, one line with an ellipsis |
|
|
1202
|
-
| `action` | `ReactNode` | — | a trailing control outside the press surface, see [tile](#actioncard-tile); `title` is then required, a `role` moves to the title button; the card's `disabled` does not reach it, disable its control too |
|
|
1203
|
+
| `action` | `ReactNode` | — | a trailing control outside the press surface, see [tile](#actioncard-tile); `title` is then required, a `role` moves to the title button; the card's `disabled` does not reach it, disable its control too; in a `tile` it wraps under a narrow title |
|
|
1203
1204
|
|
|
1204
1205
|
A card with `active`, `onClick`, `disabled` or your own `role` is `role="button"` (unless you set another): it takes the Tab stop, and
|
|
1205
1206
|
Enter and Space press it. `active` sets `aria-pressed`; pass your own `role`
|
|
@@ -1218,7 +1219,39 @@ For a card choice bound to a form value, see `ChoiceCardGroup` on
|
|
|
1218
1219
|
`variant="tile"` is a compact grid cell for boards: no minimum width (it fills
|
|
1219
1220
|
its grid track), the title and the `action` on the first row, then `status`,
|
|
1220
1221
|
`description` and `meta`, and no indicator unless `withIndicator`. `status` and
|
|
1221
|
-
`meta` work on every variant.
|
|
1222
|
+
`meta` work on every variant. The `action` wraps under a narrow title instead of
|
|
1223
|
+
overflowing the tile.
|
|
1224
|
+
|
|
1225
|
+
A status that changes in place is a [`StatusMenu`](selection-and-choice.md#statusmenu)
|
|
1226
|
+
in `action`: the chip shows the status (so the card needs no `status` badge) and
|
|
1227
|
+
opens the choices, in a bottom sheet on a phone. Its button is named "Status of
|
|
1228
|
+
Unit 21: Free". The chip is 6 to 10rem wide (a long status word is wider). A tile
|
|
1229
|
+
with an `action` is a wrapping line: the title may shrink to 4.5rem
|
|
1230
|
+
(`--ac-title-min`), and an action that does not fit beside it drops under the
|
|
1231
|
+
title, so a chip fits in a two-per-row `xs` grid on a 360px phone and an icon
|
|
1232
|
+
button such as `RowMenu` stays beside the title. A wrapped action starts at the tile's line start (under the icon when the tile
|
|
1233
|
+
has one), not under the title text.
|
|
1234
|
+
|
|
1235
|
+
```tsx
|
|
1236
|
+
import { ActionCard, StatusMenu } from '@nxgt/material/components';
|
|
1237
|
+
|
|
1238
|
+
<ActionCard
|
|
1239
|
+
variant="tile"
|
|
1240
|
+
title="Unit 21"
|
|
1241
|
+
meta="Zone B, second floor"
|
|
1242
|
+
onClick={() => openDetail(unit.id)}
|
|
1243
|
+
action={
|
|
1244
|
+
<StatusMenu
|
|
1245
|
+
label="Unit 21"
|
|
1246
|
+
value={unit.status}
|
|
1247
|
+
choices={statuses}
|
|
1248
|
+
onSelect={(next) => saveStatus(unit.id, next)} // a promise keeps the old status with a spinner
|
|
1249
|
+
/>
|
|
1250
|
+
}
|
|
1251
|
+
/>;
|
|
1252
|
+
```
|
|
1253
|
+
|
|
1254
|
+
A compact icon-button action, with a `status` badge, uses `RowMenu`:
|
|
1222
1255
|
|
|
1223
1256
|
```tsx
|
|
1224
1257
|
import {
|
|
@@ -1303,6 +1336,52 @@ A whole board built on it, with sections (each its own h2 and free count) and, w
|
|
|
1303
1336
|
|
|
1304
1337
|
---
|
|
1305
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
|
+
|
|
1306
1385
|
## ThemeToggle
|
|
1307
1386
|
|
|
1308
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>` |
|
|
@@ -1652,7 +1652,7 @@ The colour shows as a thin accent bar inset from the chip's edges (never a borde
|
|
|
1652
1652
|
|
|
1653
1653
|

|
|
1654
1654
|
|
|
1655
|
-
Highlighted code with line numbers and copy, a collapsible JSON tree, and tabbed samples (`Tabs`, underlined).
|
|
1655
|
+
Highlighted code (or plain text) with line numbers and copy, a collapsible JSON tree, and tabbed samples (`Tabs`, underlined).
|
|
1656
1656
|
|
|
1657
1657
|
<img src="../previews/data-display-codegroup.webp" height="96" alt="CodeGroup">
|
|
1658
1658
|
<img src="../previews/data-display-jsonviewer.webp" height="96" alt="JsonViewer">
|
|
@@ -1661,6 +1661,7 @@ Highlighted code with line numbers and copy, a collapsible JSON tree, and tabbed
|
|
|
1661
1661
|
import { CodeBlock, CodeGroup, JsonViewer } from '@nxgt/material/components';
|
|
1662
1662
|
|
|
1663
1663
|
<CodeBlock code={source} language="typescript" filename="greet.ts" highlightLines={[2]} />
|
|
1664
|
+
<CodeBlock code={dnsRecord} language="text" label={false} />
|
|
1664
1665
|
<CodeGroup tabs={[
|
|
1665
1666
|
{ label: 'bun', language: 'bash', code: 'bun add @nxgt/material' },
|
|
1666
1667
|
{ label: 'npm', language: 'bash', code: 'npm i @nxgt/material' },
|
|
@@ -1671,6 +1672,8 @@ import { CodeBlock, CodeGroup, JsonViewer } from '@nxgt/material/components';
|
|
|
1671
1672
|
| Prop | Type | Default | Effect |
|
|
1672
1673
|
| --- | --- | --- | --- |
|
|
1673
1674
|
| `scheme` | `'dark' \| 'light'` | follows the theme | forces the palette (all three) |
|
|
1675
|
+
| `language` | `string` | `typescript` | `CodeBlock`: one of 16 ids or aliases, or `text` (also `plaintext`, `plain`, `txt`) to show the code as is, labelled "Text". An unknown id still falls back to `typescript` |
|
|
1676
|
+
| `label` | `ReactNode \| false` | the language's name | `CodeBlock`: replaces the language label; `false` hides it |
|
|
1674
1677
|
| `showLineNumbers` | `boolean` | `true` | `CodeBlock`, `CodeGroup` |
|
|
1675
1678
|
| `highlightLines` | `number[]` | — | `CodeBlock`: marked lines |
|
|
1676
1679
|
| `maxHeight` | `number \| string` | — | `CodeBlock`, `JsonViewer`: scrolls past it |
|
|
@@ -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`,
|
|
@@ -250,6 +250,8 @@ function DeleteProject({ onDelete }: { onDelete: () => Promise<void> }) {
|
|
|
250
250
|
| `details` | `ReactNode` | — | a note under the description, read with it by assistive technology, for what else is affected or cannot be undone |
|
|
251
251
|
| `refusal` | `(refusal: ConfirmRefusalContext) => ReactNode` | the message; an `ActionRefusal`'s details and action too | renders the refusal inside the error alert (still `role="alert"`, a `div`) instead of the default, so it can carry a link. Receives `{ message, cause, close }`: `message` is the thrown error's message or the translated `material.confirm-dialog.failed` text, `cause` is what `onConfirm` threw, `close()` closes the dialog |
|
|
252
252
|
| `confirmLabel` / `cancelLabel` | `string` | "Confirm" / "Cancel", translated (`material.confirm-dialog.confirm` / `.cancel`) | button text |
|
|
253
|
+
| `confirmPhrase` | `string` | — | asks the user to type this text before Confirm enables, for a deletion worth a second thought. A field labelled by the translated `material.confirm-dialog.type-phrase` ("Type {phrase} to confirm", the phrase in code style) takes the focus on open instead of Cancel and starts empty each time the dialog opens. Paste is allowed; Enter in the field confirms only once it matches. It combines with `confirmDisabled` (both must allow). Empty or unset: no field |
|
|
254
|
+
| `confirmPhraseMatch` | `'exact' \| 'trim' \| 'trim-case-insensitive'` | `'exact'` | how the typed text is compared: character for character, ignoring spaces around it, or ignoring those and the letter case |
|
|
253
255
|
| `cancelVariant` | `ButtonVariant` | `'tonal'` | the Cancel button's variant; `'outlined'` restores the earlier look |
|
|
254
256
|
| `onCloseAutoFocus` | `(event: Event) => void` | — | called as the dialog closes, before the focus goes back; `event.preventDefault()` places it yourself, e.g. `ctx.focusTrigger()` of a `ServerTable` bulk action whose button a redraw replaced |
|
|
255
257
|
|
|
@@ -269,6 +271,24 @@ function DeleteProject({ onDelete }: { onDelete: () => Promise<void> }) {
|
|
|
269
271
|
/>
|
|
270
272
|
```
|
|
271
273
|
|
|
274
|
+
**Type the value to confirm.** For a deletion that should not happen by habit (a domain, a mailbox), ask for the name:
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
<ConfirmDialog
|
|
278
|
+
open={open}
|
|
279
|
+
onOpenChange={setOpen}
|
|
280
|
+
variant="destructive"
|
|
281
|
+
title="Delete example.com?"
|
|
282
|
+
description="Its mailboxes are deleted with it."
|
|
283
|
+
confirmLabel="Delete"
|
|
284
|
+
confirmPhrase="example.com"
|
|
285
|
+
confirmPhraseMatch="trim-case-insensitive"
|
|
286
|
+
onConfirm={remove}
|
|
287
|
+
/>
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`ConfirmDialog` has no form; `ConfirmationDialog` (with `formOptions`) does not take `confirmPhrase`. The Vue twin does not have it yet.
|
|
291
|
+
|
|
272
292
|
**The text stays while the dialog fades out.** A confirm is usually driven by a nullable subject, and closing clears it: `open={item !== null}`. `ConfirmDialog` keeps the `title`, `description`, `details`, button labels and `variant` of its last open render until it has closed, so the title does not read "Delete ?" during the exit animation. Nothing to add in the app:
|
|
273
293
|
|
|
274
294
|
```tsx
|
|
@@ -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
|

|
|
@@ -22,6 +22,7 @@ documentation-site shell. Everything is imported from
|
|
|
22
22
|
| <img src="../previews/navigation-layout-dashboardgrid.webp" width="96" alt=""> | [DashboardGrid, DashboardTile](#dashboardgrid) | Tiles with spans that the user rearranges in an edit mode, by drag or keyboard; the order is the app's |
|
|
23
23
|
| <img src="../previews/navigation-layout-editorlayout.webp" width="96" alt=""> | [EditorLayout](#editorlayout) | The editor page: toolbar, canvas that scrolls alone, properties at the end (a bottom sheet on a phone), optional outline at the start |
|
|
24
24
|
| <img src="../previews/navigation-layout-sectioncolumns.webp" width="96" alt=""> | [SectionColumns](#sectioncolumns) | Short page sections side by side from a viewport breakpoint, stacked below it |
|
|
25
|
+
| <img src="../previews/navigation-layout-panelcard.webp" width="96" alt=""> | [PanelCard](#panelcard) | Titled card for a side panel: heading, description, header actions |
|
|
25
26
|
| <img src="../previews/navigation-layout-recordpage.webp" width="96" alt=""> | [RecordPage, RecordDetails](#recordpage) | The page of one record: header card, content, facts as a side panel from `xl` |
|
|
26
27
|
| <img src="../previews/navigation-layout-resizable.webp" width="96" alt=""> | [ResizableGroup](#resizablegroup) | Resizable panels |
|
|
27
28
|
| <img src="../previews/navigation-layout-skiplink.webp" width="96" alt=""> | [SkipLink](#skiplink) | Skip-to-content link |
|
|
@@ -669,7 +670,7 @@ import { ListDetailsLayout, PaneLayout, ThreadLayout } from '@nxgt/material/comp
|
|
|
669
670
|
</ListDetailsLayout.Details>
|
|
670
671
|
</ListDetailsLayout>
|
|
671
672
|
|
|
672
|
-
<PaneLayout defaultOpen paneSide="right">
|
|
673
|
+
<PaneLayout defaultOpen="desktop" paneSide="right">
|
|
673
674
|
<PaneLayout.Content>
|
|
674
675
|
<PaneLayout.Trigger>Folders</PaneLayout.Trigger>
|
|
675
676
|
{messages}
|
|
@@ -715,10 +716,25 @@ takes over. A page rendered only in the browser starts on the real layout.
|
|
|
715
716
|
container and the flex row moves to an inner `data-slot="pane-layout-body"`.
|
|
716
717
|
- `paneWidth`: `'sm'` (16rem), `'md'` (20rem, default) or `'lg'` (24rem) for
|
|
717
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.
|
|
718
733
|
- With `phone` or `breakpoint` set, the inline pane is an `<aside>` named by
|
|
719
734
|
`sheetTitle` (default `Side Panel`), a landmark; the historical markup is a
|
|
720
735
|
`div`. `usePaneLayout()` and the function children also get `phone`,
|
|
721
736
|
`breakpoint` and `paneWidth`.
|
|
737
|
+
|
|
722
738
|
`ThreadLayout` is a centred column over the app
|
|
723
739
|
[background](#background) on the page ground (`bg-ground`, so a `bg-card` card stands apart) for focused flows (sign-in, onboarding), with no
|
|
724
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).
|
|
@@ -735,7 +751,8 @@ app bar. `width`, `stepperPlacement` and a sticky `footer` make it a centred for
|
|
|
735
751
|
| `history` / `historyParam` | `'push' \| 'replace'` / `string` | — / `'view'` | `ListDetailsLayout`: keep the view in the URL (`?view=details`), through React Router |
|
|
736
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 |
|
|
737
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 |
|
|
738
|
-
| `open` / `
|
|
754
|
+
| `open` / `onOpenChange` | `boolean` | `false` | `PaneLayout`: pane shown |
|
|
755
|
+
| `defaultOpen` | `boolean \| 'desktop' \| { desktop?, phone? }` | `false` | `PaneLayout`: initial pane state per viewport |
|
|
739
756
|
| `paneSide` | `'left' \| 'right'` | `'left'` | `PaneLayout` |
|
|
740
757
|
| `sheetTitle` | `string` | "Side Panel" (translated, `material['pane-layout']['sheet-title']`) | `PaneLayout.Pane`: screen-reader title of the sheet on phones |
|
|
741
758
|
| `header` | `ReactNode` | — | `ThreadLayout`: above the column |
|
|
@@ -1197,6 +1214,35 @@ props, so `className` adds to it.
|
|
|
1197
1214
|
|
|
1198
1215
|
**Storybook:** `Navigation & layout/SectionColumns` · **Vue:** later ([#1614](https://github.com/softistx/nxgt-material/issues/1614))
|
|
1199
1216
|
|
|
1217
|
+
## PanelCard
|
|
1218
|
+
|
|
1219
|
+
A titled card for the side of a page (an `ActivityContent` aside) or its sections: a heading, an optional one-line description and header actions over the content. It replaces the `Card` + `CardHeader` + `CardTitle as="h2"` + `CardDescription` + `CardContent` wrapper apps wrote themselves.
|
|
1220
|
+
|
|
1221
|
+
```tsx
|
|
1222
|
+
import { PanelCard } from '@nxgt/material/components';
|
|
1223
|
+
|
|
1224
|
+
<PanelCard
|
|
1225
|
+
title="Recent activity"
|
|
1226
|
+
description="The last five changes to this record."
|
|
1227
|
+
actions={<Button size="sm" variant="outlined">Refresh</Button>}
|
|
1228
|
+
>
|
|
1229
|
+
…
|
|
1230
|
+
</PanelCard>
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
| Prop | Type | Default | Effect |
|
|
1234
|
+
| --- | --- | --- | --- |
|
|
1235
|
+
| `title` | `ReactNode` | required | The heading, and the accessible name of the card |
|
|
1236
|
+
| `description` | `ReactNode` | none | One line under the title |
|
|
1237
|
+
| `headingLevel` | `2 \| 3 \| 4 \| 5 \| 6` | `2` | The title's heading level; use `3` inside a section that already has an `h2` |
|
|
1238
|
+
| `actions` | `ReactNode` | none | A slot at the end of the title row (a button, a `RowMenu`) |
|
|
1239
|
+
|
|
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.
|
|
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
|
+
|
|
1244
|
+
**Storybook:** `Navigation & layout/PanelCard` · **Vue:** later, [#2866](https://github.com/softistx/nxgt-material/issues/2866)
|
|
1245
|
+
|
|
1200
1246
|
---
|
|
1201
1247
|
|
|
1202
1248
|
## RecordPage
|
|
@@ -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 |
|
|
@@ -13,6 +13,7 @@ the app owns the data.
|
|
|
13
13
|
| <img src="../previews/rich-widgets-drawing.webp" width="96" alt=""> | [Drawing](#drawing) | Vector drawing from a scene of plain data or JSX primitives: theme colours, accessible, focusable nodes, strict-CSP safe |
|
|
14
14
|
| <img src="../previews/rich-widgets-barcode.webp" width="96" alt=""> | [Barcode](#barcode) | Code 128 and Data Matrix as accessible SVG, themed and dark-aware, black on white in print, with `LabelSheet` for a printed sheet of labels |
|
|
15
15
|
| <img src="../previews/rich-widgets-calculator.webp" width="96" alt=""> | [Calculator](#calculator) | Exact decimal calculator (Standard, Scientific, a point of sale, Financial, Conversion and Programmer) with the usual precedence, a shaped keypad, tape, memory, keyboard, copy and paste, and a number field whose button opens it (`CalculatorField`, in `@nxgt/material/calculator-field`), in its own entry; variants other than Standard load on demand |
|
|
16
|
+
| <img src="../previews/rich-widgets-keypad.webp" width="96" alt=""> | [Keypad](#keypad) | A touch and keyboard numeric keypad for a quantity, a price, a percentage or a cash tender with change (`@nxgt/material/keypad`), decimal or minor-unit entry, field bindings, in its own entry, styled by `calculator.css` |
|
|
16
17
|
| <img src="../previews/rich-widgets-emojipicker.webp" width="96" alt=""> | [EmojiPicker](#emojipicker) | Shared emoji picker and list |
|
|
17
18
|
| <img src="../previews/rich-widgets-mentionlist.webp" width="96" alt=""> | [MentionList](#mentionlist) | `@` mention list with local search |
|
|
18
19
|
| <img src="../previews/rich-widgets-documenteditor.webp" width="96" alt=""> | [DocumentEditor](#documenteditor) | Rich text editor for long-form writing, built on a headless core |
|
|
@@ -1012,6 +1013,33 @@ import { CalculatorField, CalculatorFormField } from '@nxgt/material/calculator-
|
|
|
1012
1013
|
|
|
1013
1014
|
See the [calculator guide](../guides/calculator.md).
|
|
1014
1015
|
|
|
1016
|
+
## Keypad
|
|
1017
|
+
|
|
1018
|
+
A numeric keypad from `@nxgt/material/keypad`, usable outside the Calculator:
|
|
1019
|
+
quantity, price, percentage and cash-tendered entry. The value is a canonical
|
|
1020
|
+
decimal string and the arithmetic is the calculator's exact engine. Digits go in
|
|
1021
|
+
as typed (`entry="decimal"`) or like a till (`entry="minor"`, `1 2 5` is `1.25`);
|
|
1022
|
+
`operations` adds calculation keys; `min` and `max` hold the value; the keyboard
|
|
1023
|
+
works when focus is in the keypad (`keyboard="global"` for a till). Its own entry,
|
|
1024
|
+
server-safe, styled by `@nxgt/material/calculator.css` (import it after
|
|
1025
|
+
`styles.css`; there is no keypad stylesheet).
|
|
1026
|
+
|
|
1027
|
+
```tsx
|
|
1028
|
+
import { Keypad, useKeypadNumber } from '@nxgt/material/keypad';
|
|
1029
|
+
|
|
1030
|
+
<Keypad value={qty} onValueChange={setQty} onCommit={(value) => addLine(value)} />
|
|
1031
|
+
const binding = useKeypadNumber({ value: qty, onChange: setQty, min: 1 });
|
|
1032
|
+
<Keypad binding={binding} />
|
|
1033
|
+
<Keypad variant="cash" total="17.35" denominations={[5, 10, 20, 50]} currency="EUR" onTender={pay} />
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
`useKeypadNumber` binds it to a `NumberField`, `PercentField` or `CurrencyField`
|
|
1037
|
+
(also with `minorUnits`), `useKeypadQuantity` to a `QuantityField`; the bound
|
|
1038
|
+
keypad hides its own display unless `operations` are offered. The `cash` variant shows the total, the change or
|
|
1039
|
+
the amount still due, bill shortcuts from `denominations`, and reports
|
|
1040
|
+
`onTender(tendered, settlement)`; your app owns the charge. See the
|
|
1041
|
+
[keypad guide](../guides/keypad.md).
|
|
1042
|
+
|
|
1015
1043
|
## EmojiPicker
|
|
1016
1044
|
|
|
1017
1045
|

|