@nxgt/material 2.4.2 → 2.6.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/README.md +28 -21
- package/dist/{_Uint8Array-DEXn42kj.js → _Uint8Array-CDlDeQmw.js} +1 -1
- package/dist/_overRest-CWIWQwDJ.js +81 -0
- package/dist/{abnfDiagram-O67JEVCF-C7NxOWqd.js → abnfDiagram-O67JEVCF-Cl4ukf6a.js} +4 -4
- package/dist/{architectureDiagram-NJMV4G6O-BIvwyzEh.js → architectureDiagram-NJMV4G6O-WmFlBQF5.js} +7 -7
- package/dist/{client-only.utils-DhcBUmau.js → async-slice-Qoen0Mqm.js} +164 -93
- package/dist/{blockDiagram-BEXU5L5S-CzgMFdtn.js → blockDiagram-BEXU5L5S-_6EVgQ2-.js} +11 -11
- package/dist/{c4Diagram-YGBWAQC7-WgqlRIL5.js → c4Diagram-YGBWAQC7-D9xSt2QO.js} +8 -8
- package/dist/{channel-Ba8pEAWx.js → channel-Dg92Hoym.js} +1 -1
- package/dist/{chunk-3FUC2YCW-DnIzetwB.js → chunk-3FUC2YCW-CRQHl-Jx.js} +8 -8
- package/dist/{chunk-5DYCD2WN-Bqi3iL5i.js → chunk-5DYCD2WN-Cja1INgd.js} +3 -3
- package/dist/{chunk-6AEJRKK7-DDXoGWDs.js → chunk-6AEJRKK7-3YWRP4aV.js} +2 -2
- package/dist/{chunk-742MDFTN-nG_ROTnT.js → chunk-742MDFTN-Cs1qGiTK.js} +2 -2
- package/dist/{chunk-7INBJB4K-Dg64CbhT.js → chunk-7INBJB4K-BY5n77ka.js} +6 -6
- package/dist/{chunk-7M6MHVWA-CPdb6LVf.js → chunk-7M6MHVWA-DqXpf6CM.js} +9 -9
- package/dist/{chunk-7PRAP22T-DIiYRAce.js → chunk-7PRAP22T-_Av9vcXz.js} +1 -1
- package/dist/{chunk-GNY47TPC-CxWZwDbp.js → chunk-GNY47TPC-BgMsLbCS.js} +11 -11
- package/dist/{chunk-GTNCS2PH-CzDYTYNy.js → chunk-GTNCS2PH-BZ9WMD1v.js} +3 -3
- package/dist/{chunk-GWA4HPMP-CmsxMX3A.js → chunk-GWA4HPMP-Co-A6dtV.js} +3 -3
- package/dist/{chunk-MBY4JIJT-IA2XBafa.js → chunk-MBY4JIJT-C3RQP_Nk.js} +4 -4
- package/dist/{chunk-NETBCI7D-Cq92-0cA.js → chunk-NETBCI7D-CsyH43bj.js} +1 -1
- package/dist/{chunk-O7XYJQB3-C87zK8E5.js → chunk-O7XYJQB3-A66QSOPA.js} +1 -1
- package/dist/{chunk-UA2S7LBM-B0LGFGu3.js → chunk-UA2S7LBM-BNeHeMx9.js} +6 -6
- package/dist/{chunk-WEXAMYUT-C-QP0DFq.js → chunk-WEXAMYUT-CucJfehv.js} +2 -2
- package/dist/{chunk-XXDRQBXY-Bq4sFnO2.js → chunk-XXDRQBXY-FgXU14Bh.js} +1 -1
- package/dist/{chunk-Z7XXMR3K-vh8Pvgr_.js → chunk-Z7XXMR3K-CoCjTVVx.js} +8 -8
- package/dist/{chunk-ZIGJFQKS-HDJ6e6OJ.js → chunk-ZIGJFQKS-C3xUAXJq.js} +3 -3
- package/dist/{classDiagram-v2-NBCMYWYE-BSyQx7Df.js → classDiagram-v2-NBCMYWYE-j4vk04t-.js} +14 -14
- package/dist/{components-C3jpOwPw.js → components-C3wIUrWw.js} +26931 -25668
- package/dist/components.js +6 -6
- package/dist/{cose-bilkent-JH36ORCC-11-T1rfk.js → cose-bilkent-JH36ORCC-DRUT3qBb.js} +2 -2
- package/dist/{cynefinDiagram-VND7K2PF-1kHi2rGa.js → cynefinDiagram-VND7K2PF-B9HrdQW8.js} +4 -4
- package/dist/{dagre-6A5THRUB-BDk37t1_.js → dagre-6A5THRUB-DXq_YBNL.js} +572 -613
- package/dist/{diagram-22UHCM2B-BvH24G-C.js → diagram-22UHCM2B-He8NDldw.js} +15 -15
- package/dist/{diagram-3UASUU5V-DAKVPT0Q.js → diagram-3UASUU5V-CeILva4U.js} +7 -7
- package/dist/{diagram-ATOU4E4O-Clju_0RY.js → diagram-ATOU4E4O-CNY-8kl0.js} +3 -3
- package/dist/{diagram-CDSNMT55-w7kkJ6MU.js → diagram-CDSNMT55-C8Mk1smi.js} +5 -5
- package/dist/{diagram-MLGK6HIB-mUOtRVmm.js → diagram-MLGK6HIB-CpfeILwW.js} +4 -4
- package/dist/{diagram-MPIPVDR6-DEUw58dq.js → diagram-MPIPVDR6-Do7bUnTd.js} +4 -4
- package/dist/{dist-gYpAi-su.js → dist-C3YK_ean.js} +1 -1
- package/dist/{dist-CeHNxLJG.js → dist-CRbPtqwD.js} +1 -1
- package/dist/dnd.js +1 -1
- package/dist/{ebnfDiagram-ZINNZB2B-BYfatie5.js → ebnfDiagram-ZINNZB2B-1GewX9Zq.js} +4 -4
- package/dist/{elk-276RUBZZ-CtsHzWCy.js → elk-276RUBZZ-DFn4kTey.js} +13 -13
- package/dist/{erDiagram-OPXOYQCR-DTrqayuU.js → erDiagram-OPXOYQCR-BzVUcUwh.js} +14 -14
- package/dist/{field-blur-EUOqpHbF.js → field-blur-BlrE9fOD.js} +1109 -1083
- package/dist/flowDiagram-KWPJA3E3-B4pJCj2Q.js +17 -0
- package/dist/{ganttDiagram-FUAMR5RP-DxWSt8R4.js → ganttDiagram-FUAMR5RP-B4A4UFFi.js} +6 -6
- package/dist/{gitGraphDiagram-X574FWY7-DcGaPSwy.js → gitGraphDiagram-X574FWY7-80yOq8dE.js} +3 -3
- package/dist/{graphlib-BOTXrGOf.js → graphlib-CQ3hLS1N.js} +103 -139
- package/dist/hooks.js +2 -2
- package/dist/{i18n-provider-XTTU9ArK.js → i18n-provider-CbYD2KxP.js} +1 -1
- package/dist/i18n.js +6 -6
- package/dist/{icon-button-Dl9PDYCE.js → icon-button-BxySFfrr.js} +581 -581
- package/dist/{infoDiagram-VRGFBTTK-Cvj167dl.js → infoDiagram-VRGFBTTK-Cboh-B9n.js} +3 -3
- package/dist/isArrayLikeObject-5rnYwEK7.js +28 -0
- package/dist/isEmpty-DChvniYe.js +53 -0
- package/dist/{ishikawaDiagram-OU5B5YK6-ChBwjCfr.js → ishikawaDiagram-OU5B5YK6-DwTd1YxL.js} +4 -4
- package/dist/{journeyDiagram-ZHPQQLJL-BycdRLE6.js → journeyDiagram-ZHPQQLJL-C91SPPkY.js} +4 -4
- package/dist/{kanban-definition-PNTS6WVX-BRoa-Yp5.js → kanban-definition-PNTS6WVX-S8plI4wn.js} +10 -10
- package/dist/lib/components/ui/actions/button/button.compound-variants.d.ts +114 -0
- package/dist/lib/components/ui/actions/button/button.d.ts +4 -7
- package/dist/lib/components/ui/actions/button/button.variants.d.ts +7 -0
- package/dist/lib/components/ui/actions/button/has-text-content.d.ts +10 -0
- package/dist/lib/components/ui/actions/icon-button/icon-button.variants.d.ts +1 -1
- package/dist/lib/components/ui/collections/data-table/data-table-context.d.ts +4 -0
- package/dist/lib/components/ui/collections/data-table/data-table-pagination.d.ts +3 -1
- package/dist/lib/components/ui/collections/data-table/data-table.d.ts +12 -2
- package/dist/lib/components/ui/collections/data-table/hooks.d.ts +13 -13
- package/dist/lib/components/ui/collections/data-table/partials/data-table-body.d.ts +9 -5
- package/dist/lib/components/ui/collections/data-table/partials/data-table-empty-row.d.ts +14 -0
- package/dist/lib/components/ui/collections/data-table/partials/data-table-skeleton-rows.d.ts +7 -0
- package/dist/lib/components/ui/collections/filter/filter-field-number-range.d.ts +15 -0
- package/dist/lib/components/ui/collections/pagination/index.d.ts +1 -1
- package/dist/lib/components/ui/collections/pagination/pagination.d.ts +9 -1
- package/dist/lib/components/ui/collections/tree/use-tree.d.ts +2 -0
- package/dist/lib/components/ui/dates/date-range-field/date-range-field-trigger.d.ts +1 -1
- package/dist/lib/components/ui/dates/date-range-field/date-range-field.d.ts +10 -1
- package/dist/lib/components/ui/dates/date-range-field/date-range-field.utils.d.ts +13 -0
- package/dist/lib/components/ui/dates/date-range-field/index.d.ts +1 -0
- package/dist/lib/components/ui/dates/date-range-field/partials/date-range-field-content.d.ts +25 -0
- package/dist/lib/components/ui/dates/date-range-field/partials/date-range-presets.d.ts +11 -0
- package/dist/lib/components/ui/dates/date-range-field/partials/date-range-times.d.ts +11 -0
- package/dist/lib/components/ui/dates/date-range-field/use-date-range-presets.d.ts +22 -0
- package/dist/lib/components/ui/dates/time-picker/time-picker-utils.d.ts +7 -0
- package/dist/lib/components/ui/display/chip/chip.d.ts +1 -1
- package/dist/lib/components/ui/feedback/toast/toast.d.ts +9 -4
- package/dist/lib/components/ui/fields/number-field/number-field.d.ts +3 -1
- package/dist/lib/components/ui/fields/otp-field/index.d.ts +1 -0
- package/dist/lib/components/ui/fields/otp-field/input-otp.d.ts +1 -1
- package/dist/lib/components/ui/fields/slider/slider-header.d.ts +19 -0
- package/dist/lib/components/ui/fields/slider/slider-readout.d.ts +5 -1
- package/dist/lib/components/ui/fields/slider/slider.d.ts +7 -0
- package/dist/lib/components/ui/fields/switch/switch.d.ts +7 -0
- package/dist/lib/components/ui/layout/stepper/stepper-step.d.ts +17 -0
- package/dist/lib/components/ui/media/camera/camera-content.d.ts +1 -2
- package/dist/lib/components/ui/media/camera/camera-context.d.ts +4 -0
- package/dist/lib/components/ui/media/camera/camera-provider.d.ts +2 -0
- package/dist/lib/components/ui/media/camera/camera.d.ts +6 -3
- package/dist/lib/components/ui/media/camera/capture-photo.d.ts +13 -0
- package/dist/lib/components/ui/media/camera/index.d.ts +9 -1
- package/dist/lib/components/ui/media/camera/state/camera.reducers.d.ts +1 -1
- package/dist/lib/components/ui/media/camera/state/camera.slice.d.ts +4 -9
- package/dist/lib/components/ui/media/camera/state/camera.store.d.ts +11 -5
- package/dist/lib/components/ui/media/camera/state/camera.thunks.d.ts +4 -4
- package/dist/lib/components/ui/media/camera/types.d.ts +19 -9
- package/dist/lib/components/ui/media/camera/use-camera.d.ts +5 -11
- package/dist/lib/components/ui/media/camera/utils/camera.utils.d.ts +0 -1
- package/dist/lib/components/ui/media/file-preview/file-preview-pdf.lazy.d.ts +9 -0
- package/dist/lib/components/ui/media/file-preview/file-preview.d.ts +12 -0
- package/dist/lib/components/ui/media/file-preview/index.d.ts +3 -0
- package/dist/lib/components/ui/media/file-preview/partials/file-preview-body.d.ts +9 -0
- package/dist/lib/components/ui/media/file-preview/partials/file-preview-card.d.ts +20 -0
- package/dist/lib/components/ui/media/file-preview/partials/file-preview-media.d.ts +11 -0
- package/dist/lib/components/ui/media/file-preview/partials/file-preview-pdf.d.ts +10 -0
- package/dist/lib/components/ui/media/file-preview/partials/file-preview-status.d.ts +17 -0
- package/dist/lib/components/ui/media/file-preview/partials/file-preview-text.d.ts +13 -0
- package/dist/lib/components/ui/media/file-preview/types.d.ts +23 -0
- package/dist/lib/components/ui/media/file-preview/use-file-preview.d.ts +22 -0
- package/dist/lib/components/ui/media/file-preview/use-text-preview.d.ts +15 -0
- package/dist/lib/components/ui/media/index.d.ts +2 -0
- package/dist/lib/components/ui/media/upload/network-upload-field/use-image-preview.d.ts +6 -0
- package/dist/lib/components/ui/overlays/dialog/dialog-outside.d.ts +5 -0
- package/dist/lib/components/ui/overlays/dialog/dialog-scroll-content.d.ts +9 -0
- package/dist/lib/components/ui/overlays/dialog/index.d.ts +1 -0
- package/dist/lib/components/ui/primitives/icon/icon-context.d.ts +1 -1
- package/dist/lib/components/ui/primitives/table/table.d.ts +6 -1
- package/dist/lib/components/ui/selection/index.d.ts +2 -0
- package/dist/lib/components/ui/selection/native-select/index.d.ts +1 -0
- package/dist/lib/components/ui/selection/native-select/native-select.d.ts +11 -0
- package/dist/lib/components/ui/selection/radio-group/radio-group.d.ts +8 -1
- package/dist/lib/components/ui/selection/tree-select-field/index.d.ts +2 -0
- package/dist/lib/components/ui/selection/tree-select-field/partials/tree-select-content.d.ts +13 -0
- package/dist/lib/components/ui/selection/tree-select-field/partials/tree-select-panel.d.ts +19 -0
- package/dist/lib/components/ui/selection/tree-select-field/partials/tree-select-trigger.d.ts +24 -0
- package/dist/lib/components/ui/selection/tree-select-field/tree-select-field.d.ts +9 -0
- package/dist/lib/components/ui/selection/tree-select-field/tree-select-form-field.d.ts +8 -0
- package/dist/lib/components/ui/selection/tree-select-field/tree-select.utils.d.ts +35 -0
- package/dist/lib/components/ui/selection/tree-select-field/types.d.ts +37 -0
- package/dist/lib/components/ui/selection/tree-select-field/use-tree-select-field.d.ts +20 -0
- package/dist/lib/i18n/resources/en/en.d.ts +40 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +43 -3
- package/dist/lib/i18n/resources/fr/fr.d.ts +40 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +43 -3
- package/dist/lib/i18n/resources/resources.d.ts +80 -0
- package/dist/lib/i18n/translator.d.ts +1 -1
- package/dist/lib/lib/redux/status-slice.d.ts +1 -1
- package/dist/lib.js +295 -4
- package/dist/main.js +16 -16
- package/dist/map.js +5 -5
- package/dist/material.css +1 -1
- package/dist/merge-_Awk4ywA.js +156 -0
- package/dist/{mermaid.core-BGAQ3ZAZ.js → mermaid.core-DciYB1MV.js} +49 -49
- package/dist/{mindmap-definition-NLK3R4M7-HIqs4Eil.js → mindmap-definition-NLK3R4M7-Brf_2jIE.js} +13 -13
- package/dist/{pegDiagram-GJSIUBJH-Dcm3LCQX.js → pegDiagram-GJSIUBJH-CAO6aYvj.js} +4 -4
- package/dist/{pieDiagram-5QR66LMP-DQ6djEkW.js → pieDiagram-5QR66LMP-MPh-EWf-.js} +5 -5
- package/dist/print.js +2 -2
- package/dist/{quadrantDiagram-O4NWA36T-yUWorB72.js → quadrantDiagram-O4NWA36T-CwDeE9KB.js} +3 -3
- package/dist/{railroadDiagram-XR7U4H2S-CzOJlTUA.js → railroadDiagram-XR7U4H2S-DmKDXg8V.js} +4 -4
- package/dist/{requirementDiagram-PLB6GJNP-Cs5-8Nro.js → requirementDiagram-PLB6GJNP-CCP3YtvB.js} +13 -13
- package/dist/{sankeyDiagram-IPEJSGJF-CCqxyVoj.js → sankeyDiagram-IPEJSGJF-vgVchd2a.js} +3 -3
- package/dist/{sequenceDiagram-PO4LG4MO-DMLeIQ03.js → sequenceDiagram-PO4LG4MO-B2qb55A5.js} +5 -5
- package/dist/{sortable-provider-DuRzFvwe.js → sortable-provider-CzSnl6It.js} +17 -17
- package/dist/{src-CJ8DNrI1.js → src-C9kQSo6K.js} +1 -1
- package/dist/{stateDiagram-v2-GCMORJYK-D-8sLHg8.js → stateDiagram-v2-GCMORJYK-T-oIXh-8.js} +14 -14
- package/dist/{stored-language-BP90Ydtg.js → stored-language-DmUUhRb5.js} +90 -6
- package/dist/{swimlanes-2SLR337P-DM_lDm6v.js → swimlanes-2SLR337P-tW3_wBWO.js} +12 -12
- package/dist/swimlanesDiagram-TC7HE7FX-B1lWzHp1.js +28 -0
- package/dist/{timeline-definition-EJHVYXUP-D3Tz-JnK.js → timeline-definition-EJHVYXUP-SdA6wt2-.js} +5 -5
- package/dist/{translate-material-Ctzt0Y2N.js → translate-material-DuY12xca.js} +2 -2
- package/dist/{translator-Cfug7hC5.js → translator-CaIYjyFP.js} +1 -1
- package/dist/{use-heading-spy-DtVo2HBk.js → use-heading-spy-Crdz4OH5.js} +2 -2
- package/dist/{use-material-translation-CJswHOmK.js → use-material-translation-Od0QsFPA.js} +2 -2
- package/dist/{usecaseDiagram-POWQR4AR-Cxx8PDrr.js → usecaseDiagram-POWQR4AR-B3rBcR4E.js} +13 -13
- package/dist/{vennDiagram-UO4OBE2U-Aql8mhcy.js → vennDiagram-UO4OBE2U-CO0yFCI8.js} +4 -4
- package/dist/{wardleyDiagram-VNRHLVJA-tknlhG_t.js → wardleyDiagram-VNRHLVJA-QkwghAYo.js} +4 -4
- package/dist/{xychartDiagram-PMCCYNJV-CM0QlWD5.js → xychartDiagram-PMCCYNJV-stQWLgCx.js} +7 -7
- package/docs/components/README.md +2 -1
- package/docs/components/buttons-and-actions.md +8 -3
- package/docs/components/data-display.md +4 -4
- package/docs/components/data-table-filter-and-lists.md +100 -6
- package/docs/components/date-and-time.md +106 -6
- package/docs/components/feedback-and-status.md +4 -3
- package/docs/components/form-fields.md +55 -5
- package/docs/components/media-and-files.md +117 -8
- package/docs/components/navigation-and-layout.md +6 -1
- package/docs/components/overlays-and-menus.md +27 -2
- package/docs/components/primitives.md +34 -3
- package/docs/components/rich-widgets.md +1 -1
- package/docs/components/selection-and-choice.md +111 -8
- package/docs/guides/forms.md +2 -1
- package/docs/guides/i18n.md +58 -9
- package/docs/guides/ssr.md +3 -1
- package/docs/previews/date-time-daterangefield.webp +0 -0
- package/docs/previews/media-files-camera.webp +0 -0
- package/docs/previews/media-files-filepreview.webp +0 -0
- package/docs/previews/navigation-layout-stepper.webp +0 -0
- package/docs/previews/selection-choice-treeselectfield.webp +0 -0
- package/docs/roadmap.md +42 -3
- package/docs/troubleshooting.md +167 -0
- package/package.json +2 -2
- package/dist/flowDiagram-KWPJA3E3-B8Isvcc5.js +0 -17
- package/dist/isArrayLikeObject-CCJDO2RL.js +0 -86
- package/dist/lib/components/ui/fields/slider/slider-ticks.d.ts +0 -1
- package/dist/lib/components/ui/media/camera/camera.provider.d.ts +0 -6
- package/dist/lib/components/ui/media/camera/state/index.d.ts +0 -2
- package/dist/lib/components/ui/media/camera/utils/index.d.ts +0 -1
- package/dist/lib-DIj5dvbb.js +0 -367
- package/dist/merge-Dm-kHFxo.js +0 -172
- package/dist/swimlanesDiagram-TC7HE7FX-CbV1TsML.js +0 -28
|
@@ -23,7 +23,7 @@ each other, so either can be used alone.
|
|
|
23
23
|
|
|
24
24
|

