@nxgt/material 2.12.0 → 2.14.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.
Files changed (134) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +19 -6
  3. package/dist/{_Uint8Array-azThpjEP.js → _Uint8Array-QCcRMhXd.js} +1 -1
  4. package/dist/{_overRest-DMn4Dt71.js → _overRest-C21ZmKyH.js} +1 -1
  5. package/dist/{async-slice-DhZDjEtw.js → async-slice-TBZ6Taw2.js} +2 -2
  6. package/dist/{blockDiagram-BEXU5L5S-CnFXw-7E.js → blockDiagram-BEXU5L5S-Cn9cG3vt.js} +2 -2
  7. package/dist/{chunk-3FUC2YCW-3vMiTgTl.js → chunk-3FUC2YCW-XrT5pZo5.js} +2 -2
  8. package/dist/{chunk-7M6MHVWA-NPaJJ6Gy.js → chunk-7M6MHVWA-Uf-8NCRF.js} +1 -1
  9. package/dist/{chunk-GNY47TPC-DCFqYc_z.js → chunk-GNY47TPC-CGgBmY0O.js} +4 -4
  10. package/dist/{chunk-NETBCI7D-BB4rjLSQ.js → chunk-NETBCI7D-g-jPsM9q.js} +1 -1
  11. package/dist/{chunk-Z7XXMR3K-vmLFt9Cr.js → chunk-Z7XXMR3K-BzO1efzA.js} +1 -1
  12. package/dist/{classDiagram-v2-NBCMYWYE-eUVWeh_6.js → classDiagram-v2-NBCMYWYE-7WgpfVcn.js} +2 -2
  13. package/dist/components/components.css +13 -2
  14. package/dist/components/ui/feedback/spinner/spinner.css +28 -0
  15. package/dist/components/ui/fields/otp-field/input-otp.css +48 -0
  16. package/dist/components/ui/media/pdf-viewer/pdf-viewer.css +24 -0
  17. package/dist/components/ui/overlays/drawer/vaul-drawer.css +199 -0
  18. package/dist/components/ui/overlays/drawer/vaul-keyframes.css +95 -0
  19. package/dist/{components-BYxsGbyO.js → components-qTUjhFuo.js} +116423 -127245
  20. package/dist/components.js +6 -6
  21. package/dist/{dagre-6A5THRUB-LmEtEC93.js → dagre-6A5THRUB-BOuRxJdA.js} +11 -11
  22. package/dist/{diagram-22UHCM2B-BwARaM-x.js → diagram-22UHCM2B-CF0vLJbn.js} +2 -2
  23. package/dist/{diagram-3UASUU5V-BkeqK_e8.js → diagram-3UASUU5V-RLo-MTfV.js} +1 -1
  24. package/dist/dnd.js +1 -1
  25. package/dist/{elk-276RUBZZ-DL4pxCSP.js → elk-276RUBZZ-Dr2yBM7k.js} +3 -3
  26. package/dist/{erDiagram-OPXOYQCR-Ajlj2lcb.js → erDiagram-OPXOYQCR-l7kPZ4FO.js} +2 -2
  27. package/dist/{field-blur-DgfIJceK.js → field-blur-BKGVq9_k.js} +865 -870
  28. package/dist/{flowDiagram-KWPJA3E3-BOEc9KSn.js → flowDiagram-KWPJA3E3-BQEK3XTl.js} +3 -3
  29. package/dist/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
  30. package/dist/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
  31. package/dist/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
  32. package/dist/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
  33. package/dist/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
  34. package/dist/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
  35. package/dist/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
  36. package/dist/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
  37. package/dist/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
  38. package/dist/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
  39. package/dist/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
  40. package/dist/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
  41. package/dist/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
  42. package/dist/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
  43. package/dist/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
  44. package/dist/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
  45. package/dist/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
  46. package/dist/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
  47. package/dist/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
  48. package/dist/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
  49. package/dist/{ganttDiagram-FUAMR5RP-DdxLcLNx.js → ganttDiagram-FUAMR5RP-WYm8XV7U.js} +1 -1
  50. package/dist/{graphlib-DLjE3QPd.js → graphlib-CbcZi5bx.js} +6 -6
  51. package/dist/hooks.js +26 -26
  52. package/dist/{i18n-provider-CQL7fqLy.js → i18n-provider-D5hkauu6.js} +1 -1
  53. package/dist/i18n.js +6 -6
  54. package/dist/{isArrayLikeObject-CO-5KAzG.js → isArrayLikeObject-COiPdhFP.js} +3 -3
  55. package/dist/{isEmpty-Bl1drJE7.js → isEmpty-B7UlCLih.js} +3 -3
  56. package/dist/{journeyDiagram-ZHPQQLJL-BIzB9BVn.js → journeyDiagram-ZHPQQLJL-p90NnenY.js} +1 -1
  57. package/dist/lib/components/ui/fields/attributes/attribute-form/consts.d.ts +5 -1
  58. package/dist/lib/components/ui/fields/attributes/attribute-form/types.d.ts +2 -2
  59. package/dist/lib/components/ui/fields/opening-hours/consts.d.ts +12 -1
  60. package/dist/lib/components/ui/fields/search-field/index.d.ts +2 -2
  61. package/dist/lib/components/ui/media/image-cropper/image-crop-aspects.d.ts +6 -0
  62. package/dist/lib/components/ui/media/image-cropper/use-frame-size.d.ts +4 -0
  63. package/dist/lib/components/ui/primitives/index.d.ts +1 -0
  64. package/dist/lib/components/ui/primitives/nonce-provider/index.d.ts +1 -0
  65. package/dist/lib/components/ui/primitives/nonce-provider/nonce-provider.d.ts +20 -0
  66. package/dist/lib/components/ui/selection/autocomplete/autocomplete.d.ts +2 -34
  67. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-announcer.d.ts +10 -0
  68. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-chips.d.ts +8 -3
  69. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-list-status.d.ts +13 -0
  70. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-popup.d.ts +26 -0
  71. package/dist/lib/components/ui/selection/autocomplete/types.d.ts +72 -0
  72. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-filter.d.ts +13 -0
  73. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-handlers.d.ts +17 -0
  74. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-search.d.ts +8 -0
  75. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-status.d.ts +28 -0
  76. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-values.d.ts +27 -0
  77. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete.d.ts +29 -0
  78. package/dist/lib/components/ui/selection/radio-group/radio-group.d.ts +7 -4
  79. package/dist/lib/components/ui/widgets/rich-text-editor/extensions/extensions.d.ts +2 -2
  80. package/dist/lib/components/ui/widgets/rich-text-editor/use-rich-text-editor.d.ts +17 -0
  81. package/dist/lib/hooks/index.d.ts +1 -0
  82. package/dist/lib/hooks/use-csp-nonce.d.ts +16 -0
  83. package/dist/lib/i18n/resources/en/en.d.ts +4 -0
  84. package/dist/lib/i18n/resources/en/material.json.d.ts +5 -1
  85. package/dist/lib/i18n/resources/fr/fr.d.ts +4 -0
  86. package/dist/lib/i18n/resources/fr/material.json.d.ts +5 -1
  87. package/dist/lib/i18n/resources/resources.d.ts +8 -0
  88. package/dist/lib/i18n/translator.d.ts +1 -1
  89. package/dist/lib/lib/index.d.ts +1 -0
  90. package/dist/lib/vite/index.d.ts +3 -2
  91. package/dist/lib.js +32 -31
  92. package/dist/main.js +16 -16
  93. package/dist/map.js +2 -2
  94. package/dist/material.css +25 -1
  95. package/dist/{merge-CwwtONfp.js → merge-KEf_u3Zy.js} +4 -4
  96. package/dist/{mermaid.core-6yWyNdZa.js → mermaid.core-CW8DO1T1.js} +21 -21
  97. package/dist/{mindmap-definition-NLK3R4M7-aHycauO9.js → mindmap-definition-NLK3R4M7-DJcCg9PL.js} +2 -2
  98. package/dist/{pieDiagram-5QR66LMP-DEiooyQu.js → pieDiagram-5QR66LMP-b_dHXzkV.js} +1 -1
  99. package/dist/print.js +2 -2
  100. package/dist/{quadrantDiagram-O4NWA36T-CLlaqEdw.js → quadrantDiagram-O4NWA36T-BSqAcBs7.js} +1 -1
  101. package/dist/{requirementDiagram-PLB6GJNP-DD74Pa6_.js → requirementDiagram-PLB6GJNP-BcxkmIFp.js} +2 -2
  102. package/dist/{sankeyDiagram-IPEJSGJF-DAR9G0yC.js → sankeyDiagram-IPEJSGJF-aD5r_n1q.js} +1 -1
  103. package/dist/{sortable-provider-D4S-mqLP.js → sortable-provider-C8mr3XP2.js} +1 -1
  104. package/dist/{stateDiagram-v2-GCMORJYK-pti7a_r-.js → stateDiagram-v2-GCMORJYK-Ct9ylaoP.js} +2 -2
  105. package/dist/{stored-language-BW32owje.js → stored-language-g121Yv0w.js} +10 -2
  106. package/dist/{swimlanes-2SLR337P-D10pSc8m.js → swimlanes-2SLR337P-BwCqwEoR.js} +3 -3
  107. package/dist/{swimlanesDiagram-TC7HE7FX-B8HgZod5.js → swimlanesDiagram-TC7HE7FX-WxBGUt0w.js} +3 -3
  108. package/dist/{timeline-definition-EJHVYXUP-D8CWcNj3.js → timeline-definition-EJHVYXUP-5Q8GU13Q.js} +1 -1
  109. package/dist/{translate-material-ZEzF-LKH.js → translate-material-D3Hp_AI5.js} +2 -2
  110. package/dist/{translator-BfqVlNGW.js → translator-BMFC0lBV.js} +1 -1
  111. package/dist/{use-heading-spy-DvZVu15k.js → use-heading-spy-wlOv87q3.js} +65 -59
  112. package/dist/{use-material-translation-B2GUtxUY.js → use-material-translation-BGFp1yXp.js} +2 -2
  113. package/dist/{usecaseDiagram-POWQR4AR-DIdH4Oia.js → usecaseDiagram-POWQR4AR-CzGOd4ic.js} +2 -2
  114. package/dist/vite.js +2 -2
  115. package/dist/{xychartDiagram-PMCCYNJV-aecZdRhd.js → xychartDiagram-PMCCYNJV-Bh_1zZoW.js} +1 -1
  116. package/docs/README.md +1 -0
  117. package/docs/components/data-display.md +5 -0
  118. package/docs/components/form-fields.md +14 -0
  119. package/docs/components/primitives.md +31 -0
  120. package/docs/components/selection-and-choice.md +74 -4
  121. package/docs/getting-started.md +5 -1
  122. package/docs/guides/csp.md +408 -0
  123. package/docs/guides/helpers.md +2 -0
  124. package/docs/guides/theming.md +27 -0
  125. package/docs/roadmap.md +21 -1
  126. package/docs/troubleshooting.md +220 -0
  127. package/lib/components/components.css +13 -2
  128. package/lib/components/ui/feedback/spinner/spinner.css +28 -0
  129. package/lib/components/ui/fields/otp-field/input-otp.css +48 -0
  130. package/lib/components/ui/media/pdf-viewer/pdf-viewer.css +24 -0
  131. package/lib/components/ui/overlays/drawer/vaul-drawer.css +199 -0
  132. package/lib/components/ui/overlays/drawer/vaul-keyframes.css +95 -0
  133. package/package.json +9 -4
  134. package/dist/lib/components/ui/fields/search-field/debounce.d.ts +0 -5
