@nxgt/material-vue 0.24.0 → 0.25.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 (139) hide show
  1. package/README.md +17 -19
  2. package/dist/{Button-DOcT4Pej.js → Button-C1aaI0Gw.js} +1 -1
  3. package/dist/{HoverCardTrigger-WQe85PCH.js → HoverCardTrigger-JtHn0IFQ.js} +2 -2
  4. package/dist/{camera-BNbLUPb1.js → camera-BacVyXl0.js} +13 -13
  5. package/dist/{components-YlYuHLEI.js → components-DRBKkOqk.js} +15333 -13879
  6. package/dist/components.js +8 -8
  7. package/dist/composables.js +2 -2
  8. package/dist/dnd.js +1 -1
  9. package/dist/form-scope-VxlJuvFe.js +43 -0
  10. package/dist/{i18n-provider-DTuxugHO.js → i18n-provider-D0oYv0XS.js} +1 -1
  11. package/dist/i18n.js +4 -4
  12. package/dist/{icon-button-DKLuZ2c-.js → icon-button-B2FQtIHS.js} +1 -1
  13. package/dist/lib/components/ui/collections/tree/use-tree.d.ts +2 -0
  14. package/dist/lib/components/ui/dates/date-field/date-field.vue.d.ts +10 -1
  15. package/dist/lib/components/ui/dates/date-field/date-form-field.vue.d.ts +3 -1
  16. package/dist/lib/components/ui/dates/date-field/day-matcher.d.ts +26 -0
  17. package/dist/lib/components/ui/dates/date-field/index.d.ts +1 -0
  18. package/dist/lib/components/ui/dates/date-field/partials/date-field-content.vue.d.ts +2 -0
  19. package/dist/lib/components/ui/dates/date-range-field/date-range-field.utils.d.ts +11 -0
  20. package/dist/lib/components/ui/dates/date-range-field/date-range-field.vue.d.ts +26 -1
  21. package/dist/lib/components/ui/dates/date-range-field/date-range-form-field.vue.d.ts +3 -1
  22. package/dist/lib/components/ui/dates/date-range-field/index.d.ts +1 -0
  23. package/dist/lib/components/ui/dates/date-range-field/partials/date-range-calendar.vue.d.ts +5 -0
  24. package/dist/lib/components/ui/dates/date-range-field/partials/date-range-field-trigger.vue.d.ts +2 -0
  25. package/dist/lib/components/ui/dates/date-range-field/partials/date-range-presets.vue.d.ts +12 -0
  26. package/dist/lib/components/ui/dates/date-range-field/partials/date-range-times.vue.d.ts +13 -0
  27. package/dist/lib/components/ui/dates/date-range-field/use-date-range-presets.d.ts +23 -0
  28. package/dist/lib/components/ui/dates/time-picker/time-picker-utils.d.ts +7 -0
  29. package/dist/lib/components/ui/display/accordion-card/accordion-card.vue.d.ts +29 -1
  30. package/dist/lib/components/ui/display/avatar/Avatar.vue.d.ts +14 -3
  31. package/dist/lib/components/ui/display/avatar/index.d.ts +1 -0
  32. package/dist/lib/components/ui/display/cards/cards.gallery.d.ts +5 -9
  33. package/dist/lib/components/ui/display/qrcode/qrcode.vue.d.ts +5 -3
  34. package/dist/lib/components/ui/display/status-indicator/status-tone.d.ts +3 -0
  35. package/dist/lib/components/ui/feedback/confirmation-dialog/async-form-scope.d.ts +15 -0
  36. package/dist/lib/components/ui/feedback/confirmation-dialog/confirmation-dialog.vue.d.ts +37 -24
  37. package/dist/lib/components/ui/feedback/confirmation-dialog/index.d.ts +1 -1
  38. package/dist/lib/components/ui/feedback/confirmation-dialog/partials/confirmation-dialog-form-error.vue.d.ts +3 -0
  39. package/dist/lib/components/ui/fields/attributes/form-schema.d.ts +24 -22
  40. package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
  41. package/dist/lib/components/ui/fields/number-field/number-field.vue.d.ts +2 -0
  42. package/dist/lib/components/ui/fields/slider/partials/slider-field-row.vue.d.ts +32 -0
  43. package/dist/lib/components/ui/fields/slider/slider-field.vue.d.ts +22 -1
  44. package/dist/lib/components/ui/fields/slider/slider-form-field.vue.d.ts +13 -1
  45. package/dist/lib/components/ui/forms/form-scope-schema.d.ts +43 -0
  46. package/dist/lib/components/ui/forms/form-scope.vue.d.ts +33 -0
  47. package/dist/lib/components/ui/media/file-preview/file-preview-pdf.lazy.d.ts +8 -0
  48. package/dist/lib/components/ui/media/file-preview/file-preview.vue.d.ts +9 -0
  49. package/dist/lib/components/ui/media/file-preview/index.d.ts +3 -0
  50. package/dist/lib/components/ui/media/file-preview/partials/file-preview-body.vue.d.ts +21 -0
  51. package/dist/lib/components/ui/media/file-preview/partials/file-preview-card.vue.d.ts +15 -0
  52. package/dist/lib/components/ui/media/file-preview/partials/file-preview-download.vue.d.ts +11 -0
  53. package/dist/lib/components/ui/media/file-preview/partials/file-preview-media.vue.d.ts +17 -0
  54. package/dist/lib/components/ui/media/file-preview/partials/file-preview-pdf.vue.d.ts +21 -0
  55. package/dist/lib/components/ui/media/file-preview/partials/file-preview-status.vue.d.ts +22 -0
  56. package/dist/lib/components/ui/media/file-preview/partials/file-preview-text.vue.d.ts +16 -0
  57. package/dist/lib/components/ui/media/file-preview/types.d.ts +19 -0
  58. package/dist/lib/components/ui/media/file-preview/use-file-preview.d.ts +21 -0
  59. package/dist/lib/components/ui/media/file-preview/use-text-preview.d.ts +17 -0
  60. package/dist/lib/components/ui/media/files-form-field/files-form-field.vue.d.ts +29 -0
  61. package/dist/lib/components/ui/media/files-form-field/index.d.ts +2 -0
  62. package/dist/lib/components/ui/media/image-cropper/partials/image-cropper-canvas.vue.d.ts +1 -1
  63. package/dist/lib/components/ui/media/index.d.ts +2 -0
  64. package/dist/lib/components/ui/media/pdf-viewer/partials/pdf-page.vue.d.ts +1 -1
  65. package/dist/lib/components/ui/media/pdf-viewer/partials/pdf-reader-body.vue.d.ts +2 -2
  66. package/dist/lib/components/ui/media/upload/network-upload-field/use-image-preview.d.ts +9 -1
  67. package/dist/lib/components/ui/primitives/tabs/Tabs.vue.d.ts +3 -0
  68. package/dist/lib/components/ui/primitives/tabs/index.d.ts +1 -0
  69. package/dist/lib/components/ui/primitives/tabs/tabs-variant.d.ts +9 -0
  70. package/dist/lib/components/ui/selection/autocomplete/autocomplete-form-field.vue.d.ts +31 -5
  71. package/dist/lib/components/ui/selection/autocomplete/autocomplete-items.d.ts +23 -0
  72. package/dist/lib/components/ui/selection/autocomplete/autocomplete.vue.d.ts +48 -13
  73. package/dist/lib/components/ui/selection/autocomplete/filter-options.d.ts +2 -1
  74. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-box.vue.d.ts +13 -1
  75. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-list.vue.d.ts +17 -3
  76. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-items.d.ts +15 -0
  77. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-open.d.ts +15 -0
  78. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-texts.d.ts +14 -0
  79. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete.d.ts +34 -0
  80. package/dist/lib/components/ui/selection/index.d.ts +1 -0
  81. package/dist/lib/components/ui/selection/select-field/select-field.vue.d.ts +16 -1
  82. package/dist/lib/components/ui/selection/select-field/select-form-field.vue.d.ts +16 -2
  83. package/dist/lib/components/ui/selection/tree-select-field/index.d.ts +3 -0
  84. package/dist/lib/components/ui/selection/tree-select-field/partials/tree-select-panel.vue.d.ts +27 -0
  85. package/dist/lib/components/ui/selection/tree-select-field/partials/tree-select-trigger.vue.d.ts +28 -0
  86. package/dist/lib/components/ui/selection/tree-select-field/tree-select-field.vue.d.ts +8 -0
  87. package/dist/lib/components/ui/selection/tree-select-field/tree-select-form-field.vue.d.ts +8 -0
  88. package/dist/lib/components/ui/selection/tree-select-field/tree-select.utils.d.ts +29 -0
  89. package/dist/lib/components/ui/selection/tree-select-field/types.d.ts +28 -0
  90. package/dist/lib/components/ui/selection/tree-select-field/use-tree-select-field.d.ts +19 -0
  91. package/dist/lib/components/ui/widgets/code-editor/code-editor-options.d.ts +4 -1
  92. package/dist/lib/components/ui/widgets/code-editor/code-editor.vue.d.ts +7 -0
  93. package/dist/lib/components/ui/widgets/code-editor/index.d.ts +1 -0
  94. package/dist/lib/components/ui/widgets/code-editor/monaco-editor.vue.d.ts +3 -0
  95. package/dist/lib/i18n/resources/en/en.d.ts +33 -0
  96. package/dist/lib/i18n/resources/en/material.json.d.ts +36 -3
  97. package/dist/lib/i18n/resources/fr/fr.d.ts +33 -0
  98. package/dist/lib/i18n/resources/fr/material.json.d.ts +36 -3
  99. package/dist/lib/i18n/resources/resources.d.ts +66 -0
  100. package/dist/lib/i18n/translator.d.ts +1 -1
  101. package/dist/lib/nuxt/component-names.d.ts +1 -1
  102. package/dist/lib.js +2 -2
  103. package/dist/main.js +17 -17
  104. package/dist/map.js +4 -4
  105. package/dist/material-vue.css +1 -1
  106. package/dist/{pdf-outline-sidebar-0I-LGrgY.js → pdf-outline-sidebar-jJfhNjnN.js} +2 -2
  107. package/dist/{pdf-page-C74-6lyZ.js → pdf-page-BW1hXzPu.js} +1 -1
  108. package/dist/{pdf-page-list-DPsquYPY.js → pdf-page-list-D277RtWS.js} +2 -2
  109. package/dist/{pdf-reader-Bkpp7UZ7.js → pdf-reader-BReUTsn_.js} +73 -71
  110. package/dist/{pdf-sidebar-BH-1jvy-.js → pdf-sidebar-Bk6PowY1.js} +3 -3
  111. package/dist/{pdf-thumbnail-sidebar-ByqNaYfz.js → pdf-thumbnail-sidebar-BcNDFsks.js} +3 -3
  112. package/dist/{pdf-toolbar-BRnmzddi.js → pdf-toolbar-CHjaMhH9.js} +4 -4
  113. package/dist/pdf.js +2 -2
  114. package/dist/{sortable-provider-CC9eO1nm.js → sortable-provider-C83VBfGl.js} +2 -2
  115. package/dist/{stored-language-N_UoPJsY.js → stored-language-CC_vdcCN.js} +72 -6
  116. package/dist/{stx-player-BGKIDqNX.js → stx-player-BMQ1D2Li.js} +20 -20
  117. package/dist/{translator-CU0jKvzB.js → translator-B-X_1taI.js} +1 -1
  118. package/dist/{use-heading-spy-Bw1LIAkC.js → use-heading-spy-C_ySuvTl.js} +1 -1
  119. package/dist/{use-material-translation-8hUB2M-S.js → use-material-translation-B6sIk0mF.js} +3 -3
  120. package/docs/components/README.md +2 -1
  121. package/docs/components/data-display.md +51 -10
  122. package/docs/components/data-table-filter-and-lists.md +4 -0
  123. package/docs/components/date-and-time.md +116 -9
  124. package/docs/components/feedback-and-status.md +56 -4
  125. package/docs/components/form-fields.md +15 -5
  126. package/docs/components/media-and-files.md +119 -5
  127. package/docs/components/navigation-and-layout.md +2 -1
  128. package/docs/components/primitives.md +21 -4
  129. package/docs/components/rich-widgets.md +20 -3
  130. package/docs/components/selection-and-choice.md +135 -9
  131. package/docs/guides/forms.md +3 -4
  132. package/docs/guides/i18n.md +44 -4
  133. package/docs/previews/date-time-daterangefield.webp +0 -0
  134. package/docs/previews/media-files-filepreview.webp +0 -0
  135. package/docs/previews/selection-choice-treeselectfield.webp +0 -0
  136. package/docs/roadmap.md +43 -37
  137. package/docs/troubleshooting.md +110 -1
  138. package/lib/nuxt/component-names.ts +4 -0
  139. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./utils-CykWQ0iJ.js";
