@nxgt/material 2.4.1 → 2.5.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 -19
- package/dist/{_Uint8Array-DEXn42kj.js → _Uint8Array-DuTT6911.js} +1 -1
- package/dist/_overRest-93z6TD9V.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-B8Xv2BLH.js} +164 -93
- package/dist/{blockDiagram-BEXU5L5S-CD5PW_mQ.js → blockDiagram-BEXU5L5S-C8UXdLfB.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-CNBUvTSg.js → chunk-3FUC2YCW-4gCRt6fO.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-DEh1Crrc.js → chunk-7M6MHVWA-DnPXbiTM.js} +9 -9
- package/dist/{chunk-7PRAP22T-DIiYRAce.js → chunk-7PRAP22T-_Av9vcXz.js} +1 -1
- package/dist/{chunk-GNY47TPC-vOPvRE_8.js → chunk-GNY47TPC-DLqgU2SJ.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-CQV8lo68.js → chunk-NETBCI7D-CBowDvwz.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-DcqSgI9Q.js → chunk-Z7XXMR3K-dRJc1Z30.js} +8 -8
- package/dist/{chunk-ZIGJFQKS-HDJ6e6OJ.js → chunk-ZIGJFQKS-C3xUAXJq.js} +3 -3
- package/dist/{classDiagram-v2-NBCMYWYE-tU42Be-2.js → classDiagram-v2-NBCMYWYE-CQmePeL4.js} +14 -14
- package/dist/{components-NLbPtyeB.js → components-CUkJhBwK.js} +26367 -25835
- 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-CXwkkHFr.js → dagre-6A5THRUB-BhyJENrD.js} +574 -615
- package/dist/{diagram-22UHCM2B-BswK7SlD.js → diagram-22UHCM2B-CJc0JoTV.js} +15 -15
- package/dist/{diagram-3UASUU5V-5p5ra9Yi.js → diagram-3UASUU5V-0P3Cnq5H.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-CuGpDp18.js → elk-276RUBZZ-CvgDuHoY.js} +13 -13
- package/dist/{erDiagram-OPXOYQCR-vpNBbFgR.js → erDiagram-OPXOYQCR-BFgRy8E9.js} +14 -14
- package/dist/{field-blur-EUOqpHbF.js → field-blur-D1eQk-49.js} +1109 -1083
- package/dist/flowDiagram-KWPJA3E3-DGaa9iT2.js +17 -0
- package/dist/{ganttDiagram-FUAMR5RP-DC1sfzgs.js → ganttDiagram-FUAMR5RP-CDGrtGHY.js} +6 -6
- package/dist/{gitGraphDiagram-X574FWY7-DcGaPSwy.js → gitGraphDiagram-X574FWY7-80yOq8dE.js} +3 -3
- package/dist/{graphlib-DvOFXJbI.js → graphlib-Dt-isUVV.js} +103 -139
- package/dist/hooks.js +2 -2
- package/dist/{i18n-provider-XTTU9ArK.js → i18n-provider-Drsc2YXX.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-BhWbq65y.js +28 -0
- package/dist/isEmpty-DdlzV8Ue.js +53 -0
- package/dist/{ishikawaDiagram-OU5B5YK6-ChBwjCfr.js → ishikawaDiagram-OU5B5YK6-DwTd1YxL.js} +4 -4
- package/dist/{journeyDiagram-ZHPQQLJL-C6djNSgq.js → journeyDiagram-ZHPQQLJL-BuLKqWS3.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/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/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 +15 -0
- 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/index.d.ts +1 -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 +1 -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/i18n/resources/en/en.d.ts +7 -0
- package/dist/lib/i18n/resources/en/material.json.d.ts +8 -1
- package/dist/lib/i18n/resources/fr/fr.d.ts +7 -0
- package/dist/lib/i18n/resources/fr/material.json.d.ts +8 -1
- package/dist/lib/i18n/resources/resources.d.ts +14 -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-a7J0YkZX.js +156 -0
- package/dist/{mermaid.core-CEDjsxgL.js → mermaid.core-CE34byhz.js} +49 -49
- package/dist/{mindmap-definition-NLK3R4M7-BYtcw4Io.js → mindmap-definition-NLK3R4M7-emoij-nW.js} +13 -13
- package/dist/{pegDiagram-GJSIUBJH-Dcm3LCQX.js → pegDiagram-GJSIUBJH-CAO6aYvj.js} +4 -4
- package/dist/{pieDiagram-5QR66LMP-8ETWucra.js → pieDiagram-5QR66LMP-BdG1tWLn.js} +5 -5
- package/dist/print.js +2 -2
- package/dist/{quadrantDiagram-O4NWA36T-X05JG-jZ.js → quadrantDiagram-O4NWA36T-BCd-NwsO.js} +3 -3
- package/dist/{railroadDiagram-XR7U4H2S-CzOJlTUA.js → railroadDiagram-XR7U4H2S-DmKDXg8V.js} +4 -4
- package/dist/{requirementDiagram-PLB6GJNP-BwWSE7Fn.js → requirementDiagram-PLB6GJNP-CCcH5uSr.js} +13 -13
- package/dist/{sankeyDiagram-IPEJSGJF-B6YxyZMn.js → sankeyDiagram-IPEJSGJF-78cK-bfe.js} +3 -3
- package/dist/{sequenceDiagram-PO4LG4MO-DMLeIQ03.js → sequenceDiagram-PO4LG4MO-B2qb55A5.js} +5 -5
- package/dist/{sortable-provider-DuRzFvwe.js → sortable-provider-VCxPasNp.js} +17 -17
- package/dist/{src-CJ8DNrI1.js → src-C9kQSo6K.js} +1 -1
- package/dist/{stateDiagram-v2-GCMORJYK-CAqcgA-x.js → stateDiagram-v2-GCMORJYK-B4ghJrhD.js} +14 -14
- package/dist/{stored-language-BP90Ydtg.js → stored-language-CeD7kHui.js} +20 -2
- package/dist/{swimlanes-2SLR337P-DRQ2gho_.js → swimlanes-2SLR337P-DmHm2yJS.js} +12 -12
- package/dist/swimlanesDiagram-TC7HE7FX-Dq3gGsPj.js +28 -0
- package/dist/{timeline-definition-EJHVYXUP-BtvpCePa.js → timeline-definition-EJHVYXUP-EqJV_QKn.js} +5 -5
- package/dist/{translate-material-Ctzt0Y2N.js → translate-material-DgRdhz5d.js} +2 -2
- package/dist/{translator-Cfug7hC5.js → translator-B4zUVm7j.js} +1 -1
- package/dist/{use-heading-spy-DtVo2HBk.js → use-heading-spy-1BHMXjIn.js} +2 -2
- package/dist/{use-material-translation-CJswHOmK.js → use-material-translation-UbARR81K.js} +2 -2
- package/dist/{usecaseDiagram-POWQR4AR-DsYqBvQ-.js → usecaseDiagram-POWQR4AR-CvCl0p5N.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-CEf5bvG1.js → xychartDiagram-PMCCYNJV-BphsHFKA.js} +7 -7
- package/docs/components/buttons-and-actions.md +8 -3
- package/docs/components/data-display.md +1 -1
- package/docs/components/data-table-filter-and-lists.md +95 -6
- package/docs/components/feedback-and-status.md +3 -2
- package/docs/components/form-fields.md +48 -4
- package/docs/components/media-and-files.md +46 -7
- package/docs/components/navigation-and-layout.md +6 -1
- package/docs/components/overlays-and-menus.md +27 -2
- package/docs/components/primitives.md +33 -2
- package/docs/components/rich-widgets.md +1 -1
- package/docs/components/selection-and-choice.md +32 -6
- package/docs/guides/i18n.md +14 -8
- package/docs/guides/ssr.md +3 -1
- package/docs/previews/buttons-actions-actioncard.webp +0 -0
- package/docs/previews/media-files-camera.webp +0 -0
- package/docs/previews/navigation-layout-stepper.webp +0 -0
- package/docs/roadmap.md +37 -3
- package/docs/troubleshooting.md +109 -0
- package/package.json +1 -1
- package/dist/flowDiagram-KWPJA3E3-PC80XOp8.js +0 -17
- package/dist/isArrayLikeObject-CCJDO2RL.js +0 -86
- 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-DaXG362E.js +0 -28
|
@@ -412,10 +412,41 @@ export function Code() {
|
|
|
412
412
|
| `error` | `boolean` | — | error colour on the slots |
|
|
413
413
|
| `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
414
|
|
|
415
|
+
`InputOTP`, `InputOTPGroup`, `InputOTPSlot` and `InputOTPSeparator` are the
|
|
416
|
+
parts `OtpField` is built from (`input-otp`), for a layout of your own — no
|
|
417
|
+
label, no helper text:
|
|
418
|
+
|
|
419
|
+
```tsx
|
|
420
|
+
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from '@nxgt/material/components';
|
|
421
|
+
import { useState } from 'react';
|
|
422
|
+
|
|
423
|
+
export function Pin() {
|
|
424
|
+
const [pin, setPin] = useState('');
|
|
425
|
+
return (
|
|
426
|
+
<InputOTP aria-label="PIN" maxLength={4} value={pin} onChange={setPin}>
|
|
427
|
+
<InputOTPGroup>
|
|
428
|
+
<InputOTPSlot index={0} />
|
|
429
|
+
<InputOTPSlot index={1} />
|
|
430
|
+
</InputOTPGroup>
|
|
431
|
+
<InputOTPSeparator />
|
|
432
|
+
<InputOTPGroup>
|
|
433
|
+
<InputOTPSlot index={2} />
|
|
434
|
+
<InputOTPSlot index={3} />
|
|
435
|
+
</InputOTPGroup>
|
|
436
|
+
</InputOTP>
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
`InputOTP` takes `input-otp`'s `OTPInput` props (`maxLength`, `value`,
|
|
442
|
+
`onChange`, `pattern`, …) plus `containerClassName`; `InputOTPSlot` takes the
|
|
443
|
+
`index` it shows; `InputOTPSeparator` is a `separator` with a dash, or your
|
|
444
|
+
children.
|
|
445
|
+
|
|
415
446
|
**Emits:** `string` (the digits typed so far).
|
|
416
447
|
**Form wrapper:** `OtpFormField` — `control` and `name`, see
|
|
417
448
|
[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`
|
|
449
|
+
**Storybook:** `Form fields/OtpField` · **Vue:** `OtpField`, `OtpFormField`, `InputOTP` (same parts)
|
|
419
450
|
|
|
420
451
|
---
|
|
421
452
|
|
|
@@ -478,7 +509,7 @@ import { InlineEdit } from '@nxgt/material/components';
|
|
|
478
509
|
**Emits:** `string`, trimmed, on save only.
|
|
479
510
|
**Form wrapper:** `InlineEditFormField` — `control` and `name`, see
|
|
480
511
|
[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`
|
|
512
|
+
**Storybook:** `Form fields/InlineEdit` · **Vue:** `InlineEdit`, `InlineEditFormField`
|
|
482
513
|
|
|
483
514
|
---
|
|
484
515
|
|
|
@@ -605,8 +636,16 @@ import { Slider } from '@nxgt/material/components';
|
|
|
605
636
|
Each thumb (`role="slider"`) is named by an `aria-labelledby` or `aria-label`
|
|
606
637
|
you pass, else by the label (a vertical slider does not show it, and it still
|
|
607
638
|
names the thumbs); a range's thumbs add "Minimum" and "Maximum":
|
|
608
|
-
`getByRole('slider', { name: 'Price Minimum' })`.
|
|
609
|
-
|
|
639
|
+
`getByRole('slider', { name: 'Price Minimum' })`. A labelled range is also a
|
|
640
|
+
`role="group"` named by the label, and its readout inputs are named
|
|
641
|
+
"Minimum" and "Maximum" (`material.slider.minimum` / `maximum`), so two range
|
|
642
|
+
sliders are told apart. `aria-invalid` (or `error`) goes on every thumb too,
|
|
643
|
+
not on the root `span`.
|
|
644
|
+
|
|
645
|
+
`SliderField` is the same component under `@nxgt/material-vue`'s name (an
|
|
646
|
+
alias, `SliderFieldProps` for its props), so code reads the same in both
|
|
647
|
+
libraries; neither name is deprecated, and the full naming alignment is
|
|
648
|
+
planned for 3.0.
|
|
610
649
|
|
|
611
650
|
**Emits:** `number[]`. `SliderFormField` stores a plain `number` when the
|
|
612
651
|
form's value is a number, and `number[]` when it is an array — the default
|
|
@@ -656,6 +695,11 @@ while the switch is on, from `checked` or, uncontrolled, from `defaultChecked` a
|
|
|
656
695
|
`[data-checked="true"]` (a bare `[data-checked]` also matches rows that are
|
|
657
696
|
off). `data-filed` says the same and is deprecated: it goes in 3.0.
|
|
658
697
|
|
|
698
|
+
`SwitchField` is the same component under `@nxgt/material-vue`'s name (an
|
|
699
|
+
alias, `SwitchFieldProps` for its props), so code reads the same in both
|
|
700
|
+
libraries; neither name is deprecated, and the full naming alignment is
|
|
701
|
+
planned for 3.0.
|
|
702
|
+
|
|
659
703
|
**Emits:** `boolean`.
|
|
660
704
|
**Form wrapper:** `SwitchFormField` — `control` and `name`, see
|
|
661
705
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
@@ -19,6 +19,7 @@ them into a form.
|
|
|
19
19
|
| <img src="../previews/media-files-pdfreader.webp" width="96" alt=""> | [PdfReader](#pdfreader) | Browser-only PDF reader with zoom, search, thumbnails and print |
|
|
20
20
|
| <img src="../previews/media-files-stxplayer.webp" width="96" alt=""> | [StxPlayer](#stxplayer) | Video / audio player with a queue, captions and mini layout |
|
|
21
21
|
| | [VideoPlayer](#videoplayer) | Deprecated alias of `StxPlayer` |
|
|
22
|
+
| <img src="../previews/media-files-camera.webp" width="96" alt=""> | [Camera](#camera) | Webcam preview with a start / stop toggle and a still capture |
|
|
22
23
|
| | [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
24
|
| <img src="../previews/media-files-map.webp" width="96" alt=""> | [Map](#map) | Leaflet map, markers and a location field (`@nxgt/material/map`) |
|
|
24
25
|
|
|
@@ -633,6 +634,51 @@ No preview: it is [`StxPlayer`](#stxplayer) under another name
|
|
|
633
634
|
|
|
634
635
|
---
|
|
635
636
|
|
|
637
|
+
## Camera
|
|
638
|
+
|
|
639
|
+

|
|
640
|
+
|
|
641
|
+
Webcam preview with a start / stop toggle and a still capture.
|
|
642
|
+
|
|
643
|
+
```tsx
|
|
644
|
+
import { Camera } from '@nxgt/material/components';
|
|
645
|
+
|
|
646
|
+
export function Webcam() {
|
|
647
|
+
return (
|
|
648
|
+
<div className="w-[640px]">
|
|
649
|
+
<Camera />
|
|
650
|
+
</div>
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
It asks for camera permission when it mounts, lists the devices, streams on
|
|
656
|
+
the toggle and shows the captured photo beside the preview; the stream is
|
|
657
|
+
stopped on unmount. **It renders in the browser only, after mount**
|
|
658
|
+
(`clientOnly`): on the server, and on the first client render, it renders
|
|
659
|
+
nothing, so it is safe in an SSR page. `Camera` takes the props of a `div`
|
|
660
|
+
(`className`, …) and has no callback: the capture stays inside the
|
|
661
|
+
component. Each `Camera` keeps its own state.
|
|
662
|
+
|
|
663
|
+
`Camera` renders its own parts and takes no children. The helpers it runs
|
|
664
|
+
are exported, and the first three are what a webcam UI of your own needs
|
|
665
|
+
(`<video>` + `getUserMedia`):
|
|
666
|
+
|
|
667
|
+
| Export | What it is |
|
|
668
|
+
| --- | --- |
|
|
669
|
+
| `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 |
|
|
670
|
+
| `isCameraSupported()`, `requestCameraPermissions()`, `getCameraDevices()` | the browser checks the component runs |
|
|
671
|
+
| `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` |
|
|
672
|
+
| `createCameraStore(preloaded?)` | the Redux store each `Camera` creates for itself, for tests |
|
|
673
|
+
|
|
674
|
+
Types: `CameraProps`, `CameraState`, `CameraActions`, `CameraContextValue`,
|
|
675
|
+
`CameraAlert`, `CameraDeviceInfo`, `CaptureSettings`, `ImageFormat`,
|
|
676
|
+
`CapturePhotoResult`, `CameraStore`.
|
|
677
|
+
|
|
678
|
+
**Storybook:** `Media & files/Camera` · **Vue:** `Camera`, `useCamera`, `capturePhotoFromVideo` (`useCameraStore` for the store)
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
636
682
|
## Print components
|
|
637
683
|
|
|
638
684
|
No preview: these render PDF with `@react-pdf/renderer`, not DOM, so there is
|
|
@@ -892,10 +938,3 @@ Parts: `Map` (also exported as `MapComponent`), `Marker`, `LocationMarker`,
|
|
|
892
938
|
|
|
893
939
|
**Form wrapper:** `LocationFormField` — see [forms](../guides/forms.md).
|
|
894
940
|
**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
|
|
|
@@ -61,7 +61,32 @@ import {
|
|
|
61
61
|
|
|
62
62
|
Parts: `Dialog`, `DialogTrigger`, `DialogContent`, `DialogHeader`,
|
|
63
63
|
`DialogTitle`, `DialogDescription`, `DialogFooter`, `DialogClose`, plus
|
|
64
|
-
`DialogOverlay` and `DialogPortal
|
|
64
|
+
`DialogOverlay` and `DialogPortal`, and `DialogScrollContent`: a content for
|
|
65
|
+
a dialog taller than the screen, which sits in a scrolling overlay with a
|
|
66
|
+
margin instead of being clipped, and renders its own close button. It takes
|
|
67
|
+
`DialogContent`'s props and moves and returns focus the same way.
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { Button, Dialog, DialogScrollContent, DialogTitle, DialogTrigger } from '@nxgt/material/components';
|
|
71
|
+
|
|
72
|
+
const clauses = Array.from({ length: 30 }, (_, i) => `Clause ${i + 1}. Either party may end the service with thirty days' notice.`);
|
|
73
|
+
|
|
74
|
+
export function Terms() {
|
|
75
|
+
return (
|
|
76
|
+
<Dialog>
|
|
77
|
+
<DialogTrigger>
|
|
78
|
+
<Button>Read the terms</Button>
|
|
79
|
+
</DialogTrigger>
|
|
80
|
+
<DialogScrollContent>
|
|
81
|
+
<DialogTitle>Terms of service</DialogTitle>
|
|
82
|
+
{clauses.map((clause) => (
|
|
83
|
+
<p key={clause}>{clause}</p>
|
|
84
|
+
))}
|
|
85
|
+
</DialogScrollContent>
|
|
86
|
+
</Dialog>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
65
90
|
|
|
66
91
|
| Prop | Type | Default | Effect |
|
|
67
92
|
| --- | --- | --- | --- |
|
|
@@ -105,7 +130,7 @@ export function RenameButton() {
|
|
|
105
130
|
}
|
|
106
131
|
```
|
|
107
132
|
|
|
108
|
-
**Storybook:** `Overlays & menus/Dialog` · **Vue:** `Dialog` (same parts)
|
|
133
|
+
**Storybook:** `Overlays & menus/Dialog` · **Vue:** `Dialog` (same parts)
|
|
109
134
|
|
|
110
135
|
---
|
|
111
136
|
|
|
@@ -516,11 +516,42 @@ import {
|
|
|
516
516
|
```
|
|
517
517
|
|
|
518
518
|
Parts: `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableRow`,
|
|
519
|
-
`TableHead`, `TableCell`, `TableCaption` — each takes its HTML element's props
|
|
519
|
+
`TableHead`, `TableCell`, `TableCaption` — each takes its HTML element's props
|
|
520
|
+
— plus `TableEmpty`, a full-width row for "nothing here" (`colSpan`, default
|
|
521
|
+
`1`; the content is centred):
|
|
522
|
+
|
|
523
|
+
```tsx
|
|
524
|
+
import { Table, TableBody, TableCell, TableEmpty, TableHead, TableHeader, TableRow } from '@nxgt/material/components';
|
|
525
|
+
|
|
526
|
+
type Invoice = { id: string; amount: string };
|
|
527
|
+
|
|
528
|
+
export function Invoices({ invoices }: { invoices: Invoice[] }) {
|
|
529
|
+
return (
|
|
530
|
+
<Table>
|
|
531
|
+
<TableHeader>
|
|
532
|
+
<TableRow>
|
|
533
|
+
<TableHead>Invoice</TableHead>
|
|
534
|
+
<TableHead className="text-right">Amount</TableHead>
|
|
535
|
+
</TableRow>
|
|
536
|
+
</TableHeader>
|
|
537
|
+
<TableBody>
|
|
538
|
+
{invoices.length === 0 && <TableEmpty colSpan={2}>No invoices yet.</TableEmpty>}
|
|
539
|
+
{invoices.map((invoice) => (
|
|
540
|
+
<TableRow key={invoice.id}>
|
|
541
|
+
<TableCell>{invoice.id}</TableCell>
|
|
542
|
+
<TableCell className="text-right">{invoice.amount}</TableCell>
|
|
543
|
+
</TableRow>
|
|
544
|
+
))}
|
|
545
|
+
</TableBody>
|
|
546
|
+
</Table>
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
520
551
|
For sorting, paging and selection use `DataTable` on the
|
|
521
552
|
[Data table, filter & lists](data-table-filter-and-lists.md) page.
|
|
522
553
|
|
|
523
|
-
**Storybook:** `Primitives/Table` · **Vue:** `Table` (same parts)
|
|
554
|
+
**Storybook:** `Primitives/Table` · **Vue:** `Table` (same parts)
|
|
524
555
|
|
|
525
556
|
---
|
|
526
557
|
|
|
@@ -372,7 +372,7 @@ function Config() {
|
|
|
372
372
|
|
|
373
373
|
**Form wrapper:** `CodeEditorFormField` — `control` and `name`, see
|
|
374
374
|
[forms](../guides/forms.md).
|
|
375
|
-
**Storybook:** `Rich widgets/CodeEditor` · **Vue:** `CodeEditor`
|
|
375
|
+
**Storybook:** `Rich widgets/CodeEditor` · **Vue:** `CodeEditor`, `CodeEditorFormField`
|
|
376
376
|
|
|
377
377
|
---
|
|
378
378
|
|
|
@@ -10,7 +10,7 @@ value type each one writes. Everything is imported from
|
|
|
10
10
|
|
|
11
11
|
| | Component | What it does |
|
|
12
12
|
| --- | --- | --- |
|
|
13
|
-
| <img src="../previews/selection-choice-select.webp" width="96" alt=""> | [Select](#select) | Select primitives with groups and labels |
|
|
13
|
+
| <img src="../previews/selection-choice-select.webp" width="96" alt=""> | [Select](#select) | Select primitives with groups and labels, and a styled native `<select>` |
|
|
14
14
|
| <img src="../previews/selection-choice-selectfield.webp" width="96" alt=""> | [SelectField](#selectfield) | Labelled select built from `options` |
|
|
15
15
|
| <img src="../previews/selection-choice-combobox.webp" width="96" alt=""> | [Combobox](#combobox) | Base UI combobox primitives |
|
|
16
16
|
| <img src="../previews/selection-choice-comboboxfield.webp" width="96" alt=""> | [ComboboxField](#comboboxfield) | Labelled, searchable single select |
|
|
@@ -73,9 +73,32 @@ Parts: `Select`, `SelectTrigger`, `SelectValue`, `SelectContent`,
|
|
|
73
73
|
| `SelectContent` `position` | `'popper' \| 'item-aligned'` | `'popper'` | below the trigger, as wide as it; `item-aligned` overlays it macOS-style |
|
|
74
74
|
| `SelectTrigger` `size` | `'sm' \| 'default'` | `'default'` | trigger height |
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
`NativeSelect` is the browser's own `<select>` with the library's styling and
|
|
77
|
+
chevron, for when the platform picker is what you want (long lists on a
|
|
78
|
+
phone, no portal). Parts: `NativeSelect`, `NativeSelectOption`,
|
|
79
|
+
`NativeSelectOptGroup`; `value` / `defaultValue` / `onChange`, `className`
|
|
80
|
+
and every other attribute go to the `<select>`.
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { NativeSelect, NativeSelectOptGroup, NativeSelectOption } from '@nxgt/material/components';
|
|
84
|
+
import { useState } from 'react';
|
|
85
|
+
|
|
86
|
+
export function Food() {
|
|
87
|
+
const [food, setFood] = useState('banana');
|
|
88
|
+
return (
|
|
89
|
+
<NativeSelect aria-label="Food" value={food} onChange={(event) => setFood(event.target.value)}>
|
|
90
|
+
<NativeSelectOptGroup label="Fruits">
|
|
91
|
+
<NativeSelectOption value="apple">Apple</NativeSelectOption>
|
|
92
|
+
<NativeSelectOption value="banana">Banana</NativeSelectOption>
|
|
93
|
+
</NativeSelectOptGroup>
|
|
94
|
+
</NativeSelect>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Emits:** `string` through `onValueChange` (`Select`), a change event
|
|
100
|
+
(`NativeSelect`). For a form, use `SelectField` / `SelectFormField`.
|
|
101
|
+
**Storybook:** `Selection & choice/Select` · **Vue:** `Select` (same parts), `NativeSelect` (same parts)
|
|
79
102
|
|
|
80
103
|
---
|
|
81
104
|
|
|
@@ -297,7 +320,7 @@ the field's `name`, which two forms on one page shared).
|
|
|
297
320
|
and `null` for indeterminate.
|
|
298
321
|
**Form wrapper:** `CheckboxFormField` — `control` and `name`, see
|
|
299
322
|
[forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
|
|
300
|
-
**Storybook:** `Selection & choice/Checkbox` · **Vue:** `Checkbox`
|
|
323
|
+
**Storybook:** `Selection & choice/Checkbox` · **Vue:** `Checkbox`, `CheckboxFormField`
|
|
301
324
|
|
|
302
325
|
---
|
|
303
326
|
|
|
@@ -372,7 +395,10 @@ export function Plan() {
|
|
|
372
395
|
```
|
|
373
396
|
|
|
374
397
|
Parts: `RadioGroup` (labelled, from `options`) and `RadioGroupItem` (one
|
|
375
|
-
radio, for a hand-built layout).
|
|
398
|
+
radio, for a hand-built layout). `RadioGroupField` is the same component under
|
|
399
|
+
`@nxgt/material-vue`'s name (an alias, `RadioGroupFieldProps` for its props),
|
|
400
|
+
so code reads the same in both libraries; neither name is deprecated, and the
|
|
401
|
+
full naming alignment is planned for 3.0.
|
|
376
402
|
|
|
377
403
|
| Prop | Type | Default | Effect |
|
|
378
404
|
| --- | --- | --- | --- |
|
package/docs/guides/i18n.md
CHANGED
|
@@ -195,16 +195,19 @@ The copy components render on their own comes from this namespace: empty
|
|
|
195
195
|
states, dialogs, pickers, the rich-text editor's toolbar, menus, node dialogs,
|
|
196
196
|
slash menu, font names and table-caption alignment
|
|
197
197
|
(`material['rich-text-editor'].*`), the carousel arrows' screen-reader text,
|
|
198
|
-
the camera
|
|
198
|
+
the camera's button, toggle and image (`material.camera.capture`, `captured`,
|
|
199
|
+
`start` and `stop`), the autocomplete's `Add "…"` and empty state, the QR code and
|
|
199
200
|
barcode accessible names, the calendar's navigation tooltips
|
|
200
201
|
(`material.calendar.*`), the data table's selection checkboxes, the filter's
|
|
201
202
|
presets and `Min`/`Max` fields, the `variant="4"` spinner's label,
|
|
202
203
|
`ScrollToTop`, the network upload field's `Retry`/`Remove`, the map's
|
|
203
204
|
`You are here` and zoom buttons, and the icon-only close button of
|
|
204
205
|
`DialogClose`, `DrawerClose`, `SheetClose` and `SheetContent`'s
|
|
205
|
-
`withCloseButton`.
|
|
206
|
+
`withCloseButton`. A range `Slider`'s readout inputs read
|
|
207
|
+
`material.slider.minimum` / `maximum` ("Minimum", "Maximum"). The other icon-only buttons are named from it too: the
|
|
206
208
|
`Sidebar` toggle (`material.sidebar.toggle`), the data table's row expander
|
|
207
|
-
(`material.table.labels.toggle-row`),
|
|
209
|
+
(`material.table.labels.toggle-row`), its loading status, "Loading data…" /
|
|
210
|
+
"Chargement des données…" (`material.table.loading`), a chip's remove button — `Chip`,
|
|
208
211
|
`ComboboxChip` and the `Autocomplete` chips, "Remove {label}"
|
|
209
212
|
(`material.chip.dismiss-label`) — the `LocationPreview` trigger
|
|
210
213
|
(`material.map.preview` / `preview-named`), and the delete button of the
|
|
@@ -246,11 +249,14 @@ sheet title on a phone (`material['pane-layout']['sheet-title']`);
|
|
|
246
249
|
`JsonViewer`'s "Empty JSON" (`material['json-viewer'].empty`);
|
|
247
250
|
`TimePicker`'s AM/PM (`material['time-picker'].am` / `pm`, "a.m." / "p.m." in
|
|
248
251
|
French); and the names of `DurationField`'s inputs, "Hours" and "Minutes"
|
|
249
|
-
(`material['duration-field']['hours-label']` / `minutes-label`)
|
|
250
|
-
`
|
|
251
|
-
`material.
|
|
252
|
-
`PaginationFirst
|
|
253
|
-
|
|
252
|
+
(`material['duration-field']['hours-label']` / `minutes-label`); the status a
|
|
253
|
+
`Stepper` step announces after its title, "Completed" or "Error"
|
|
254
|
+
(`material.stepper.completed` / `error`, "Terminée" / "Erreur" in French); and
|
|
255
|
+
`PaginationFirst` / `PaginationLast`'s "First" / "Last"
|
|
256
|
+
(`material.pagination.first` / `last`). `material.carousel.roledescription` /
|
|
257
|
+
`slide` and `material.attributes.buttons.add-option` are there for
|
|
258
|
+
`@nxgt/material-vue`'s carousel and attribute options, so the bundles hold
|
|
259
|
+
the same keys. Your `placeholder`, `sheetTitle` and `aria-label` still
|
|
254
260
|
win.
|
|
255
261
|
|
|
256
262
|
To format filter values yourself in the provider's language, pass
|
package/docs/guides/ssr.md
CHANGED
|
@@ -34,7 +34,9 @@ at import time would crash the server before it answers anything.
|
|
|
34
34
|
- **Browser-only widgets render nothing on the server.** `PdfReader`,
|
|
35
35
|
`PdfSidebar`, `PdfPageList`, `PdfThumbnailSidebar` and the mermaid diagrams
|
|
36
36
|
inside `Markdown` load their dependency with `import()` and are wrapped in
|
|
37
|
-
`clientOnly`. They appear after hydration, with no mismatch.
|
|
37
|
+
`clientOnly`. They appear after hydration, with no mismatch. `Camera` is
|
|
38
|
+
`clientOnly` too: it has no lazy dependency, but `getUserMedia` has no
|
|
39
|
+
server side.
|
|
38
40
|
- **Two entries are the exceptions, on purpose.** They are not in any barrel,
|
|
39
41
|
and the build fails if another entry reaches their dependencies:
|
|
40
42
|
- `@nxgt/material/print` reaches `@react-pdf/renderer` and pdfkit, whose
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/roadmap.md
CHANGED
|
@@ -6,7 +6,27 @@ the version something shipped in.
|
|
|
6
6
|
|
|
7
7
|
## Now
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- **Spinner `light` colour renders** — a `Spinner` with `color="light"` shows
|
|
10
|
+
its light ring, for loading states on dark or coloured surfaces.
|
|
11
|
+
- **Range filters announce their field name** — the slider thumbs and the
|
|
12
|
+
`Min`/`Max` inputs of a `slider-range` or `number-range` filter are named
|
|
13
|
+
after the field, so a screen reader says which range is changing.
|
|
14
|
+
- **Alert keeps long content inside** — a long title, description or unbroken
|
|
15
|
+
string wraps inside the `Alert` instead of running past its edge.
|
|
16
|
+
- **Stepper: unique ids, announced steps and truncated titles** — two steppers
|
|
17
|
+
on one page no longer share ids, a screen reader hears which step is current
|
|
18
|
+
and whether each is done or in error, and a long step title truncates.
|
|
19
|
+
- **Layouts that hold long content** — `ListTile`, `StatCard` and `Banner`
|
|
20
|
+
keep long titles, values and actions inside their box instead of overflowing.
|
|
21
|
+
- **The same exports as `@nxgt/material-vue`** — `Camera`, `NativeSelect`, the
|
|
22
|
+
`InputOTP` parts, `PaginationFirst` and `PaginationLast`, `TableEmpty`,
|
|
23
|
+
`DialogScrollContent` and `ToastAction`, `Button`'s sizes, and the
|
|
24
|
+
`RadioGroupField`, `SliderField` and `SwitchField` names, so code moves
|
|
25
|
+
between the two libraries without renaming.
|
|
26
|
+
- **DataTable loading and empty states** — skeleton rows while the data loads
|
|
27
|
+
(the rows already shown stay in place, dimmed, during a refresh), the table
|
|
28
|
+
announced as busy, and your own content when there are no rows, told
|
|
29
|
+
whether a filter emptied it.
|
|
10
30
|
|
|
11
31
|
## Next
|
|
12
32
|
|
|
@@ -15,6 +35,12 @@ Nothing in progress right now; see Next.
|
|
|
15
35
|
- **Previews that cannot go stale** — a CI check that fails when a story changes
|
|
16
36
|
but its preview image does not, so the picture in the docs is the component
|
|
17
37
|
you install.
|
|
38
|
+
- **DateRangeField presets and time** — one-click ranges such as Today, Last 7
|
|
39
|
+
days and This month, and a time of day on each end of the range.
|
|
40
|
+
- **TreeSelectField** — pick one or several items from a tree inside a form
|
|
41
|
+
field.
|
|
42
|
+
- **FilePreview** — preview an image, PDF, video or text file, chosen by its
|
|
43
|
+
type, in one component.
|
|
18
44
|
|
|
19
45
|
## Later
|
|
20
46
|
|
|
@@ -30,11 +56,14 @@ Nothing in progress right now; see Next.
|
|
|
30
56
|
The `Switch` row's misspelt `data-filed` attribute goes too: select
|
|
31
57
|
`[data-checked="true"]` instead. `DateField` / `DateRangeField` stop
|
|
32
58
|
accepting a day rule as `disabled`: pass it as `disabledDays`.
|
|
59
|
+
- **3.0: the same component names as `@nxgt/material-vue`** — the labelled
|
|
60
|
+
fields take the names both libraries share (`RadioGroupField`,
|
|
61
|
+
`SliderField`, `SwitchField`, today aliases of `RadioGroup`, `Slider` and
|
|
62
|
+
`Switch`), so code reads the same in React and Vue. Both names work until
|
|
63
|
+
then, and neither is deprecated yet.
|
|
33
64
|
- **Multi-month calendar with custom caption** — the month, caption and grid
|
|
34
65
|
slots together with `numberOfMonths` greater than one; today that combination
|
|
35
66
|
is not supported.
|
|
36
|
-
- **Range filters in generated filter forms** — slider ranges and number ranges
|
|
37
|
-
rendered by the schema-driven filter fields, not only by hand-written ones.
|
|
38
67
|
- **Ready-made documents** — invoice, quote, receipt, report, certificate and
|
|
39
68
|
label-sheet templates that take your data and return a document, their labels
|
|
40
69
|
translated.
|
|
@@ -219,5 +248,10 @@ Nothing in progress right now; see Next.
|
|
|
219
248
|
chat and markdown. 1.16.1.
|
|
220
249
|
- **`MentionList`** — mention suggestions with local search, for chat and the
|
|
221
250
|
rich text editor. 1.16.0.
|
|
251
|
+
- **Range filters in generated filter forms** — slider ranges and number ranges
|
|
252
|
+
rendered by the schema-driven filter fields: a `Filter` schema field with
|
|
253
|
+
`type: 'slider-range'` or `'number-range'` renders a two-thumb slider or a
|
|
254
|
+
`Min`/`Max` pair. 1.0.0.
|
|
255
|
+
([filters](components/data-table-filter-and-lists.md))
|
|
222
256
|
|
|
223
257
|
Older releases: the [version list on npm](https://www.npmjs.com/package/@nxgt/material?activeTab=versions).
|
package/docs/troubleshooting.md
CHANGED
|
@@ -77,6 +77,7 @@ words in your error.
|
|
|
77
77
|
**Runtime**
|
|
78
78
|
|
|
79
79
|
- [`must be used within` / `must be used inside` (context providers)](#must-be-used-within--must-be-used-inside)
|
|
80
|
+
- [`Camera components must be used inside <Camera>`](#camera-components-must-be-used-inside-camera)
|
|
80
81
|
- [`Clipboard is not available`](#clipboard-is-not-available)
|
|
81
82
|
- [`Upload failed with status 403`](#upload-failed-with-status-403)
|
|
82
83
|
- [`Network error during upload`](#network-error-during-upload)
|
|
@@ -94,6 +95,7 @@ words in your error.
|
|
|
94
95
|
- [A test no longer finds a chip's remove button by the exact name `Remove`](#a-test-no-longer-finds-a-chips-remove-button-by-the-exact-name-remove)
|
|
95
96
|
- [A test no longer finds a pagination, breadcrumb or other built-in label by its English name](#a-test-no-longer-finds-a-pagination-breadcrumb-or-other-built-in-label-by-its-english-name)
|
|
96
97
|
- [A test no longer finds an icon badge by role `button`, or an `Icon` named "Icon {name}" inside a button](#a-test-no-longer-finds-an-icon-badge-by-role-button-or-an-icon-named-icon-name-inside-a-button)
|
|
98
|
+
- [A button named "Icon plus Add" is now named "Add"](#a-button-named-icon-plus-add-is-now-named-add)
|
|
97
99
|
- [`An invalid form control with name='…' is not focusable` (an `UploadField`)](#an-invalid-form-control-with-name-is-not-focusable)
|
|
98
100
|
- [A `DateField` or `DateRangeField` with `disabled` still opens](#a-datefield-or-daterangefield-with-disabled-still-opens)
|
|
99
101
|
- [An `id`, `aria-describedby` or `data-testid` on a `DateField`, `DateRangeField` or `TimeField` is not on the trigger](#an-id-aria-describedby-or-data-testid-on-a-datefield-daterangefield-or-timefield-is-not-on-the-trigger)
|
|
@@ -109,6 +111,7 @@ words in your error.
|
|
|
109
111
|
- [`ConfirmDialog` buttons stay in English](#confirmdialog-buttons-stay-in-english)
|
|
110
112
|
- [A `ConfirmationDialog` opened with `open` no longer closes](#a-confirmationdialog-opened-with-open-no-longer-closes)
|
|
111
113
|
- [A menu item selected by `[data-variant=destructive]` stops matching](#a-menu-item-selected-by-data-variantdestructive-stops-matching)
|
|
114
|
+
- [A `Stepper` step selected by `#step-N` stops matching](#a-stepper-step-selected-by-step-n-stops-matching)
|
|
112
115
|
- [Keys typed right after opening a dialog, sheet or drawer land inside it](#keys-typed-right-after-opening-a-dialog-sheet-or-drawer-land-inside-it)
|
|
113
116
|
|
|
114
117
|
**PDF reader**
|
|
@@ -1213,6 +1216,61 @@ function ActiveCount() {
|
|
|
1213
1216
|
</FilterProvider>;
|
|
1214
1217
|
```
|
|
1215
1218
|
|
|
1219
|
+
### `Camera components must be used inside <Camera>`
|
|
1220
|
+
|
|
1221
|
+
**When:** on render, from `useCamera()` or `useCameraContext()` called in a
|
|
1222
|
+
component of yours.
|
|
1223
|
+
**Why:** both read the context `Camera` provides to its own parts (the video,
|
|
1224
|
+
the capture button, the toggle). `Camera` renders those parts itself and takes
|
|
1225
|
+
no children, so no component of yours is ever below its provider: the hooks
|
|
1226
|
+
serve its internals, and throw anywhere else. A second copy of
|
|
1227
|
+
`@nxgt/material` gives the same message (see
|
|
1228
|
+
[Invalid hook call](#invalid-hook-call-hooks-can-only-be-called-inside-of-the-body-of-a-function-component)).
|
|
1229
|
+
**Fix:** render `<Camera />` as it is, or build your own webcam UI on the
|
|
1230
|
+
helpers, which need no provider:
|
|
1231
|
+
|
|
1232
|
+
```tsx
|
|
1233
|
+
import { capturePhotoFromVideo, isCameraSupported } from '@nxgt/material/components';
|
|
1234
|
+
import { useEffect, useRef, useState } from 'react';
|
|
1235
|
+
|
|
1236
|
+
const SETTINGS = { resolution: { width: 1280, height: 720 }, quality: 0.9, format: 'jpeg' as const, mirror: false };
|
|
1237
|
+
|
|
1238
|
+
export function Snapshot() {
|
|
1239
|
+
const video = useRef<HTMLVideoElement>(null);
|
|
1240
|
+
const [photo, setPhoto] = useState<string | null>(null);
|
|
1241
|
+
|
|
1242
|
+
useEffect(() => {
|
|
1243
|
+
if (!isCameraSupported()) return;
|
|
1244
|
+
let stream: MediaStream | undefined;
|
|
1245
|
+
let cancelled = false;
|
|
1246
|
+
const stop = (s: MediaStream) => s.getTracks().forEach((track) => track.stop());
|
|
1247
|
+
navigator.mediaDevices.getUserMedia({ video: true }).then((s) => {
|
|
1248
|
+
// Unmounted before the permission prompt resolved: release the camera.
|
|
1249
|
+
if (cancelled) return stop(s);
|
|
1250
|
+
stream = s;
|
|
1251
|
+
if (video.current) video.current.srcObject = s;
|
|
1252
|
+
});
|
|
1253
|
+
return () => {
|
|
1254
|
+
cancelled = true;
|
|
1255
|
+
if (stream) stop(stream);
|
|
1256
|
+
};
|
|
1257
|
+
}, []);
|
|
1258
|
+
|
|
1259
|
+
const capture = () => {
|
|
1260
|
+
const result = capturePhotoFromVideo(video.current, { isStreaming: true, captureSettings: SETTINGS });
|
|
1261
|
+
if (result?.capturedImage) setPhoto(result.capturedImage);
|
|
1262
|
+
};
|
|
1263
|
+
|
|
1264
|
+
return (
|
|
1265
|
+
<>
|
|
1266
|
+
<video ref={video} autoPlay playsInline muted />
|
|
1267
|
+
<button type="button" onClick={capture}>Capture</button>
|
|
1268
|
+
{photo && <img src={photo} alt="Captured" />}
|
|
1269
|
+
</>
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
```
|
|
1273
|
+
|
|
1216
1274
|
### `Clipboard is not available`
|
|
1217
1275
|
|
|
1218
1276
|
**When:** calling `copyToClipboard(value)` on a page served over plain `http`
|
|
@@ -1629,6 +1687,33 @@ If you passed an `IconButton` as `ConfirmationDialog`'s `icon`, pass the glyph
|
|
|
1629
1687
|
alone and style the badge with `iconProps={{ variant, color }}`: the dialog
|
|
1630
1688
|
already wraps it in a badge, and yours is now a button inside a `<span>`.
|
|
1631
1689
|
|
|
1690
|
+
### A button named "Icon plus Add" is now named "Add"
|
|
1691
|
+
|
|
1692
|
+
**When:** after upgrading to 2.5.0, `getByRole('button', { name: 'Icon plus Add' })`
|
|
1693
|
+
(or any name that starts with "Icon {name}") finds nothing, or a
|
|
1694
|
+
`getByRole('img', { name: 'Icon plus' })` inside a `Button` that also has text.
|
|
1695
|
+
**Why:** an `Icon` is named "Icon {name}" by default, and a `Button` takes its
|
|
1696
|
+
name from its content, so a `Button` holding an `Icon` and the text "Add" was
|
|
1697
|
+
announced "Icon plus Add". A `Button` with text, an `aria-label` or an
|
|
1698
|
+
`aria-labelledby` now hides an `Icon` inside it (`aria-hidden="true"`), as a
|
|
1699
|
+
named `IconButton` already did. A `Button` holding only an `Icon` still reads
|
|
1700
|
+
it, since that is the only name it has; give it an `aria-label` instead. Text
|
|
1701
|
+
a component produces from its props (a translation component) is not seen:
|
|
1702
|
+
pass the button an `aria-label` then. Text inside an `aria-hidden` element
|
|
1703
|
+
(a decorative count, `<span aria-hidden="true">3</span>`) does not name the
|
|
1704
|
+
button either, so beside it the `Icon` stays named.
|
|
1705
|
+
**Fix:** select the button by its text:
|
|
1706
|
+
|
|
1707
|
+
```ts
|
|
1708
|
+
await page.getByRole('button', { name: 'Add' }).click();
|
|
1709
|
+
```
|
|
1710
|
+
|
|
1711
|
+
and name an icon-only button yourself:
|
|
1712
|
+
|
|
1713
|
+
```tsx
|
|
1714
|
+
<Button aria-label="Add"><Icon name="plus" /></Button>
|
|
1715
|
+
```
|
|
1716
|
+
|
|
1632
1717
|
### `An invalid form control with name='…' is not focusable`
|
|
1633
1718
|
|
|
1634
1719
|
**When:** submitting a form with an empty `required` `UploadField`, before
|
|
@@ -1930,6 +2015,30 @@ render `data-variant="error"`, and both use the `error` colour.
|
|
|
1930
2015
|
[data-slot='menubar-item'][data-variant='error'] { font-weight: 600; }
|
|
1931
2016
|
```
|
|
1932
2017
|
|
|
2018
|
+
### A `Stepper` step selected by `#step-N` stops matching
|
|
2019
|
+
|
|
2020
|
+
**When:** after upgrading to 2.5.0, a test or a stylesheet that targets a
|
|
2021
|
+
step by its id — `#step-0`, `page.locator('#step-1')`,
|
|
2022
|
+
`document.getElementById('step-2')` — finds nothing.
|
|
2023
|
+
**Why:** every `Stepper` gave its steps the same ids, `step-0`, `step-1`, …,
|
|
2024
|
+
so two steppers on one page produced duplicate ids, which is invalid HTML and
|
|
2025
|
+
breaks any `aria-*` reference to them. Each id is now prefixed with one
|
|
2026
|
+
unique to its stepper (React's `useId`). The steps are also an `<ol>` of
|
|
2027
|
+
`<li>` now, and the current one carries `aria-current="step"`. A selector
|
|
2028
|
+
written against the old markup, such as `div[data-slot="stepper-step"]`,
|
|
2029
|
+
stops matching too, since a step is now an `li`: drop the tag
|
|
2030
|
+
(`[data-slot="stepper-step"]`).
|
|
2031
|
+
**Fix:** select by `data-pw`, which did not change — `step-N`, or
|
|
2032
|
+
`<name>-step-N` when you passed `name`:
|
|
2033
|
+
|
|
2034
|
+
```ts
|
|
2035
|
+
await page.locator('[data-pw="step-1"]').click();
|
|
2036
|
+
// with <Stepper name="checkout" … />
|
|
2037
|
+
await page.locator('[data-pw="checkout-step-1"]').click();
|
|
2038
|
+
// or the current step
|
|
2039
|
+
await expect(page.locator('[aria-current="step"]')).toContainText('Shipping');
|
|
2040
|
+
```
|
|
2041
|
+
|
|
1933
2042
|
### Keys typed right after opening a dialog, sheet or drawer land inside it
|
|
1934
2043
|
|
|
1935
2044
|
**When:** after upgrading, a test (or code of yours) that opens a `Dialog`,
|
package/package.json
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import "./src-CJ8DNrI1.js";
|
|
2
|
-
import "./chunk-ZIGJFQKS-HDJ6e6OJ.js";
|
|
3
|
-
import "./chunk-O7XYJQB3-C87zK8E5.js";
|
|
4
|
-
import "./chunk-742MDFTN-nG_ROTnT.js";
|
|
5
|
-
import "./chunk-MBY4JIJT-IA2XBafa.js";
|
|
6
|
-
import "./chunk-7PRAP22T-DIiYRAce.js";
|
|
7
|
-
import "./chunk-7INBJB4K-Dg64CbhT.js";
|
|
8
|
-
import "./chunk-5DYCD2WN-Bqi3iL5i.js";
|
|
9
|
-
import "./chunk-Z7XXMR3K-DcqSgI9Q.js";
|
|
10
|
-
import "./chunk-GWA4HPMP-CmsxMX3A.js";
|
|
11
|
-
import "./chunk-XXDRQBXY-Bq4sFnO2.js";
|
|
12
|
-
import "./chunk-WEXAMYUT-C-QP0DFq.js";
|
|
13
|
-
import "./chunk-UA2S7LBM-B0LGFGu3.js";
|
|
14
|
-
import "./chunk-GNY47TPC-vOPvRE_8.js";
|
|
15
|
-
import "./chunk-LNGE3PJU-DJjGTS7O.js";
|
|
16
|
-
import { n as e } from "./chunk-7M6MHVWA-DEh1Crrc.js";
|
|
17
|
-
export { e as diagram };
|