@@ -8,11 +8,11 @@ import "./chunk-7PRAP22T-0C6_H8Li.js";
8
8
  import { a as g, i as _, o as v, r as y } from "./chunk-J5ZVWO5B-DmRRmhon.js";
9
9
  import "./chunk-7INBJB4K-k-I67NSP.js";
10
10
  import "./chunk-5DYCD2WN-Cd1zazNp.js";
11
- import "./chunk-Z7XXMR3K-vmLFt9Cr.js";
11
+ import "./chunk-Z7XXMR3K-BzO1efzA.js";
12
12
  import { t as ee } from "./chunk-XXDRQBXY-dKXxu_aj.js";
13
13
  import { t as te } from "./chunk-WEXAMYUT-CbFOhEve.js";
14
14
  import "./chunk-UA2S7LBM-ChuksCDg.js";
15
- import { r as ne, t as re } from "./chunk-GNY47TPC-DCFqYc_z.js";
15
+ import { r as ne, t as re } from "./chunk-GNY47TPC-CGgBmY0O.js";
16
16
  //#region ../../node_modules/chevrotain/node_modules/lodash-es/_freeGlobal.js
17
17
  var ie = typeof global == "object" && global && global.Object === Object && global, ae = typeof self == "object" && self && self.Object === Object && self, b = ie || ae || Function("return this")(), oe = b.Symbol, se = Object.prototype, ce = se.hasOwnProperty, le = se.toString, ue = oe ? oe.toStringTag : void 0;