2
- import { Gt as t, Kt as n, Qt as r, Xt as i, Yt as a, Zt as o, qt as s, xp as c } from "./components-YlYuHLEI.js";
3
- import { t as l } from "./use-material-translation-8hUB2M-S.js";
2
+ import { $t as t, Jt as n, Qt as r, Yt as i, Zt as a, en as o, jp as s, qt as c } from "./components-DRBKkOqk.js";
3
+ import { t as l } from "./use-material-translation-B6sIk0mF.js";
4
4
  import { createBlock as u, createCommentVNode as d, createElementBlock as f, createElementVNode as p, createTextVNode as m, createVNode as h, defineComponent as g, normalizeClass as _, openBlock as v, toDisplayString as y, unref as b, withCtx as x } from "vue";
5
5
  //#region lib/components/ui/media/stx-player/partials/stx-player-frame.vue?vue&type=script&setup=true&lang.ts
6
6
  var S = ["data-layout", "aria-label"], C = {
@@ -19,50 +19,50 @@ var S = ["data-layout", "aria-label"], C = {
19
19
  Array
20
20
  ] }
21
21
  },
22
- setup(s) {
23
- let { containerRef: g, state: w } = r();
22
+ setup(i) {
23
+ let { containerRef: g, state: w } = o();
24
24
  function T(e) {
25
25
  g.value = e instanceof HTMLElement ? e : null;
26
26
  }
27
27
  let { t: E } = l();
28
- return (r, l) => (v(), f("section", {
28
+ return (o, l) => (v(), f("section", {
29
29
  ref: T,
30
30
  "data-slot": "stx-player",
31
- "data-layout": s.layout,
31
+ "data-layout": i.layout,
32
32
  "aria-label": b(w).current?.title ?? b(E)("material.stx-player.label", "Media player"),
33
- class: _(b(e)("@container rounded outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50", r.$props.class))
34
- }, [s.layout === "mini" ? (v(), u(a, { key: 0 })) : (v(), f("div", {
33
+ class: _(b(e)("@container rounded outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50", o.$props.class))
34
+ }, [i.layout === "mini" ? (v(), u(a, { key: 0 })) : (v(), f("div", {
35
35
  key: 1,
36
- class: _(b(e)("grid gap-3", s.showQueue && "@min-[768px]:grid-cols-[minmax(0,1fr)_18rem] @min-[768px]:items-stretch"))
36
+ class: _(b(e)("grid gap-3", i.showQueue && "@min-[768px]:grid-cols-[minmax(0,1fr)_18rem] @min-[768px]:items-stretch"))
37
37
  }, [p("div", { class: _(b(e)("bg-card overflow-hidden rounded border", b(w).current?.kind === "audio" && "flex flex-col gap-4 p-6")) }, [
38
- b(w).current?.kind === "audio" ? (v(), f("div", C, [h(c, { variant: "title-medium" }, {
38
+ b(w).current?.kind === "audio" ? (v(), f("div", C, [h(s, { variant: "title-medium" }, {
39
39
  default: x(() => [m(y(b(w).current.title), 1)]),
40
40
  _: 1
41
- }), b(w).current.artist ? (v(), u(c, {
41
+ }), b(w).current.artist ? (v(), u(s, {
42
42
  key: 0,
43
43
  class: "text-muted-foreground"
44
44
  }, {
45
45
  default: x(() => [m(y(b(w).current.artist) + " " + y(b(w).current.album ? ` · ${b(w).current.album}` : ""), 1)]),
46
46
  _: 1
47
47
  })) : d("", !0)])) : d("", !0),
48
- h(t, null, {
49
- default: x(() => [b(w).current?.kind === "audio" ? d("", !0) : (v(), u(o, {
48
+ h(c, null, {
49
+ default: x(() => [b(w).current?.kind === "audio" ? d("", !0) : (v(), u(t, {
50
50
  key: 0,
51
51
  tone: "overlay"
52
52
  }, {
53
- trailing: x(() => [h(i, { tone: "overlay" })]),
53
+ trailing: x(() => [h(r, { tone: "overlay" })]),
54
54
  _: 1
55
55
  }))]),
56
56
  _: 1
57
57
  }),
58
- b(w).current?.kind === "audio" ? (v(), u(o, {
58
+ b(w).current?.kind === "audio" ? (v(), u(t, {
59
59
  key: 1,
60
60
  tone: "surface"
61
61
  }, {
62
- trailing: x(() => [h(i, { tone: "surface" })]),
62
+ trailing: x(() => [h(r, { tone: "surface" })]),
63
63
  _: 1
64
64
  })) : d("", !0)
65
- ], 2), s.showQueue ? (v(), u(n, {
65
+ ], 2), i.showQueue ? (v(), u(n, {
66
66
  key: 0,
67
67
  class: "min-h-72"
68
68
  })) : d("", !0)], 2))], 10, S));
@@ -99,16 +99,16 @@ var S = ["data-layout", "aria-label"], C = {
99
99
  emits: ["trackChange"],
100
100
  setup(e, { emit: t }) {
101
101
  let n = e, r = t;
102
- function i(e, t) {
102
+ function a(e, t) {
103
103
  n.onTrackChange?.(e, t), r("trackChange", e, t);
104
104
  }
105
- return (t, r) => (v(), u(s, {
105
+ return (t, r) => (v(), u(i, {
106
106
  tracks: e.tracks,
107
107
  "current-index": e.currentIndex,
108
108
  "default-index": e.defaultIndex,
109
109
  "auto-play": e.autoPlay,
110
110
  loop: e.loop,
111
- "on-track-change": i
111
+ "on-track-change": a
112
112
  }, {
113
113
  default: x(() => [h(w, {
114
114
  layout: e.layout,
@@ -1,4 +1,4 @@
1
- import { a as e, i as t, r as n, t as r } from "./stored-language-N_UoPJsY.js";
1
+ import { a as e, i as t, r as n, t as r } from "./stored-language-CC_vdcCN.js";
2
2
  import { t as i } from "./get-CTzJAwfc.js";
3
3
  //#region ../../node_modules/@formatjs/fast-memoize/index.js
4
4
  function a(e, t) {
@@ -1,4 +1,4 @@
1
- import { a as e } from "./i18n-provider-DTuxugHO.js";
1
+ import { a as e } from "./i18n-provider-D0oYv0XS.js";
2
2
  import { c as t, d as n, f as r, l as i, n as a, u as o } from "./number.utils-Cy1z6qI7.js";
3
3
  import { computed as s, getCurrentInstance as c, onMounted as l, ref as u, toValue as d } from "vue";
4
4
  import { useIntersectionObserver as f, useMediaQuery as p, useSSRWidth as m, useStorage as h } from "@vueuse/core";
@@ -1,6 +1,6 @@
1
- import "./stored-language-N_UoPJsY.js";
2
- import { a as e } from "./i18n-provider-DTuxugHO.js";
3
- import { i as t, r as n } from "./translator-CU0jKvzB.js";
1
+ import "./stored-language-CC_vdcCN.js";
2
+ import { a as e } from "./i18n-provider-D0oYv0XS.js";
3
+ import { i as t, r as n } from "./translator-B-X_1taI.js";
4
4
  //#region lib/i18n/translate-material.ts
5
5
  function r(e, r, i, a) {
6
6
  if (e) for (let t of /* @__PURE__ */ new Set([e, e.split("-")[0]])) {
@@ -98,10 +98,11 @@ Telling the user what happened: alert, banner, confirm dialogs, empty state, pro
98
98
 
99
99
  ## [Media & files](media-and-files.md)
100
100
 
101
- Uploads, file lists, images and cropping, galleries, carousel, PDF, audio/video player, camera, maps, print documents. 14 entries.
101
+ Uploads, file lists, file previews, images and cropping, galleries, carousel, PDF, audio/video player, camera, maps, print documents. 15 entries.
102
102
 
103
103
  [<img src="../previews/media-files-uploadfield.webp" height="72" alt="UploadField">](media-and-files.md#uploadfield)
104
104
  [<img src="../previews/media-files-filelist.webp" height="72" alt="FileList">](media-and-files.md#filelist)
105
+ [<img src="../previews/media-files-filepreview.webp" height="72" alt="FilePreview">](media-and-files.md#filepreview)
105
106
  [<img src="../previews/media-files-imagecropper.webp" height="72" alt="ImageCropper">](media-and-files.md#imagecropper)
106
107
  [<img src="../previews/media-files-pdfreader.webp" height="72" alt="PdfReader">](media-and-files.md#pdfreader)
107
108
 
@@ -35,7 +35,7 @@ from `@nxgt/material-vue/components`.
35
35
 
36
36
  ![Avatar](../previews/data-display-avatar.webp)
37
37
 
38
- Round image with a fallback that shows while it loads or when it fails.
38
+ Round image with a fallback that shows while it loads or when it fails, and an optional presence dot.
39
39
 
40
40
  ```vue
41
41
  <script setup lang="ts">
@@ -53,10 +53,23 @@ import { Avatar, AvatarFallback, AvatarImage } from '@nxgt/material-vue/componen
53
53
  Parts: `Avatar` (the round frame), `AvatarImage` (`src`, `alt`),
54
54
  `AvatarFallback` (initials or an icon in its slot). Size and shape come from
55
55
  `class` (`size-12 rounded-md`). Unlike the React `Avatar`, there is no
56
- `src` / `fallback` / `status` shorthand: compose the parts, and put a
57
- [`StatusIndicator`](#statusindicator) beside it for an online dot.
56
+ `src` / `fallback` shorthand: compose the parts.
58
57
 
59
- **Storybook:** `Data display/Avatar` · **React:** `Avatar`, `AvatarImage`, `AvatarFallback`
58
+ `status` adds a presence dot at the bottom right, named for a screen reader
59
+ (`role="img"`): `success` "Online", `neutral` "Offline", `warning` "Away",
60
+ `error` "Busy", any other tone "Active" (`material.avatar.*`, translated).
61
+ `status-pulse` animates it. Attributes still land on the avatar itself.
62
+
63
+ ```vue
64
+ <template>
65
+ <Avatar status="success" class="size-10">
66
+ <AvatarImage src="/people/ada.png" alt="Ada Lovelace" />
67
+ <AvatarFallback>AL</AvatarFallback>
68
+ </Avatar>
69
+ </template>
70
+ ```
71
+
72
+ **Storybook:** `Data display/Avatar` (story `Status`) · **React:** `Avatar`, `AvatarImage`, `AvatarFallback`
60
73
 
61
74
  ---
62
75
 
@@ -463,7 +476,7 @@ import { Icon, ListTile } from '@nxgt/material-vue/components';
463
476
 
464
477
  ![AccordionCard](../previews/data-display-accordioncard.webp)
465
478
 
466
- Renders `items` (title, content) as a styled accordion.
479
+ Renders `items` (title, content) as a styled accordion, with `#header`, `#title` and `#content` slots.
467
480
 
468
481
  ```vue
469
482
  <script setup lang="ts">
@@ -492,11 +505,37 @@ const open = ref('shipping');
492
505
  | `showIcon` | `boolean` | `true` | decorative chevron on each header, inside the trigger `<button>` |
493
506
  | `v-model` | `string \| string[]` | — | open item(s); omit for an uncontrolled accordion |
494
507
 
495
- Titles and content are strings; React's `renderHeader` has no Vue
496
- equivalent — build a custom one from the `Accordion` primitives.
508
+ Titles and content are strings; the slots take markup instead (React's
509
+ `title` / `content` nodes and `renderHeader`). Each receives `{ item, index }`:
510
+
511
+ | Slot | Replaces |
512
+ | --- | --- |
513
+ | `#title` | the header's text, beside the chevron |
514
+ | `#content` | the panel's content |
515
+ | `#header` (also `title`) | the whole header: one element, which becomes the trigger — a `<button>`, as React's `renderHeader` returns |
516
+
517
+ ```vue
518
+ <script setup lang="ts">
519
+ import { ref } from 'vue';
520
+ import { AccordionCard } from '@nxgt/material-vue/components';
521
+
522
+ const open = ref('shipping');
523
+ const items = [
524
+ { key: 'shipping', title: 'Shipping', content: 'Ships within two working days.' },
525
+ { key: 'returns', title: 'Returns', content: 'Free returns for 30 days.' },
526
+ ];
527
+ </script>
528
+
529
+ <template>
530
+ <AccordionCard v-model="open" :items="items">
531
+ <template #title="{ item, index }">{{ index + 1 }}. {{ item.title }}</template>
532
+ <template #content="{ item }"><p><strong>{{ item.title }}:</strong> {{ item.content }}</p></template>
533
+ </AccordionCard>
534
+ </template>
535
+ ```
497
536
 
498
537
  **Emits:** `update:modelValue` with `string | string[]`.
499
- **Storybook:** `Data display/AccordionCard` · **React:** `AccordionCard`
538
+ **Storybook:** `Data display/AccordionCard` (story `Slots`) · **React:** `AccordionCard`
500
539
 
501
540
  ---
502
541
 
@@ -816,7 +855,9 @@ import { QRCode } from '@nxgt/material-vue/components';
816
855
 
817
856
  <template>
818
857
  <QRCode value="https://example.com/tickets/DS-184" :size="160" level="H">
819
- <img src="/logo.svg" alt="" width="32" />
858
+ <template #logo>
859
+ <img src="/logo.svg" alt="" width="32" />
860
+ </template>
820
861
  </QRCode>
821
862
  </template>
822
863
  ```
@@ -829,7 +870,7 @@ import { QRCode } from '@nxgt/material-vue/components';
829
870
  | `fgColor`, `bgColor` | `string` | `'#000000'`, `'#FFFFFF'` | colours |
830
871
  | `title` | `string` | — | accessible label of the SVG |
831
872
  | `aria-label` | `string` | — | the SVG's name too, and wins over `title` |
832
- | default slot | — | — | the logo, centred over the code on a white pad |
873
+ | `#logo` | slot | — | the logo, centred over the code on a white pad (React's `logo`); the default slot still works |
833
874
 
834
875
  The code is an `<svg role="img">` named by `aria-label`, else `title`, else
835
876
  "QR code" (`material.qrcode.label`, translated):
@@ -392,6 +392,10 @@ own) and `TreeItem` (one row); `findTreeNode` and `flattenVisibleTree`.
392
392
  Arrow keys move and expand / collapse, Home / End jump, Enter and Space
393
393
  select.
394
394
 
395
+ Tab lands on the focused row, or on the first visible row when that one is
396
+ filtered or folded away. `useTree` returns that row as `tabbableId`: give it
397
+ `tabindex="0"` in a tree you build yourself.
398
+
395
399
  **Emits:** `update:selectedIds` and `update:expandedIds` with `string[]`, `select`.
396
400
  **Storybook:** `Data table, filter & lists/Tree` · **React:** `Tree`, `TreeItem`, `useTree`
397
401
 
@@ -70,7 +70,7 @@ No form wrapper — use `DateFormField`.
70
70
 
71
71
  ![DateField](../previews/date-time-datefield.webp)
72
72
 
73
- Labelled trigger that opens a calendar popover, with an optional time picker.
73
+ Labelled trigger that opens a calendar popover, with an optional time picker and `disabled-days`.
74
74
 
75
75
  ```vue
76
76
  <script setup lang="ts">
@@ -81,7 +81,12 @@ const delivery = ref<Date | undefined>();
81
81
  </script>
82
82
 
83
83
  <template>
84
- <DateField v-model="delivery" label="Delivery date" placeholder="Pick a day" />
84
+ <DateField
85
+ v-model="delivery"
86
+ label="Delivery date"
87
+ placeholder="Pick a day"
88
+ :disabled-days="[{ dayOfWeek: [0] }, { before: new Date() }]"
89
+ />
85
90
  </template>
86
91
  ```
87
92
 
@@ -89,10 +94,12 @@ const delivery = ref<Date | undefined>();
89
94
  | --- | --- | --- | --- |
90
95
  | `v-model` | `Date \| undefined` | — | the selected day, shown in the short `P` format |
91
96
  | `show-time-picker` | `boolean` | — | adds a `TimePicker` and an OK button; the popover stays open |
97
+ | `disabled-days` | `DisabledDays` | — | days that cannot be picked: a `Date`, a `Date[]`, `{ from, to }` (ends included), `{ before }`, `{ after }`, `{ before, after }`, `{ dayOfWeek }` (`0` is Sunday), a `(date) => boolean`, or a list of those (any that matches) — React's `disabledDays` |
92
98
  | `placeholder`, `label`, `helper-text`, `error`, `required`, `disabled` | | — | `placeholder` is the trigger's text; `label` and `helper-text` sit beside it; `error`, `required` and `disabled` its state |
93
99
 
94
- Unlike React, the calendar's own props (bounds, disabled days) are not passed
95
- through; the popover closes on the first pick unless `show-time-picker` is
100
+ Unlike React, the calendar's other props (bounds, the first month) are not
101
+ passed through: use `disabled-days` for both bounds and days. A disabled day
102
+ is greyed out and cannot be picked. The popover closes on the first pick unless `show-time-picker` is
96
103
  set, and picking a day keeps the time of the current value.
97
104
 
98
105
  Attributes you put on the field (`id`, `aria-describedby`, `data-*`,
@@ -113,7 +120,7 @@ before your `aria-describedby` ids.
113
120
  **Emits:** `update:modelValue` with `Date | undefined`.
114
121
  **Form wrapper:** `DateFormField` — `name` inside a vee-validate form, see
115
122
  [forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
116
- **Storybook:** `Date & time/DateField` · **React:** `DateField`, `DateFormField`
123
+ **Storybook:** `Date & time/DateField` (story `DisabledDays`) · **React:** `DateField`, `DateFormField`
117
124
 
118
125
  ---
119
126
 
@@ -121,7 +128,7 @@ before your `aria-describedby` ids.
121
128
 
122
129
  ![DateRangeField](../previews/date-time-daterangefield.webp)
123
130
 
124
- From / to range picked in a popover calendar.
131
+ From / to range picked in a popover calendar, with `disabled-days`, presets and times.
125
132
 
126
133
  ```vue
127
134
  <script setup lang="ts">
@@ -132,17 +139,26 @@ const stay = ref<DateRange | undefined>();
132
139
  </script>
133
140
 
134
141
  <template>
135
- <DateRangeField v-model="stay" label="Stay" placeholder="Check-in – check-out" />
142
+ <DateRangeField
143
+ v-model="stay"
144
+ label="Stay"
145
+ placeholder="Check-in – check-out"
146
+ :disabled-days="{ dayOfWeek: [0] }"
147
+ />
136
148
  </template>
137
149
  ```
138
150
 
139
151
  | Prop | Type | Default | Effect |
140
152
  | --- | --- | --- | --- |
141
153
  | `v-model` | `DateRange \| undefined` | — | `{ from?: Date; to?: Date }` |
154
+ | `disabled-days` | `DisabledDays` | — | days that cannot be picked: a `Date`, a `Date[]`, `{ from, to }` (ends included), `{ before }`, `{ after }`, `{ before, after }`, `{ dayOfWeek }` (`0` is Sunday), a `(date) => boolean`, or a list of those (any that matches) — as on `DateField` |
142
155
  | `placeholder`, `label`, `helper-text`, `error`, `required`, `disabled` | | — | `placeholder` is the trigger's text; `label` and `helper-text` sit beside it; `error`, `required` and `disabled` its state |
156
+ | `presets` | `readonly DateRangePresetOption[]` | — (no list) | one-click ranges beside the calendar: built-in ids (`DATE_RANGE_PRESET_IDS`) and `{ label, range }` of your own |
157
+ | `with-time` | `boolean` | `false` | a time of day on each end, under the calendar, and on the trigger; the popover stays open until OK |
158
+ | `min-date`, `max-date` | `Date` | — | the first and last day that can be picked (Reka's `minValue` / `maxValue`) |
143
159
 
144
160
  It emits on each click (`to` is unset after the first) and closes the popover
145
- once both ends are picked.
161
+ once both ends are picked (with `with-time`, OK closes it).
146
162
 
147
163
  Attributes you put on the field (`id`, `aria-describedby`, `data-*`,
148
164
  listeners) go on the trigger, the element that has focus, not on the field's
@@ -159,11 +175,97 @@ the helper text sit beside the trigger in the field's root `div`
159
175
  (`data-slot="date-range-field"`), not in it; the helper text describes the trigger
160
176
  before your `aria-describedby` ids.
161
177
 
178
+ ### Presets
179
+
180
+ `presets` lists one-click ranges beside the calendar (above it on a narrow
181
+ screen), in the order you give them. A built-in id is translated
182
+ (`material.date-range-field.preset.<id>`); a preset of your own is
183
+ `{ label, range }`, where `range` is `{ from, to }` or `(now) => ({ from, to })`.
184
+
185
+ ```vue
186
+ <script setup lang="ts">
187
+ import { ref } from 'vue';
188
+ import {
189
+ type DateRange,
190
+ DateRangeField,
191
+ type DateRangePreset,
192
+ } from '@nxgt/material-vue/components';
193
+
194
+ const lastQuarter: DateRangePreset = {
195
+ label: 'Last quarter',
196
+ range: (now) => {
197
+ const start = new Date(now.getFullYear(), Math.floor(now.getMonth() / 3) * 3 - 3, 1);
198
+ return { from: start, to: new Date(start.getFullYear(), start.getMonth() + 3, 0) };
199
+ },
200
+ };
201
+ const period = ref<DateRange | undefined>();
202
+ const today = new Date();
203
+ </script>
204
+
205
+ <template>
206
+ <DateRangeField
207
+ v-model="period"
208
+ label="Period"
209
+ :max-date="today"
210
+ :presets="['today', 'last-7-days', 'this-month', 'last-month', lastQuarter]"
211
+ />
212
+ </template>
213
+ ```
214
+
215
+ | Built-in id | Range |
216
+ | --- | --- |
217
+ | `today`, `yesterday` | that day |
218
+ | `last-7-days`, `last-30-days` | ends today and counts it |
219
+ | `this-month`, `this-year` | from the first day to today |
220
+ | `last-month` | the whole previous month |
221
+
222
+ A built-in range starts at 00:00 (and with `with-time` ends at 23:59); your
223
+ own ranges keep the times you gave them. Picking a preset sets the value and
224
+ closes the popover (with `with-time` it stays open); open again, the calendar
225
+ is on that range's month. The list is a group named "Presets"
226
+ (`material.date-range-field.presets`) of buttons in the tab order, and the
227
+ one whose days are the value's has `aria-pressed="true"`. A preset is
228
+ disabled when its range starts before `min-date`, ends after `max-date`, or
229
+ either end is one of `disabled-days`; the days between are not checked, as the
230
+ calendar lets a range span a disabled day.
231
+ The built-in presets use the current date when the popover opens; there is
232
+ no `now` prop, and none is planned. A preset of your own whose end comes
233
+ before its start is disabled.
234
+
235
+ ### Times
236
+
237
+ `with-time` adds two `TimePicker`s under the calendar, in groups named
238
+ "Start time" and "End time" (`material.date-range-field.start-time`,
239
+ `.end-time`), each disabled until its end has a day, and an OK button
240
+ (`material.buttons.ok`) that closes the popover. The trigger shows each end
241
+ with its time (date-fns `Pp`).
242
+
243
+ ```vue
244
+ <script setup lang="ts">
245
+ import { ref } from 'vue';
246
+ import { type DateRange, DateRangeField } from '@nxgt/material-vue/components';
247
+
248
+ const slot = ref<DateRange | undefined>();
249
+ </script>
250
+
251
+ <template>
252
+ <DateRangeField v-model="slot" label="Booking" with-time :presets="['today']" />
253
+ </template>
254
+ ```
255
+
256
+ A day picked on the calendar keeps the time that end had; a new start is
257
+ 00:00 and a new end 23:59, as a built-in preset ends, so a filter on
258
+ `<= to` keeps the last day. On the same day, an end time set before the
259
+ start time becomes the start time.
260
+
261
+ Without `with-time`, a picked day carries the current time of day.
262
+ (`@nxgt/material` gives it 00:00.)
263
+
162
264
  **Emits:** `update:modelValue` with `DateRange | undefined` — `{ from, to }`,
163
265
  where `to` stays `undefined` until the second day is picked.
164
266
  **Form wrapper:** `DateRangeFormField` — `name` inside a vee-validate form, see
165
267
  [forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
166
- **Storybook:** `Date & time/DateRangeField` · **React:** `DateRangeField`, `DateRangeFormField`
268
+ **Storybook:** `Date & time/DateRangeField` (stories `DisabledDays`, `MinMaxDates`, `Presets`, `DisabledPresets`, `WithTime`) · **React:** `DateRangeField`, `DateRangeFormField` (`withTime`, `minDate` / `maxDate`; without time, a picked day is at 00:00 there, the current time of day here)
167
269
 
168
270
  ---
169
271
 
@@ -245,6 +347,11 @@ const time = ref<Date | undefined>(new Date());
245
347
  | `label` | `string` | — | header text |
246
348
  | `#footer` | slot | — | under the inputs (the OK button in `TimeField`) |
247
349
 
350
+ The hours and minutes inputs are named "Hours" and "Minutes"
351
+ (`material.time-picker.hours`, `.minutes`). AM / PM shows the half of the day
352
+ `date` is in, so a date changed from outside shows its own; switching it moves
353
+ the time 12 hours, and a click on the pressed half changes nothing.
354
+
248
355
  **Emits:** `change` with `Date | undefined` (no `v-model`). No form wrapper —
249
356
  use `TimeFormField`.
250
357
  **Storybook:** `Date & time/TimePicker` · **React:** `TimePicker`
@@ -240,14 +240,66 @@ function archive() {}
240
240
  | --- | --- | --- | --- |
241
241
  | `#trigger` slot | — | — | becomes the dialog trigger (`as-child`, no extra button): pass one element, such as a `Button` |
242
242
  | `v-model:open` | `boolean` | uncontrolled | open state; without it the dialog keeps its own |
243
- | `@confirm` | `() => void` | — | emitted on confirm; the dialog then closes |
243
+ | `@confirm` | `(values?) => void` | — | emitted on confirm, with the form's parsed values when there is one; the dialog then closes |
244
+ | `form-options` | `ConfirmationDialogFormOptions` (`{ schema, defaultValues? }`) | — | a form in the dialog, below the default slot — see below |
244
245
  | `color` | `IconButton` colour | `'warning'` | icon and both buttons |
245
246
  | `variant` | `IconButton` variant | `'tonal'` | the icon's button style |
246
247
  | `#icon` slot | — | info icon, in a decorative `aria-hidden` badge | replaces the badge: keep yours decorative (`<IconButton as="span" aria-hidden="true">`), not a button that does nothing |
247
248
  | `confirm-text` / `cancel-text` | `string` | "Confirm" / "Cancel", translated (`material.confirm-dialog.confirm` / `.cancel`) | button text |
248
249
 
249
- Vue's `ConfirmationDialog` has no `formOptions`: to collect values before
250
- confirming, put a form in its default slot or use `CustomDialog`.
250
+ `form-options` puts a form in the confirmation, as React's `formOptions`:
251
+ `schema` is a Zod 4 schema (any Standard Schema, or a vee-validate typed
252
+ schema), `defaultValues` its initial values, and the `#form` slot renders the
253
+ fields — `*-form-field` wrappers in it join the form. The confirm button stays
254
+ disabled until the form is valid, and `@confirm` receives the schema's output
255
+ (transforms applied). The component is generic, so that payload is typed from
256
+ the schema; to type the options apart from the template, import
257
+ `ConfirmationDialogFormOptions<TInput, TOutput>` from
258
+ `@nxgt/material-vue/components`. Being generic, its ref is typed with
259
+ `ComponentExposed`, not `InstanceType`
260
+ ([troubleshooting](../troubleshooting.md#does-not-satisfy-the-constraint-abstract-new-args-any--any)).
261
+
262
+ ```vue
263
+ <script setup lang="ts">
264
+ import { z } from 'zod';
265
+ import { Button, ConfirmationDialog, TextFormField } from '@nxgt/material-vue/components';
266
+
267
+ const formOptions = {
268
+ schema: z.object({ project: z.string().trim().pipe(z.literal('my-project')) }),
269
+ defaultValues: { project: '' },
270
+ };
271
+
272
+ function remove(values?: { project: 'my-project' }) {
273
+ console.log('deleting', values?.project);
274
+ }
275
+ </script>
276
+
277
+ <template>
278
+ <ConfirmationDialog
279
+ title="Delete project"
280
+ description='Type "my-project" to confirm.'
281
+ color="error"
282
+ confirm-text="Delete"
283
+ :form-options="formOptions"
284
+ @confirm="remove"
285
+ >
286
+ <template #trigger>
287
+ <Button color="error">Delete project</Button>
288
+ </template>
289
+ <template #form>
290
+ <TextFormField name="project" label="Project name" />
291
+ </template>
292
+ </ConfirmationDialog>
293
+ </template>
294
+ ```
295
+
296
+ `#form` also receives `{ values, errors, meta, setFieldValue }` for fields
297
+ that are not form wrappers. The form lives with the dialog's content, so it
298
+ starts again from `defaultValues` each time the dialog opens. The form's code
299
+ (and vee-validate) loads only when a dialog has `form-options`; confirm stays
300
+ disabled until it has loaded and first validated. If it cannot load, after one
301
+ retry, a translated message takes its place
302
+ (`material.confirm-dialog.form-failed`).
251
303
 
252
304
  ![CustomAlertDialog](../previews/feedback-status-customalertdialog.webp)
253
305
 
@@ -257,7 +309,7 @@ but built on `AlertDialog`, so it does not close on an outside click. Put
257
309
  `AlertDialogCancel` / `AlertDialogAction` in its `#footer`. Its trigger is
258
310
  `as-child`, so a `Button` goes straight in.
259
311
 
260
- **Storybook:** `Feedback & status/ConfirmDialog`, `Feedback & status/ConfirmationDialog`,
312
+ **Storybook:** `Feedback & status/ConfirmDialog`, `Feedback & status/ConfirmationDialog` (story `WithForm`),
261
313
  `Feedback & status/CustomAlertDialog` · **React:** `ConfirmDialog`, `ConfirmationDialog`, `CustomAlertDialog`
262
314
 
263
315
  ---
@@ -267,6 +267,7 @@ const quantity = ref<number | null>(2);
267
267
  | `hide-steppers` | `boolean` | `false` | hides the +/− buttons |
268
268
  | `#suffix` | slot | — | unit after the input |
269
269
  | `aria-label` | `string` | — | names the input when it has no `label` (a part of a larger field) |
270
+ | `aria-labelledby` | `string` | — | names the input by another element's id (a part of a larger field) |
270
271
 
271
272
  **Emits:** `update:modelValue` with `number | null`.
272
273
  **Form wrapper:** `NumberFormField` — `name` inside a vee-validate form, see
@@ -600,9 +601,14 @@ const title = ref('Spring catalogue');
600
601
  | --- | --- | --- | --- |
601
602
  | `v-model` | `string` | `''` | the committed text; updated on save only |
602
603
  | `@save` | `(value: string) => void` | — | on save (Enter or the check button), not on each key |
604
+ | `label` | `string` | — | shown above the text; names the input while editing |
605
+ | `name` | `string` | — | the input's `name` while editing, and the field's `data-pw` |
603
606
  | `placeholder` | `string` | "Click to edit" | shown while empty |
604
607
  | `disabled` | `boolean` | `false` | read-only text |
605
608
 
609
+ Enter, Escape, Save and Cancel return focus to the field, while a click
610
+ elsewhere saves and leaves focus where it went.
611
+
606
612
  **Emits:** `update:modelValue` and `save`, both `string`, trimmed, on save
607
613
  only.
608
614
  **Form wrapper:** `InlineEditFormField` (`name` plus the same props; the form
@@ -720,7 +726,7 @@ name: 'Satisfaction' })`.
720
726
 
721
727
  ![Slider](../previews/form-fields-slider.webp)
722
728
 
723
- Labelled single or range slider with a value readout.
729
+ Labelled single or range slider with a value readout, ticks and leading / trailing content.
724
730
 
725
731
  ```vue
726
732
  <script setup lang="ts">
@@ -731,20 +737,24 @@ const price = ref<number[]>([20, 70]);
731
737
  </script>
732
738
 
733
739
  <template>
734
- <SliderField v-model="price" label="Price" :min="0" :max="100" />
740
+ <SliderField v-model="price" label="Price" :min="0" :max="100" :ticks="5" leading="0" trailing="100" />
735
741
  </template>
736
742
  ```
737
743
 
738
744
  In Vue the labelled field is `SliderField`; `Slider` is the bare Reka UI
739
745
  slider (track, range and thumbs, `v-model` always `number[]`) it is built on.
740
- React's `ticks`, `leading` / `trailing` and editable readout inputs are not
741
- in `SliderField`: its readout is text.
746
+ React's editable readout inputs are not in `SliderField`: its readout is
747
+ text. `ticks`, `leading` and `trailing` are; for markup on either side of the
748
+ track, use the `#leading` / `#trailing` slots (`SliderFormField` passes them
749
+ through).
742
750
 
743
751
  | Prop | Type | Default | Effect |
744
752
  | --- | --- | --- | --- |
745
753
  | `v-model` | `number \| number[]` | `[min]` shown when unset | a number for one thumb, an array for one thumb per entry: `[20, 70]` |
746
754
  | `min`, `max`, `step` | `number` | `0`, `100`, `1` | range and increment |
747
755
  | `show-readout` | `boolean` | `true` | the value(s) next to the label |
756
+ | `ticks` | `boolean \| number` | — | marks under the track: `true` one per `step` (at most 21), a number that many, ends included |
757
+ | `leading`, `trailing` | `string \| number` | — | small muted text before / after the track; the `#leading` / `#trailing` slots take markup |
748
758
  | `required` | `boolean` | — | the asterisk, and `aria-required` on every thumb |
749
759
  | `aria-invalid` | `boolean \| 'true' \| 'false'` | follows `error` | on every thumb; wins over `error` |
750
760
 
@@ -764,7 +774,7 @@ wins over `error` as on React's `Slider`) goes on every thumb, not on the root
764
774
  stores the same — the initial value decides.
765
775
  **Form wrapper:** `SliderFormField` — `name` inside a vee-validate form, see
766
776
  [forms](../guides/forms.md#the-pair) and [what each field emits](../guides/forms.md#what-each-field-emits).
767
- **Storybook:** `Form fields/Slider` · **React:** `Slider`, `SliderField` (an alias of `Slider`), `SliderFormField`
777
+ **Storybook:** `Form fields/Slider` (story `TicksAndEnds`) · **React:** `Slider`, `SliderField` (an alias of `Slider`), `SliderFormField`
768
778
 
769
779
  ---
770
780