|
|
25
25
|
|
|
26
|
-
TanStack Table v9 with sorting, filtering, selection, expandable rows, pagination or load-more, and
|
|
26
|
+
TanStack Table v9 with sorting, filtering, selection, expandable rows, pagination or load-more, column visibility, skeleton rows while loading and your own empty state.
|
|
27
27
|
|
|
28
28
|
```tsx
|
|
29
29
|
import {
|
|
@@ -52,7 +52,8 @@ function PeopleTable({ people }: { people: Person[] }) {
|
|
|
52
52
|
|
|
53
53
|
Parts: `DataTable`; `DataTableColumnHeader` (a sortable header with a hide
|
|
54
54
|
action); `DataTablePagination` and `DataTableViewOptions` (used by the table,
|
|
55
|
-
exported for a table of your own
|
|
55
|
+
exported for a table of your own; `DataTablePagination` takes `disabled`);
|
|
56
|
+
`useColumnHelpers<T>()` returns a
|
|
56
57
|
`columnHelper` bound to the table's features and `predefinedColumns()` —
|
|
57
58
|
`select` (checkboxes) and `expand` (a toggle for `renderExpandedRow`, named
|
|
58
59
|
"Toggle row details" (`material.table.labels.toggle-row`) with `aria-expanded`);
|
|
@@ -71,6 +72,61 @@ table enables, for typing your own column defs.
|
|
|
71
72
|
| `onLoadMore` / `isLoadingMore` | `() => void` / `boolean` | — | adds a "load more" button under the rows |
|
|
72
73
|
| `options` | `{ showPage?: boolean; showSelected?: boolean }` | both `true` | parts of the footer |
|
|
73
74
|
| `showHeader` | `boolean` | hidden on mobile | header row |
|
|
75
|
+
| `loading` | `boolean` | `false` | skeleton rows, or the current rows dimmed; `aria-busy`; controls disabled |
|
|
76
|
+
| `loadingRows` | `number` | the page size | how many skeleton rows |
|
|
77
|
+
| `empty` | `ReactNode \| ({ filtered }) => ReactNode` | icon and "No data available" | content of the empty row, spanning every visible column |
|
|
78
|
+
|
|
79
|
+
### Loading and empty states
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { DataTable, useColumnHelpers } from '@nxgt/material/components';
|
|
83
|
+
|
|
84
|
+
type Person = { id: string; name: string; email: string };
|
|
85
|
+
|
|
86
|
+
function PeopleTable({
|
|
87
|
+
people,
|
|
88
|
+
isFetching,
|
|
89
|
+
}: {
|
|
90
|
+
people: Person[];
|
|
91
|
+
isFetching: boolean;
|
|
92
|
+
}) {
|
|
93
|
+
const { columnHelper } = useColumnHelpers<Person>();
|
|
94
|
+
const columns = [
|
|
95
|
+
columnHelper.accessor('name', { header: 'Name' }),
|
|
96
|
+
columnHelper.accessor('email', { header: 'Email' }),
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<DataTable
|
|
101
|
+
data={people}
|
|
102
|
+
columns={columns}
|
|
103
|
+
loading={isFetching}
|
|
104
|
+
loadingRows={5}
|
|
105
|
+
empty={({ filtered }) =>
|
|
106
|
+
filtered ? 'No person matches these filters.' : 'No people yet.'
|
|
107
|
+
}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
While `loading` is `true`, the table is `aria-busy` and a visually
|
|
114
|
+
hidden status says "Loading data…" (`material.table.loading`). With no row to
|
|
115
|
+
show, the body is `loadingRows` skeleton rows (default: the page size), one
|
|
116
|
+
cell per visible column. With rows already shown — a refresh — the rows stay
|
|
117
|
+
where they are, dimmed and `inert`, rather than being swapped for
|
|
118
|
+
skeletons: the table keeps its height, so nothing below it jumps when the data
|
|
119
|
+
arrives. Sorting (`DataTableColumnHeader`), the `select` column's "Select
|
|
120
|
+
all", the toolbar's native controls (it
|
|
121
|
+
is a disabled `<fieldset>`, so every input or button you render there, a
|
|
122
|
+
filter or an export, is disabled
|
|
123
|
+
too) and pagination are disabled meanwhile.
|
|
124
|
+
|
|
125
|
+
When no row is left after filtering, the empty state is one `TableEmpty`
|
|
126
|
+
cell spanning every visible column: today's icon and "No data available" by
|
|
127
|
+
default, or your own content, told whether rows exist but none matches the
|
|
128
|
+
filters (`filtered`); content that renders nothing gets the default.
|
|
129
|
+
`DataTableEmpty` and `DataTableEmptyState` type the prop and its argument.
|
|
74
130
|
|
|
75
131
|
For the TanStack API itself (column helpers, features, row models), see
|
|
76
132
|
[TanStack Table v9](https://tanstack.com/table/latest).
|
|
@@ -116,7 +172,10 @@ const schema: FilterSchema = {
|
|
|
116
172
|
A field's `type` is one of `text`, `number`, `select`, `multi-select`, `date`,
|
|
117
173
|
`date-range`, `number-range`, `checkbox-group`, `radio-group`,
|
|
118
174
|
`chips-multiple`, `chips-single`, `slider`, `slider-range`, `switch` or
|
|
119
|
-
`custom`; `schema.groups` puts fields in collapsible sections.
|
|
175
|
+
`custom`; `schema.groups` puts fields in collapsible sections. A
|
|
176
|
+
`number-range` or `slider-range` is a `role="group"` named by its `label`,
|
|
177
|
+
with its inputs named by their part ("Min" / "Max", or the slider's
|
|
178
|
+
"<label> Minimum" / "<label> Maximum"): `getByRole('group', { name: 'Price' })`.
|
|
120
179
|
|
|
121
180
|
Parts: `Filter` (one component, `variant` picks the layout);
|
|
122
181
|
`FilterProvider` + `FilterContent` + `FilterActions` to lay the panel out
|
|
@@ -174,9 +233,39 @@ import { PaginationBar } from '@nxgt/material/components';
|
|
|
174
233
|
<PaginationBar page={page} pageCount={12} onPageChange={setPage} />
|
|
175
234
|
```
|
|
176
235
|
|
|
236
|
+
```tsx
|
|
237
|
+
import {
|
|
238
|
+
Pagination,
|
|
239
|
+
PaginationContent,
|
|
240
|
+
PaginationFirst,
|
|
241
|
+
PaginationItem,
|
|
242
|
+
PaginationLast,
|
|
243
|
+
PaginationLink,
|
|
244
|
+
} from '@nxgt/material/components';
|
|
245
|
+
|
|
246
|
+
<Pagination>
|
|
247
|
+
<PaginationContent>
|
|
248
|
+
<PaginationItem>
|
|
249
|
+
<PaginationFirst href="?page=1" />
|
|
250
|
+
</PaginationItem>
|
|
251
|
+
<PaginationItem>
|
|
252
|
+
<PaginationLink href="?page=2" isActive>
|
|
253
|
+
2
|
|
254
|
+
</PaginationLink>
|
|
255
|
+
</PaginationItem>
|
|
256
|
+
<PaginationItem>
|
|
257
|
+
<PaginationLast href="?page=12" />
|
|
258
|
+
</PaginationItem>
|
|
259
|
+
</PaginationContent>
|
|
260
|
+
</Pagination>;
|
|
261
|
+
```
|
|
262
|
+
|
|
177
263
|
Parts: `Pagination`, `PaginationContent`, `PaginationItem`,
|
|
178
|
-
`PaginationLink` (`isActive`), `
|
|
179
|
-
`PaginationEllipsis` for link-based
|
|
264
|
+
`PaginationLink` (`isActive`), `PaginationFirst`, `PaginationPrevious`,
|
|
265
|
+
`PaginationNext`, `PaginationLast`, `PaginationEllipsis` for link-based
|
|
266
|
+
pagination (first / previous / next / last take an optional `label` and
|
|
267
|
+
`icon`; their names are translated, `material.pagination.*`, and the text
|
|
268
|
+
hides below `sm`); `PaginationBar` for the
|
|
180
269
|
stateful one; `paginationItems(page, pageCount, siblingCount)` for the
|
|
181
270
|
window alone.
|
|
182
271
|
|
|
@@ -188,7 +277,7 @@ window alone.
|
|
|
188
277
|
| `siblingCount` | `number` | `1` | pages shown on each side of the current one |
|
|
189
278
|
| `disabled` | `boolean` | — | disables every button |
|
|
190
279
|
|
|
191
|
-
**Storybook:** `Data table, filter & lists/Pagination` · **Vue:** `Pagination` (parts), `PaginationFirst`, `PaginationLast`
|
|
280
|
+
**Storybook:** `Data table, filter & lists/Pagination` · **Vue:** `Pagination` (parts), `PaginationFirst`, `PaginationLast`, `PaginationLink`, `PaginationBar`, `paginationItems`
|
|
192
281
|
|
|
193
282
|
---
|
|
194
283
|
|
|
@@ -230,6 +319,11 @@ own) and `TreeItem` (one row); `findTreeNode` and `flattenVisibleTree`.
|
|
|
230
319
|
Arrow keys move and expand / collapse, Home / End jump, Enter and Space
|
|
231
320
|
select.
|
|
232
321
|
|
|
322
|
+
Tab lands on the focused row, or on the first visible row when that one is
|
|
323
|
+
filtered or folded away; mounting a `Tree` does not move focus. `useTree`
|
|
324
|
+
returns that row as `tabbableId`: give it `tabindex="0"` in a tree you build
|
|
325
|
+
yourself.
|
|
326
|
+
|
|
233
327
|
**Storybook:** `Data table, filter & lists/Tree` · **Vue:** `Tree`, `TreeItem`, `useTree`
|
|
234
328
|
|
|
235
329
|
---
|
|
@@ -105,7 +105,7 @@ calendar is labelled by the trigger, so named by the label.
|
|
|
105
105
|
**Emits:** `Date | undefined`.
|
|
106
106
|
**Form wrapper:** `DateFormField` — `control` and `name`, see
|
|
107
107
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
108
|
-
**Storybook:** `Date & time/DateField` · **Vue:** `DateField`, `DateFormField`
|
|
108
|
+
**Storybook:** `Date & time/DateField` · **Vue:** `DateField`, `DateFormField` (`disabled-days`, same rules)
|
|
109
109
|
|
|
110
110
|
---
|
|
111
111
|
|
|
@@ -113,7 +113,7 @@ calendar is labelled by the trigger, so named by the label.
|
|
|
113
113
|
|
|
114
114
|

|
|
115
115
|
|
|
116
|
-
From / to range picked in a popover calendar.
|
|
116
|
+
From / to range picked in a popover calendar, with presets and times.
|
|
117
117
|
|
|
118
118
|
```tsx
|
|
119
119
|
import { type DateRange, DateRangeField } from '@nxgt/material/components';
|
|
@@ -128,10 +128,13 @@ export function Stay() {
|
|
|
128
128
|
| Prop | Type | Default | Effect |
|
|
129
129
|
| --- | --- | --- | --- |
|
|
130
130
|
| `value` | `DateRange` | — | `{ from: Date \| undefined; to?: Date }` |
|
|
131
|
-
| `onValueChange` | `(value?: DateRange) => void` | — | called on each click: `to` is
|
|
131
|
+
| `onValueChange` | `(value?: DateRange) => void` | — | called on each click: after the first, `to` is the same day as `from` |
|
|
132
132
|
| `placeholder`, `label`, `helperText`, `error`, `required` | | — | `placeholder` is the trigger's text; `label` and `helperText` sit beside it; `error` and `required` its state |
|
|
133
133
|
| `disabled` | `boolean` | — | disables the field: the trigger cannot be focused or opened |
|
|
134
134
|
| `disabledDays` | react-day-picker's `Matcher \| Matcher[]` | — | days that cannot be picked (the calendar's `disabled`) |
|
|
135
|
+
| `presets` | `readonly DateRangePresetOption[]` | — (no list) | one-click ranges beside the calendar: built-in ids (`DATE_RANGE_PRESET_IDS`) and `{ label, range }` of your own |
|
|
136
|
+
| `withTime` | `boolean` | `false` | a time of day on each end, under the calendar, and on the trigger; the popover stays open until OK |
|
|
137
|
+
| `minDate`, `maxDate` | `Date` | — | the first and last day that can be picked (the calendar's, as are `disablePast` / `disableFuture`) |
|
|
135
138
|
|
|
136
139
|
Every other `Calendar` prop is passed to the calendar; it opens on the
|
|
137
140
|
month of `value.from`. `id`, `aria-describedby` and `data-*` attributes go on
|
|
@@ -148,11 +151,103 @@ trigger in the field's root `div` (`data-slot="date-range-field"`, which takes
|
|
|
148
151
|
rendered alone, shows only the value: use the field for the label and the
|
|
149
152
|
helper text. The open calendar is labelled by the trigger.
|
|
150
153
|
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
### Presets
|
|
155
|
+
|
|
156
|
+
`presets` lists one-click ranges beside the calendar (above it on a narrow
|
|
157
|
+
screen), in the order you give them. A built-in id is translated
|
|
158
|
+
(`material.date-range-field.preset.<id>`); a preset of your own is
|
|
159
|
+
`{ label, range }`, where `range` is `{ from, to }` or `(now) => ({ from, to })`.
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import {
|
|
163
|
+
type DateRange,
|
|
164
|
+
DateRangeField,
|
|
165
|
+
type DateRangePreset,
|
|
166
|
+
} from '@nxgt/material/components';
|
|
167
|
+
import { useState } from 'react';
|
|
168
|
+
|
|
169
|
+
const lastQuarter: DateRangePreset = {
|
|
170
|
+
label: 'Last quarter',
|
|
171
|
+
range: (now) => {
|
|
172
|
+
const start = new Date(now.getFullYear(), Math.floor(now.getMonth() / 3) * 3 - 3, 1);
|
|
173
|
+
return { from: start, to: new Date(start.getFullYear(), start.getMonth() + 3, 0) };
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export function Report() {
|
|
178
|
+
const [period, setPeriod] = useState<DateRange | undefined>();
|
|
179
|
+
return (
|
|
180
|
+
<DateRangeField
|
|
181
|
+
label="Period"
|
|
182
|
+
value={period}
|
|
183
|
+
onValueChange={setPeriod}
|
|
184
|
+
maxDate={new Date()}
|
|
185
|
+
presets={['today', 'last-7-days', 'this-month', 'last-month', lastQuarter]}
|
|
186
|
+
/>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
| Built-in id | Range |
|
|
192
|
+
| --- | --- |
|
|
193
|
+
| `today`, `yesterday` | that day |
|
|
194
|
+
| `last-7-days`, `last-30-days` | ends today and counts it |
|
|
195
|
+
| `this-month`, `this-year` | from the first day to today |
|
|
196
|
+
| `last-month` | the whole previous month |
|
|
197
|
+
|
|
198
|
+
A built-in range starts at 00:00 (and with `withTime` ends at 23:59); your
|
|
199
|
+
own ranges keep the times you gave them. Picking a preset sets the value and
|
|
200
|
+
closes the popover (with `withTime` it stays open); open again, the calendar
|
|
201
|
+
is on that range's month. The list is a group named "Presets"
|
|
202
|
+
(`material.date-range-field.presets`) of buttons in the tab order, and the
|
|
203
|
+
one whose days are the value's has `aria-pressed="true"`. A preset is
|
|
204
|
+
disabled when its range starts before `minDate`, ends after `maxDate`, or
|
|
205
|
+
either end is one of `disabledDays`; the days between are not checked, as the
|
|
206
|
+
calendar lets a range span a disabled day.
|
|
207
|
+
The built-in presets use the current date when the popover opens; there is
|
|
208
|
+
no `now` prop, and none is planned. A preset of your own whose end comes
|
|
209
|
+
before its start is disabled.
|
|
210
|
+
|
|
211
|
+
### Times
|
|
212
|
+
|
|
213
|
+
`withTime` adds two `TimePicker`s under the calendar, in groups named
|
|
214
|
+
"Start time" and "End time" (`material.date-range-field.start-time`,
|
|
215
|
+
`.end-time`), each disabled until its end has a day, and an OK button
|
|
216
|
+
(`material.buttons.ok`) that closes the popover. The trigger shows each end
|
|
217
|
+
with its time (date-fns `Pp`).
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
import { type DateRange, DateRangeField } from '@nxgt/material/components';
|
|
221
|
+
import { useState } from 'react';
|
|
222
|
+
|
|
223
|
+
export function Booking() {
|
|
224
|
+
const [slot, setSlot] = useState<DateRange | undefined>();
|
|
225
|
+
return (
|
|
226
|
+
<DateRangeField
|
|
227
|
+
label="Booking"
|
|
228
|
+
value={slot}
|
|
229
|
+
onValueChange={setSlot}
|
|
230
|
+
withTime
|
|
231
|
+
presets={['today']}
|
|
232
|
+
/>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
A day picked on the calendar keeps the time that end had; a new start is
|
|
238
|
+
00:00 and a new end 23:59, as a built-in preset ends, so a filter on
|
|
239
|
+
`<= to` keeps the last day. On the same day, an end time set before the
|
|
240
|
+
start time becomes the start time.
|
|
241
|
+
|
|
242
|
+
Without `withTime`, a picked day is at 00:00. (`@nxgt/material-vue` gives
|
|
243
|
+
it the current time of day.)
|
|
244
|
+
|
|
245
|
+
**Emits:** `DateRange | undefined` — `{ from, to }`, where the first day
|
|
246
|
+
picked is both `from` and `to` (react-day-picker's `addToRange`) until the
|
|
247
|
+
second day is picked.
|
|
153
248
|
**Form wrapper:** `DateRangeFormField` — `control` and `name`, see
|
|
154
249
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
155
|
-
**Storybook:** `Date & time/DateRangeField` · **Vue:** `DateRangeField`, `DateRangeFormField`
|
|
250
|
+
**Storybook:** `Date & time/DateRangeField` (stories `DisabledDays`, `MinMaxDates`, `Presets`, `DisabledPresets`, `WithTime`) · **Vue:** `DateRangeField`, `DateRangeFormField` (`disabled-days`, `presets`, `with-time`, `min-date` / `max-date`; without time, a picked day carries the current time of day there, 00:00 here)
|
|
156
251
|
|
|
157
252
|
---
|
|
158
253
|
|
|
@@ -235,6 +330,11 @@ export function Clock() {
|
|
|
235
330
|
| `label` | `ReactNode` | — | header text |
|
|
236
331
|
| `footer` | `ReactNode` | — | under the inputs (the OK button in `TimeField`) |
|
|
237
332
|
|
|
333
|
+
The hours and minutes inputs are named "Hours" and "Minutes"
|
|
334
|
+
(`material.time-picker.hours`, `.minutes`). AM / PM shows the half of the day
|
|
335
|
+
`date` is in, so a date changed from outside shows its own; switching it moves
|
|
336
|
+
the time 12 hours, and a click on the pressed half changes nothing.
|
|
337
|
+
|
|
238
338
|
**Emits:** `Date | undefined`. No form wrapper — use `TimeFormField`.
|
|
239
339
|
**Storybook:** `Date & time/TimePicker` · **Vue:** `TimePicker`
|
|
240
340
|
|
|
@@ -227,7 +227,7 @@ built on `AlertDialog`, so it does not close on an outside click. Put
|
|
|
227
227
|
`AlertDialogCancel` / `AlertDialogAction` in its `footer`.
|
|
228
228
|
|
|
229
229
|
**Storybook:** `Feedback & status/ConfirmDialog`, `Feedback & status/ConfirmationDialog`,
|
|
230
|
-
`Feedback & status/CustomAlertDialog` · **Vue:** `ConfirmDialog`, `ConfirmationDialog
|
|
230
|
+
`Feedback & status/CustomAlertDialog` · **Vue:** `ConfirmDialog`, `ConfirmationDialog` (`form-options` + `#form` slot for `renderForm`), `CustomAlertDialog`
|
|
231
231
|
|
|
232
232
|
---
|
|
233
233
|
|
|
@@ -415,7 +415,7 @@ toasts.dismiss(id);
|
|
|
415
415
|
| --- | --- | --- | --- |
|
|
416
416
|
| `title` / `description` | `ReactNode` | preset title for `success`, `error`, `loading` | toast text |
|
|
417
417
|
| `icon` | `ReactNode` (`toast`) · icon name (`toasts.custom`) | preset icon | leading icon |
|
|
418
|
-
| `action` | `{ label, onClick? }` or `(dismiss) => ReactNode` | a translated “Close” for `success`, `info`, `error` | trailing button |
|
|
418
|
+
| `action` | `ToastAction`: `{ label, onClick? }` or `(dismiss) => ReactNode` | a translated “Close” for `success`, `info`, `error` | trailing button: `onClick`, then the toast closes; or what the function renders, given `dismiss` |
|
|
419
419
|
| `options` | Sonner `ExternalToast` | `duration: 5000` for presets, `Infinity` for `loading` | passed to Sonner (`id`, `duration`, `position`…) |
|
|
420
420
|
|
|
421
421
|
The toast id defaults to the title, so two toasts with the same title
|
|
@@ -423,7 +423,8 @@ replace each other; pass `options: { id }` to show both. `Toaster` is Sonner's
|
|
|
423
423
|
and takes its props (`position`, `expand`, `richColors`…). `Toast` is the
|
|
424
424
|
component each toast renders, for use with `sonnerToast.custom`.
|
|
425
425
|
|
|
426
|
-
**
|
|
426
|
+
**Types:** `ToastAction`, `ToastProps`, `ToastMethodProps`.
|
|
427
|
+
**Storybook:** `Feedback & status/Toast` · **Vue:** `Toaster`, `toast`, `toasts`, `Toast` (`ToastAction` type)
|
|
427
428
|
|
|
428
429
|
---
|
|
429
430
|
|
|
@@ -218,6 +218,7 @@ export function Quantity() {
|
|
|
218
218
|
| `hideSteppers` | `boolean` | `false` | hides the +/− buttons |
|
|
219
219
|
| `suffix` | `ReactNode` | — | unit after the input |
|
|
220
220
|
| `aria-label` | `string` | — | names the input when it has no `label` (a part of a larger field) |
|
|
221
|
+
| `aria-labelledby` | `string` | — | names the input by another element's id (a part of a larger field) |
|
|
221
222
|
|
|
222
223
|
**Emits:** `number | null`.
|
|
223
224
|
**Form wrapper:** `NumberFormField` — `control` and `name`, see
|
|
@@ -412,10 +413,41 @@ export function Code() {
|
|
|
412
413
|
| `error` | `boolean` | — | error colour on the slots |
|
|
413
414
|
| `id` | `string` | from `useId()` | the input's id, which the label points to; the generated one is the same on the server and the client |
|
|
414
415
|
|
|
416
|
+
`InputOTP`, `InputOTPGroup`, `InputOTPSlot` and `InputOTPSeparator` are the
|
|
417
|
+
parts `OtpField` is built from (`input-otp`), for a layout of your own — no
|
|
418
|
+
label, no helper text:
|
|
419
|
+
|
|
420
|
+
```tsx
|
|
421
|
+
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from '@nxgt/material/components';
|
|
422
|
+
import { useState } from 'react';
|
|
423
|
+
|
|
424
|
+
export function Pin() {
|
|
425
|
+
const [pin, setPin] = useState('');
|
|
426
|
+
return (
|
|
427
|
+
<InputOTP aria-label="PIN" maxLength={4} value={pin} onChange={setPin}>
|
|
428
|
+
<InputOTPGroup>
|
|
429
|
+
<InputOTPSlot index={0} />
|
|
430
|
+
<InputOTPSlot index={1} />
|
|
431
|
+
</InputOTPGroup>
|
|
432
|
+
<InputOTPSeparator />
|
|
433
|
+
<InputOTPGroup>
|
|
434
|
+
<InputOTPSlot index={2} />
|
|
435
|
+
<InputOTPSlot index={3} />
|
|
436
|
+
</InputOTPGroup>
|
|
437
|
+
</InputOTP>
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
`InputOTP` takes `input-otp`'s `OTPInput` props (`maxLength`, `value`,
|
|
443
|
+
`onChange`, `pattern`, …) plus `containerClassName`; `InputOTPSlot` takes the
|
|
444
|
+
`index` it shows; `InputOTPSeparator` is a `separator` with a dash, or your
|
|
445
|
+
children.
|
|
446
|
+
|
|
415
447
|
**Emits:** `string` (the digits typed so far).
|
|
416
448
|
**Form wrapper:** `OtpFormField` — `control` and `name`, see
|
|
417
449
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
418
|
-
**Storybook:** `Form fields/OtpField` · **Vue:** `OtpField`, `OtpFormField`, `InputOTP`
|
|
450
|
+
**Storybook:** `Form fields/OtpField` · **Vue:** `OtpField`, `OtpFormField`, `InputOTP` (same parts)
|
|
419
451
|
|
|
420
452
|
---
|
|
421
453
|
|
|
@@ -475,10 +507,13 @@ import { InlineEdit } from '@nxgt/material/components';
|
|
|
475
507
|
| `placeholder` | `string` | "Click to edit" | shown while empty |
|
|
476
508
|
| `disabled` | `boolean` | `false` | read-only text |
|
|
477
509
|
|
|
510
|
+
Enter, Escape, Save and Cancel return focus to the field, while a click
|
|
511
|
+
elsewhere saves and leaves focus where it went.
|
|
512
|
+
|
|
478
513
|
**Emits:** `string`, trimmed, on save only.
|
|
479
514
|
**Form wrapper:** `InlineEditFormField` — `control` and `name`, see
|
|
480
515
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
481
|
-
**Storybook:** `Form fields/InlineEdit` · **Vue:** `InlineEdit`
|
|
516
|
+
**Storybook:** `Form fields/InlineEdit` · **Vue:** `InlineEdit`, `InlineEditFormField`
|
|
482
517
|
|
|
483
518
|
---
|
|
484
519
|
|
|
@@ -605,15 +640,25 @@ import { Slider } from '@nxgt/material/components';
|
|
|
605
640
|
Each thumb (`role="slider"`) is named by an `aria-labelledby` or `aria-label`
|
|
606
641
|
you pass, else by the label (a vertical slider does not show it, and it still
|
|
607
642
|
names the thumbs); a range's thumbs add "Minimum" and "Maximum":
|
|
608
|
-
`getByRole('slider', { name: 'Price Minimum' })`.
|
|
609
|
-
|
|
643
|
+
`getByRole('slider', { name: 'Price Minimum' })`. A labelled range is also a
|
|
644
|
+
`role="group"` named by the label, and its readout inputs are named
|
|
645
|
+
"Minimum" and "Maximum" (`material.slider.minimum` / `maximum`), so two range
|
|
646
|
+
sliders are told apart. A single value's readout input is named as its thumb
|
|
647
|
+
is, and "Value" (`material.slider.value`) when nothing names the slider.
|
|
648
|
+
`aria-invalid` (or `error`) goes on every thumb too,
|
|
649
|
+
not on the root `span`.
|
|
650
|
+
|
|
651
|
+
`SliderField` is the same component under `@nxgt/material-vue`'s name (an
|
|
652
|
+
alias, `SliderFieldProps` for its props), so code reads the same in both
|
|
653
|
+
libraries; neither name is deprecated, and the full naming alignment is
|
|
654
|
+
planned for 3.0.
|
|
610
655
|
|
|
611
656
|
**Emits:** `number[]`. `SliderFormField` stores a plain `number` when the
|
|
612
657
|
form's value is a number, and `number[]` when it is an array — the default
|
|
613
658
|
value decides.
|
|
614
659
|
**Form wrapper:** `SliderFormField` — `control` and `name`, see
|
|
615
660
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
616
|
-
**Storybook:** `Form fields/Slider` · **Vue:** `Slider`, `SliderField`, `SliderFormField`
|
|
661
|
+
**Storybook:** `Form fields/Slider` · **Vue:** `Slider`, `SliderField` (`ticks`, `leading` / `trailing` or their slots), `SliderFormField`
|
|
617
662
|
|
|
618
663
|
---
|
|
619
664
|
|
|
@@ -656,6 +701,11 @@ while the switch is on, from `checked` or, uncontrolled, from `defaultChecked` a
|
|
|
656
701
|
`[data-checked="true"]` (a bare `[data-checked]` also matches rows that are
|
|
657
702
|
off). `data-filed` says the same and is deprecated: it goes in 3.0.
|
|
658
703
|
|
|
704
|
+
`SwitchField` is the same component under `@nxgt/material-vue`'s name (an
|
|
705
|
+
alias, `SwitchFieldProps` for its props), so code reads the same in both
|
|
706
|
+
libraries; neither name is deprecated, and the full naming alignment is
|
|
707
|
+
planned for 3.0.
|
|
708
|
+
|
|
659
709
|
**Emits:** `boolean`.
|
|
660
710
|
**Form wrapper:** `SwitchFormField` — `control` and `name`, see
|
|
661
711
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
@@ -11,6 +11,7 @@ them into a form.
|
|
|
11
11
|
| <img src="../previews/media-files-s3uploadfield.webp" width="96" alt=""> | [S3UploadField, NetworkUploadField](#s3uploadfield-networkuploadfield) | Drag-and-drop uploader with progress, retry, previews and size / count limits |
|
|
12
12
|
| <img src="../previews/media-files-uploadfield.webp" width="96" alt=""> | [UploadField](#uploadfield) | Labelled file input showing the chosen file's name |
|
|
13
13
|
| <img src="../previews/media-files-filelist.webp" width="96" alt=""> | [FileList](#filelist) | File list with type icons, size, download and remove |
|
|
14
|
+
| <img src="../previews/media-files-filepreview.webp" width="96" alt=""> | [FilePreview](#filepreview) | One file previewed by its type: image, PDF, video, audio, text, or a download card |
|
|
14
15
|
| | [FilesFormField](#filesformfield) | Free-solo autocomplete of file URLs, with image thumbnails |
|
|
15
16
|
| <img src="../previews/media-files-imagefield.webp" width="96" alt=""> | [ImageField](#imagefield) | Single image upload with preview and progress |
|
|
16
17
|
| <img src="../previews/media-files-imagecropper.webp" width="96" alt=""> | [ImageCropper](#imagecropper) | Crop with zoom, rotation, aspect presets and a round shape |
|
|
@@ -19,6 +20,7 @@ them into a form.
|
|
|
19
20
|
| <img src="../previews/media-files-pdfreader.webp" width="96" alt=""> | [PdfReader](#pdfreader) | Browser-only PDF reader with zoom, search, thumbnails and print |
|
|
20
21
|
| <img src="../previews/media-files-stxplayer.webp" width="96" alt=""> | [StxPlayer](#stxplayer) | Video / audio player with a queue, captions and mini layout |
|
|
21
22
|
| | [VideoPlayer](#videoplayer) | Deprecated alias of `StxPlayer` |
|
|
23
|
+
| <img src="../previews/media-files-camera.webp" width="96" alt=""> | [Camera](#camera) | Webcam preview with a start / stop toggle and a still capture |
|
|
22
24
|
| | [Print components](#print-components) | `PrintDocument` page shell and `@react-pdf/renderer` building blocks for PDF documents, and `downloadPdf`, `openPdf`, `usePdfFile` and `pdfResponse` to get the file |
|
|
23
25
|
| <img src="../previews/media-files-map.webp" width="96" alt=""> | [Map](#map) | Leaflet map, markers and a location field (`@nxgt/material/map`) |
|
|
24
26
|
|
|
@@ -220,6 +222,75 @@ dropzone as well read the error twice, and the group still says it once
|
|
|
220
222
|
|
|
221
223
|
---
|
|
222
224
|
|
|
225
|
+
## FilePreview
|
|
226
|
+
|
|
227
|
+

|
|
228
|
+
|
|
229
|
+
Previews one file, picked by its MIME type (else its extension): an image, a PDF, a video or audio file, the text of a text, JSON, CSV or Markdown file, and a download card for anything else.
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
232
|
+
import { useState } from 'react';
|
|
233
|
+
import { FilePreview } from '@nxgt/material/components';
|
|
234
|
+
|
|
235
|
+
export function Attachment() {
|
|
236
|
+
const [file, setFile] = useState<File | null>(null);
|
|
237
|
+
return (
|
|
238
|
+
<>
|
|
239
|
+
<input type="file" onChange={(event) => setFile(event.target.files?.[0] ?? null)} />
|
|
240
|
+
{/* A File or Blob: shown through an object URL, revoked on change and unmount. */}
|
|
241
|
+
<FilePreview file={file} onError={(error) => console.warn(error)} />
|
|
242
|
+
{/* A URL: the kind comes from `type`, else the extension; `name` is the alt text. */}
|
|
243
|
+
<FilePreview file="/api/files/42" type="application/pdf" name="contract.pdf" />
|
|
244
|
+
</>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
| Prop | Type | Default | Effect |
|
|
250
|
+
| --- | --- | --- | --- |
|
|
251
|
+
| `file` | `File \| Blob \| string \| null` | — | the file, or its URL; nothing renders without one. A URL must be `http(s):`, relative, `blob:`, or a `data:` URL of an image, video, audio, plain text, CSV, Markdown, JSON or PDF; any other (`javascript:`, `data:text/html`, and also a `data:` URL of XML, TSV or YAML) shows the error state, with no link and nothing loaded |
|
|
252
|
+
| `type` | `string` | the `Blob`'s, or a `data:` URL's | MIME type; picks the kind before the extension does |
|
|
253
|
+
| `name` | `string` | the `File`'s, else the URL's last segment | shown on the card, the image's alt text and the download's file name; "Untitled file" when there is none |
|
|
254
|
+
| `maxTextBytes` | `number` | `262144` (`TEXT_PREVIEW_MAX_BYTES`, 256 KB) | how much of a text kind is read; past it, the preview shows the beginning and says so |
|
|
255
|
+
| `onError` | `(error: unknown) => void` | — | the file could not be read (an HTTP error: `FilePreviewHttpError`, with its `status`; a refused URL: `FilePreviewUnsafeUrlError`) or shown (a broken image, video or audio file) |
|
|
256
|
+
| `className`, other `<figure>` props | | | on the root `<figure>` |
|
|
257
|
+
|
|
258
|
+
| Kind | Picked by | Renders |
|
|
259
|
+
| --- | --- | --- |
|
|
260
|
+
| `image` | `image/*`; `.png` `.jpg` `.jpeg` `.gif` `.webp` `.avif` `.svg` `.bmp` `.ico` | `<img>`, its alt text the file name |
|
|
261
|
+
| `pdf` | `application/pdf`; `.pdf` | [`PdfReader`](#pdfreader), imported the first time a PDF is previewed; the card when the viewer cannot be loaded. A PDF the reader fails to open leaves the preview `ready`: `PdfReader` shows its own error, and `onError` is not called |
|
|
262
|
+
| `video` | `video/*`; `.mp4` `.m4v` `.webm` `.ogv` `.mov` | `<video controls>` |
|
|
263
|
+
| `audio` | `audio/*`; `.mp3` `.wav` `.ogg` `.oga` `.m4a` `.aac` `.flac` `.opus` | `<audio controls>` |
|
|
264
|
+
| `json` | `application/json`, `text/json`, `*+json`; `.json` | [`JsonViewer`](data-display.md#codeblock) |
|
|
265
|
+
| `markdown` | `text/markdown`, `text/x-markdown`; `.md` `.markdown` | [`Markdown`](data-display.md#markdown), rendered; its links are sanitized like any `Markdown`, but a remote image in it still loads, so an untrusted `.md` can make the browser request a URL of its choosing |
|
|
266
|
+
| `csv` | `text/csv`, `application/csv`, `text/tab-separated-values`; `.csv` `.tsv` | the text, in a `<pre>` |
|
|
267
|
+
| `text` | any other `text/*`, `application/xml`, `application/yaml`, `application/javascript`…; `.txt` `.log` `.xml` `.yaml` `.yml` `.ini` `.toml` | the text, in a `<pre>` |
|
|
268
|
+
| `other` | anything else, `application/octet-stream` without a known extension | a [`FileCard`](data-display.md#card-presets): icon, name, size and type, and a Download link |
|
|
269
|
+
|
|
270
|
+
A text kind is fetched (a URL) or read (a `Blob`) up to `maxTextBytes` and
|
|
271
|
+
decoded with the `charset` of the response's `Content-Type` (or of the
|
|
272
|
+
`Blob`'s `type`) when the browser's `TextDecoder` supports it, else as UTF-8;
|
|
273
|
+
a JSON or Markdown file cut by the cap is shown as plain text, with "Showing
|
|
274
|
+
the first 256.0 KB of this file." below it. The `<pre>` and the Markdown box
|
|
275
|
+
are regions named "Contents of {name}"; they scroll and take focus, so a
|
|
276
|
+
keyboard can scroll them.
|
|
277
|
+
|
|
278
|
+
The root is a `<figure>` named "Preview of {name}", with `data-kind` and
|
|
279
|
+
`data-state`: `pending` on the server and in the first client render — an
|
|
280
|
+
empty placeholder, with no object URL made and nothing fetched, so it hydrates
|
|
281
|
+
without a mismatch — then `loading` ("Loading preview…", put into a `status` live region that
|
|
282
|
+
stays mounted, so it is announced), `ready`
|
|
283
|
+
or `error` ("This file could not be previewed.", an `alert`, with a Download
|
|
284
|
+
link when there is something to download). The kind comes from
|
|
285
|
+
`fileKind({ type, name, url })`, exported too, from
|
|
286
|
+
[`@nxgt/material-core/file-preview`](https://cdn.jsdelivr.net/npm/@nxgt/material-core@0/README.md#a-files-preview-file-preview).
|
|
287
|
+
Every string is translated (`material.file-preview.*`, see the
|
|
288
|
+
[i18n guide](../guides/i18n.md)).
|
|
289
|
+
|
|
290
|
+
**Storybook:** `Media & files/FilePreview` · **Vue:** `FilePreview`
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
223
294
|
## FilesFormField
|
|
224
295
|
|
|
225
296
|
No preview: it has no story of its own. It is an [`Autocomplete`](selection-and-choice.md)
|
|
@@ -256,7 +327,7 @@ function Gallery() {
|
|
|
256
327
|
| `freeSolo` | `boolean` | `true` | accept values that are not in `options` |
|
|
257
328
|
|
|
258
329
|
**Form wrapper:** it is one — `control` and `name`, see [forms](../guides/forms.md).
|
|
259
|
-
**Storybook:** — · **Vue:**
|
|
330
|
+
**Storybook:** — · **Vue:** `FilesFormField`
|
|
260
331
|
|
|
261
332
|
---
|
|
262
333
|
|
|
@@ -633,6 +704,51 @@ No preview: it is [`StxPlayer`](#stxplayer) under another name
|
|
|
633
704
|
|
|
634
705
|
---
|
|
635
706
|
|
|
707
|
+
## Camera
|
|
708
|
+
|
|
709
|
+

|
|
710
|
+
|
|
711
|
+
Webcam preview with a start / stop toggle and a still capture.
|
|
712
|
+
|
|
713
|
+
```tsx
|
|
714
|
+
import { Camera } from '@nxgt/material/components';
|
|
715
|
+
|
|
716
|
+
export function Webcam() {
|
|
717
|
+
return (
|
|
718
|
+
<div className="w-[640px]">
|
|
719
|
+
<Camera />
|
|
720
|
+
</div>
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
It asks for camera permission when it mounts, lists the devices, streams on
|
|
726
|
+
the toggle and shows the captured photo beside the preview; the stream is
|
|
727
|
+
stopped on unmount. **It renders in the browser only, after mount**
|
|
728
|
+
(`clientOnly`): on the server, and on the first client render, it renders
|
|
729
|
+
nothing, so it is safe in an SSR page. `Camera` takes the props of a `div`
|
|
730
|
+
(`className`, …) and has no callback: the capture stays inside the
|
|
731
|
+
component. Each `Camera` keeps its own state.
|
|
732
|
+
|
|
733
|
+
`Camera` renders its own parts and takes no children. The helpers it runs
|
|
734
|
+
are exported, and the first three are what a webcam UI of your own needs
|
|
735
|
+
(`<video>` + `getUserMedia`):
|
|
736
|
+
|
|
737
|
+
| Export | What it is |
|
|
738
|
+
| --- | --- |
|
|
739
|
+
| `capturePhotoFromVideo(video, state)` | draws a `<video>` frame on a canvas (`captureSettings`: resolution, quality, format, mirror) and returns `{ capturedImage }` as a data URL, `{ alert }` on failure, `null` when not streaming or on the server |
|
|
740
|
+
| `isCameraSupported()`, `requestCameraPermissions()`, `getCameraDevices()` | the browser checks the component runs |
|
|
741
|
+
| `useCamera()`, `useCameraContext()` | the context `Camera`'s own parts read (`{ videoRef, canvasRef, streamRef, state, actions }`); they throw outside `Camera`, and `Camera` exposes no slot, so they serve its internals and tests, as in `@nxgt/material-vue` |
|
|
742
|
+
| `createCameraStore(preloaded?)` | the Redux store each `Camera` creates for itself, for tests |
|
|
743
|
+
|
|
744
|
+
Types: `CameraProps`, `CameraState`, `CameraActions`, `CameraContextValue`,
|
|
745
|
+
`CameraAlert`, `CameraDeviceInfo`, `CaptureSettings`, `ImageFormat`,
|
|
746
|
+
`CapturePhotoResult`, `CameraStore`.
|
|
747
|
+
|
|
748
|
+
**Storybook:** `Media & files/Camera` · **Vue:** `Camera`, `useCamera`, `capturePhotoFromVideo` (`useCameraStore` for the store)
|
|
749
|
+
|
|
750
|
+
---
|
|
751
|
+
|
|
636
752
|
## Print components
|
|
637
753
|
|
|
638
754
|
No preview: these render PDF with `@react-pdf/renderer`, not DOM, so there is
|
|
@@ -892,10 +1008,3 @@ Parts: `Map` (also exported as `MapComponent`), `Marker`, `LocationMarker`,
|
|
|
892
1008
|
|
|
893
1009
|
**Form wrapper:** `LocationFormField` — see [forms](../guides/forms.md).
|
|
894
1010
|
**Storybook:** `Media & files/Map` · **Vue:** —
|
|
895
|
-
|
|
896
|
-
---
|
|
897
|
-
|
|
898
|
-
## Not documented here
|
|
899
|
-
|
|
900
|
-
`Media & files/Camera` exists in Storybook only: the `Camera` component is not
|
|
901
|
-
exported from the package, so it has no entry on this page.
|
|
@@ -437,6 +437,11 @@ Parts: `Stepper`, `StepperIndicator` (the progress bar alone: `current`,
|
|
|
437
437
|
| `onChange` | `(index: number) => void` | — | makes steps clickable |
|
|
438
438
|
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | vertical from `md` only; phones get the compact bar |
|
|
439
439
|
| `renderStepLabel` | `(step, index) => ReactNode` | — | replaces the "Step n" caption |
|
|
440
|
+
| `name` | `string` | — | the stepper's `data-pw` (`stepper` without it), and the prefix of each step's: `<name>-step-N` instead of `step-N` |
|
|
441
|
+
|
|
442
|
+
Each step is an `<li>` with `data-pw="step-N"` (`<name>-step-N` with `name`),
|
|
443
|
+
N counted from 0, for tests to select; the current step's button carries
|
|
444
|
+
`aria-current="step"`, and a completed or failed step is announced as such.
|
|
440
445
|
|
|
441
446
|
A server renders the desktop layout (every step), as does a hydrating client;
|
|
442
447
|
a phone switches to the compact bar right after hydrating. A page rendered
|
|
@@ -550,7 +555,7 @@ On `DocPage`: `title`, `description`, `badge`, `crumbs`, `editHref`,
|
|
|
550
555
|
`updatedAt`, and `copyPage` / `copyMarkdown` with `markdown` for the copy
|
|
551
556
|
buttons.
|
|
552
557
|
|
|
553
|
-
**Storybook:** `Navigation & layout/Doc`, `Navigation & layout/Doc/Api`, `Navigation & layout/Doc/Blocks` · **Vue:** `Doc`, `DocPage`, `DocTopbar`, `DocSidebar`, `DocToc`, `DocPager`, `DocSearch`, `DocChildCards`, `DocSeeAlso`, `DocEmpty`, `DocProgress`
|
|
558
|
+
**Storybook:** `Navigation & layout/Doc`, `Navigation & layout/Doc/Api`, `Navigation & layout/Doc/Blocks` · **Vue:** `Doc`, `DocPage`, `DocTopbar`, `DocSidebar`, `DocNav`, `DocArticle`, `DocToc`, `DocPager`, `DocSearch`, `DocChildCards`, `DocSeeAlso`, `DocEmpty`, `DocProgress`, `DocLink`, `DefaultDocLink`, `DocCopyPage` (no `linkComponent`: `@navigate`)
|
|
554
559
|
|
|
555
560
|
---
|
|
556
561
|
|