18
18
  function de(e) {
package/dist/vite.js CHANGED
@@ -1,2 +1,2 @@
1
- import { pdfkitStandardFonts as e } from "@nxgt/material-core/vite";
2
- export { e as pdfkitStandardFonts };
1
+ import { cspAssetsInlineLimit as e, pdfkitStandardFonts as t } from "@nxgt/material-core/vite";
2
+ export { e as cspAssetsInlineLimit, t as pdfkitStandardFonts };
@@ -1,4 +1,4 @@
1
- import { at as e, rt as t, st as n } from "./components-BYxsGbyO.js";
1
+ import { at as e, rt as t, st as n } from "./components-qTUjhFuo.js";
2
2
  import { n as r } from "./src-CsLQInsN.js";
3
3
  import { i } from "./chunk-ZIGJFQKS-9VXArDKF.js";
4
4
  import { n as a } from "./chunk-Y2CYZVJY-BAad7w_K.js";
package/docs/README.md CHANGED
@@ -14,6 +14,7 @@ detail: every option, the second example, and the traps.
14
14
  | [Printing](guides/printing.md) | You render PDF documents with `@nxgt/material/print`, turn them into a download, a new tab or a server response, write them as plain data, or bundle them into an SSR server |
15
15
  | [Maps](guides/maps.md) | You render Leaflet maps or a location field with `@nxgt/material/map` |
16
16
  | [SSR](guides/ssr.md) | Your app renders on a server and bundles this package |
17
+ | [Content Security Policy](guides/csp.md) | Your app sends a strict CSP: the directives to use, the components that still inject a `<style>`, the zod and KaTeX settings, self-hosting a font, the per-request nonce |
17
18
  | [Helpers](guides/helpers.md) | You need a utility, hook, slice factory or type from `/lib`, `/hooks`, `/i18n`, `/models`, `/types` or `/dnd` |
18
19
  | [Troubleshooting](troubleshooting.md) | Something failed and you have an error message or a symptom |
19
20
  | [Roadmap](roadmap.md) | You want to know what is planned, what shipped, and what will not be done |
@@ -532,6 +532,11 @@ import { Markdown } from '@nxgt/material/components';
532
532
  Mermaid diagrams render in the browser only, after mount; on the server the
533
533
  fence is left empty.
534
534
 
535
+ Math needs no import of its own: KaTeX's CSS is in the stylesheet, and its
536
+ fonts are files the stylesheet loads (`dist/fonts/katex/` on the compiled
537
+ route), allowed by `font-src 'self'`. Math in system fonts means the fonts
538
+ were not served ([troubleshooting](../troubleshooting.md#math-renders-in-the-system-fonts)).
539
+
535
540
  **Storybook:** `Data display/Markdown` · **Vue:** `Markdown`, `MermaidDiagram`
536
541
 
537
542
  ---
@@ -744,6 +744,8 @@ Parts: `AttributesField` (the editable list), `AttributesList` (display with
744
744
  attribute), `AttributeValuesForm` (fill values for a set of attributes).
745
745
  Form wrappers: `AttributesFormField` (the definitions),
746
746
  `AttributeValuesFormFieldSet` and `AttributeValueFormField` (the values).
747
+ `AttributeForm` builds its zod schema when it first renders, not when the
748
+ module loads ([CSP](../guides/csp.md#zod-and-unsafe-eval)).
747
749
 
748
750
  | Prop | Type | Default | Effect |
749
751
  | --- | --- | --- | --- |
@@ -856,6 +858,18 @@ const hours: OpeningHour[] = [
856
858
  Parts: `OpeningHours` (the list) and `OpeningHoursDialog` (add / edit).
857
859
  `OpeningHoursItem`, a day row bound to a react-hook-form array, is
858
860
  **deprecated** and removed in 3.0: use `OpeningHoursFormField`.
861
+ `openingHoursSchema()` returns the zod schema of one `OpeningHour`
862
+ (`dayOfWeek` coerced from a string), built on the first call; the
863
+ `OpeningHoursSchema` constant is **deprecated** and removed in 3.0. Under a
864
+ strict CSP, call `z.config({ jitless: true })` first: see
865
+ [CSP](../guides/csp.md#zod-and-unsafe-eval).
866
+
867
+ ```ts
868
+ import { openingHoursSchema } from '@nxgt/material/components';
869
+ import { z } from 'zod';
870
+
871
+ const schema = z.object({ hours: z.array(openingHoursSchema()) });
872
+ ```
859
873
 
860
874
  | Prop | Type | Default | Effect |
861
875
  | --- | --- | --- | --- |
@@ -27,6 +27,7 @@ other pages are built from these.
27
27
  | <img src="../previews/primitives-toggle.webp" width="96" alt=""> | [Toggle](#toggle) | Pressable on / off button |
28
28
  | <img src="../previews/primitives-togglegroup.webp" width="96" alt=""> | [ToggleGroup](#togglegroup) | Single or multiple toggle group |
29
29
  | | [Activity](#activity) | React `<Activity>` switched by a boolean |
30
+ | | [NonceProvider](#nonceprovider) | Gives the page's CSP nonce to every component below |
30
31
 
31
32
  ## Accordion
32
33
 
@@ -773,3 +774,33 @@ import { Activity } from '@nxgt/material/components';
773
774
  | `visible` | `boolean` | `false` | `true` renders with `mode="visible"`; `false` hides the subtree and keeps its state |
774
775
 
775
776
  **Storybook:** — · **Vue:** `Activity`
777
+
778
+ ## NonceProvider
779
+
780
+ No preview: it draws nothing of its own, it hands the page's Content Security
781
+ Policy nonce down.
782
+
783
+ Gives this request's CSP nonce to every component below it: the `<style>`
784
+ elements of `ScrollArea` and `Select`, the stylesheet tiptap injects for
785
+ `RichTextEditor`, and, on the client, the scroll lock of every modal. Render it once, at the root, on the server and on
786
+ the client, with the same nonce.
787
+
788
+ ```tsx
789
+ import { NonceProvider } from '@nxgt/material/components';
790
+
791
+ <NonceProvider nonce={nonce}>
792
+ <App />
793
+ </NonceProvider>;
794
+ ```
795
+
796
+ | Prop | Type | Default | Effect |
797
+ | --- | --- | --- | --- |
798
+ | `nonce` | `string \| undefined` | — | the nonce of the page's `style-src-elem 'nonce-…'`; `undefined` or `''` inherits the nearest outer `NonceProvider`'s nonce (none at the root) |
799
+
800
+ `useCspNonce()` (`@nxgt/material/hooks`) reads it in a component of your own;
801
+ `getCspNonce()` and `setCspNonce()` (`@nxgt/material/lib`) read and keep the
802
+ page's nonce outside React. The server entry, the client entry and what each
803
+ component needs are in the [Content Security Policy
804
+ guide](../guides/csp.md#nonceprovider).
805
+
806
+ **Storybook:** `Primitives/NonceProvider` · **Vue:** `NonceProvider`
@@ -250,7 +250,7 @@ export function Framework() {
250
250
 
251
251
  ![Autocomplete](../previews/selection-choice-autocomplete.webp)
252
252
 
253
- Single or multiple (chips) autocomplete with free-solo values and a query callback.
253
+ Single or multiple (chips) autocomplete with free-solo values, accent-insensitive matching and a debounced server search.
254
254
 
255
255
  ```tsx
256
256
  import { Autocomplete } from '@nxgt/material/components';
@@ -286,15 +286,79 @@ Parts: `Autocomplete`, `AutocompleteChips` (the chips row it uses in
286
286
  | `mode` | `'single' \| 'multiple'` | — | required; decides the `value` type |
287
287
  | `value` | `string` / `string[]` | — | required, per `mode` |
288
288
  | `onValueChange` | `(value?: string \| null) => void` / `(value?: string[] \| null) => void` | — | per `mode` |
289
- | `options` | `T[]` (`{ value, label }` by default) | — | the items |
289
+ | `options` | `T[]` (`{ value, label }` by default) | `[]` | the items; with `onSearch`, the list before a search and the selected options' labels |
290
290
  | `getOptionValue`, `getOptionLabel` | `(option: T) => string` | `.value`, `.label` | for other option shapes |
291
+ | `filter` | `'accent-insensitive' \| 'none' \| (option: T, query: string) => boolean` | `'accent-insensitive'`, `'none'` with `onSearch` | how the list is filtered as the user types: label or value, ignoring case and accents (`e` finds "École") in the provider's language; `'none'` shows `options` as given; a function gets the trimmed query, never an empty one |
291
292
  | `freeSolo` | `boolean` | — | accept a typed value that is not an option |
292
- | `onQueryChange` | `(query: string) => void` | — | fetch options as the user types |
293
+ | `onSearch` | `(query: string, { signal }: { signal: AbortSignal }) => Promise<T[]>` | — | server search: called once typing pauses; its results become the list. Each search aborts the previous one, and a response that lands after a newer one is dropped |
294
+ | `onSearchError` | `(error: unknown) => void` | — | `onSearch` rejected (an abort is not a rejection) |
295
+ | `minQueryLength` | `number` | `1` | `onSearch` waits for this many characters; below it the list says "Type at least N characters to search." |
296
+ | `debounceMs` | `number` | `250` | the pause before `onSearch` and `onQueryChange` run |
297
+ | `onQueryChange` | `(query: string) => void` | — | the query once typing pauses — fetch `options` yourself here |
298
+ | `loading` | `boolean` | — | with `onQueryChange`: "Loading…" in the list, `aria-busy` on the listbox, announced |
299
+ | `searchError` | `ReactNode` | — | with `onQueryChange`: shown and announced in place of the options |
300
+ | `empty` | `ReactNode` | "No option found." | text when nothing matches |
293
301
  | `actions` | `ReactNode \| ((query: string) => ReactNode)` | — | buttons at the end of the list ("Create …") |
294
302
  | `required` | `boolean` | — | the asterisk, and `aria-required` on the `combobox` input, which the label names |
295
303
  | `disabled` | `boolean` | — | disables the input and each chip's remove button |
296
304
  | `error` | `boolean` | — | error border on the chips box, error helper text, `aria-invalid` on the input |
297
305
 
306
+ ### Searching a server
307
+
308
+ `onSearch` takes the query and an `AbortSignal`; hand the signal to `fetch`
309
+ so a search the user typed past is cancelled. The field shows "Loading…",
310
+ keeps the last results until the new ones arrive, and never lets an older
311
+ response overwrite a newer one, even from an API that ignores the signal.
312
+ Pass the selected option in `options` so its chip has a label before any
313
+ search; a picked option keeps its chip once a new search no longer returns
314
+ it.
315
+
316
+ ```tsx
317
+ import { Autocomplete } from '@nxgt/material/components';
318
+ import { useState } from 'react';
319
+
320
+ type Person = { value: string; label: string };
321
+
322
+ async function searchPeople(query: string, { signal }: { signal: AbortSignal }) {
323
+ const response = await fetch(`/api/people?q=${encodeURIComponent(query)}`, { signal });
324
+ if (!response.ok) throw new Error(`Search failed: ${response.status}`);
325
+ return (await response.json()) as Person[];
326
+ }
327
+
328
+ export function PersonField() {
329
+ const [value, setValue] = useState('');
330
+ return (
331
+ <Autocomplete<Person>
332
+ mode="single"
333
+ label="Person"
334
+ placeholder="Search by name"
335
+ value={value}
336
+ onValueChange={(next) => setValue(next ?? '')}
337
+ onSearch={searchPeople}
338
+ minQueryLength={2}
339
+ debounceMs={300}
340
+ />
341
+ );
342
+ }
343
+ ```
344
+
345
+ With `onSearch`, `minQueryLength` defaults to 1, `debounceMs` to 250 and `filter` to `'none'`
346
+ (the server filters). A rejected search shows and announces "The search
347
+ failed. Try again." by itself and hands the error to `onSearchError`; an abort is
348
+ never an error. Type your search with `AutocompleteSearch<T>` (its second
349
+ argument is `AutocompleteSearchContext`, `{ signal }`); `AutocompleteFilter<T>`
350
+ and `AutocompleteSearchProps<T>` are exported from `@nxgt/material/components` too.
351
+
352
+ To fetch it yourself instead, keep `options` and `loading` in your state,
353
+ update them from `onQueryChange`, and pass `filter="none"` when the server
354
+ already filtered.
355
+
356
+ **Accessibility.** A polite live region (`role="status"`) says "Loading…",
357
+ then "3 results" or "No results" (or the failure) once the search answers or
358
+ typing pauses — never on every keystroke. The listbox is `aria-busy` while
359
+ loading. The strings are `material.autocomplete.loading`, `.results`,
360
+ `.search-error` and `.min-query`. The keyboard works as before.
361
+
298
362
  **Emits:** `string | null` in `single` mode, `string[]` in `multiple`.
299
363
  **Form wrapper:** `AutocompleteFormField` — `control` and `name`, see
300
364
  [forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
@@ -409,7 +473,7 @@ full naming alignment is planned for 3.0.
409
473
 
410
474
  | Prop | Type | Default | Effect |
411
475
  | --- | --- | --- | --- |
412
- | `options` | `{ value: string; label: ReactNode }[]` | — | the radios |
476
+ | `options` | `{ value: string; label: ReactNode; disabled?: boolean }[]` | — | the radios; `disabled` on an option disables only it |
413
477
  | `value` / `defaultValue` | `string` | — | Radix radio group state |
414
478
  | `onValueChange` | `(value: string) => void` | — | |
415
479
  | `orientation` | `'vertical' \| 'horizontal'` | `'horizontal'` | layout |
@@ -422,6 +486,12 @@ before the first radio, and `error` sets `aria-invalid` on it (a `radiogroup`
422
486
  may carry it; your own `aria-invalid` wins). Your own `aria-label` /
423
487
  `aria-labelledby` wins, and your `aria-describedby` ids come first.
424
488
 
489
+ An option's type is exported as `RadioGroupOption`. A disabled option (`disabled: true` on it) is announced as disabled, skipped by
490
+ the arrow keys (as the Radix primitives do) and drawn with a muted label and a
491
+ faded radio; the label keeps 4.5:1 contrast in both themes. The group's own
492
+ `disabled` disables every option. An option disabled while it is the current
493
+ value stays checked.
494
+
425
495
  **Emits:** `string`.
426
496
  **Form wrapper:** `RadioGroupFormField` — `control` and `name`, see
427
497
  [forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
@@ -30,7 +30,9 @@ does not import that entry never has them in its tree.
30
30
  | `@nxgt/material/print` | `bun add @react-pdf/renderer@^4.9 react-pdf-tailwind` ([printing](guides/printing.md)) |
31
31
  | `@nxgt/material/map` | `bun add leaflet react-leaflet @react-leaflet/core @adamscybot/react-leaflet-component-marker react-geolocated` ([maps](guides/maps.md)) |
32
32
 
33
- The tarball contains the built `dist/`, `lib/styles.css`, the component
33
+ The tarball contains the built `dist/` (with KaTeX's fonts in
34
+ `dist/fonts/katex/`, which the compiled stylesheet loads),
35
+ `lib/styles.css`, the component
34
36
  stylesheets it imports (`lib/components/**/*.css`), `lib/map/map.css`,
35
37
  these docs and `CHANGELOG.md` (from the release after 2.6.1). There is no
36
38
  `postinstall` build step.
@@ -45,6 +47,8 @@ In the app's root stylesheet, in this order:
45
47
  @source "../node_modules/@nxgt/material/";
46
48
  ```
47
49
 
50
+ Toaster, Drawer and InputOTP take their CSS from this stylesheet: without it they render unstyled.
51
+
48
52
  - **`styles.css` is source, not compiled CSS.** It holds the Tailwind v4
49
53
  `@theme` layer (colours, `--radius`, the `dark` variant, base styles), so it
50
54
  runs through *your* Tailwind build. Nothing injects it: import it