@nxgt/material-vue 0.23.4 → 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 (182) hide show
  1. package/README.md +30 -28
  2. package/dist/{Button-CVIR6uLK.js → Button-C1aaI0Gw.js} +95 -72
  3. package/dist/{HoverCardTrigger-iAL5ALrL.js → HoverCardTrigger-JtHn0IFQ.js} +2 -2
  4. package/dist/{camera-Gvg5N956.js → camera-BacVyXl0.js} +6 -6
  5. package/dist/{components-AScwCcds.js → components-DRBKkOqk.js} +15764 -13833
  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-BqWxpaKu.js → i18n-provider-D0oYv0XS.js} +1 -1
  11. package/dist/i18n.js +4 -4
  12. package/dist/{icon-button-DbEME76t.js → icon-button-B2FQtIHS.js} +34 -37
  13. package/dist/lib/components/ui/actions/button/has-text-content.d.ts +10 -0
  14. package/dist/lib/components/ui/actions/copy-button/copy-value.vue.d.ts +21 -0
  15. package/dist/lib/components/ui/actions/copy-button/index.d.ts +2 -0
  16. package/dist/lib/components/ui/collections/data-table/data-table-context.d.ts +6 -0
  17. package/dist/lib/components/ui/collections/data-table/data-table-pagination.vue.d.ts +2 -0
  18. package/dist/lib/components/ui/collections/data-table/data-table.vue.d.ts +17 -27
  19. package/dist/lib/components/ui/collections/data-table/index.d.ts +1 -1
  20. package/dist/lib/components/ui/collections/data-table/partials/data-table-body.vue.d.ts +10 -1
  21. package/dist/lib/components/ui/collections/data-table/partials/data-table-empty.vue.d.ts +13 -1
  22. package/dist/lib/components/ui/collections/data-table/partials/data-table-head.vue.d.ts +22 -0
  23. package/dist/lib/components/ui/collections/data-table/partials/data-table-skeleton-rows.vue.d.ts +10 -0
  24. package/dist/lib/components/ui/collections/data-table/use-data-table.d.ts +17 -0
  25. package/dist/lib/components/ui/collections/filter/partials/filter-field-slider-range.vue.d.ts +1 -4
  26. package/dist/lib/components/ui/collections/pagination/index.d.ts +5 -0
  27. package/dist/lib/components/ui/collections/pagination/pagination-bar.vue.d.ts +21 -0
  28. package/dist/lib/components/ui/collections/pagination/pagination-items.d.ts +2 -0
  29. package/dist/lib/components/ui/collections/pagination/pagination-link.vue.d.ts +18 -0
  30. package/dist/lib/components/ui/collections/tree/use-tree.d.ts +2 -0
  31. package/dist/lib/components/ui/dates/date-field/date-field.vue.d.ts +10 -1
  32. package/dist/lib/components/ui/dates/date-field/date-form-field.vue.d.ts +3 -1
  33. package/dist/lib/components/ui/dates/date-field/day-matcher.d.ts +26 -0
  34. package/dist/lib/components/ui/dates/date-field/index.d.ts +1 -0
  35. package/dist/lib/components/ui/dates/date-field/partials/date-field-content.vue.d.ts +2 -0
  36. package/dist/lib/components/ui/dates/date-range-field/date-range-field.utils.d.ts +11 -0
  37. package/dist/lib/components/ui/dates/date-range-field/date-range-field.vue.d.ts +26 -1
  38. package/dist/lib/components/ui/dates/date-range-field/date-range-form-field.vue.d.ts +3 -1
  39. package/dist/lib/components/ui/dates/date-range-field/index.d.ts +1 -0
  40. package/dist/lib/components/ui/dates/date-range-field/partials/date-range-calendar.vue.d.ts +5 -0
  41. package/dist/lib/components/ui/dates/date-range-field/partials/date-range-field-trigger.vue.d.ts +2 -0
  42. package/dist/lib/components/ui/dates/date-range-field/partials/date-range-presets.vue.d.ts +12 -0
  43. package/dist/lib/components/ui/dates/date-range-field/partials/date-range-times.vue.d.ts +13 -0
  44. package/dist/lib/components/ui/dates/date-range-field/use-date-range-presets.d.ts +23 -0
  45. package/dist/lib/components/ui/dates/time-picker/time-picker-utils.d.ts +7 -0
  46. package/dist/lib/components/ui/display/accordion-card/accordion-card.vue.d.ts +29 -1
  47. package/dist/lib/components/ui/display/avatar/Avatar.vue.d.ts +14 -3
  48. package/dist/lib/components/ui/display/avatar/index.d.ts +1 -0
  49. package/dist/lib/components/ui/display/cards/cards.gallery.d.ts +5 -9
  50. package/dist/lib/components/ui/display/markdown/index.d.ts +18 -0
  51. package/dist/lib/components/ui/display/qrcode/qrcode.vue.d.ts +5 -3
  52. package/dist/lib/components/ui/display/status-indicator/status-tone.d.ts +3 -0
  53. package/dist/lib/components/ui/feedback/confirmation-dialog/async-form-scope.d.ts +15 -0
  54. package/dist/lib/components/ui/feedback/confirmation-dialog/confirmation-dialog.vue.d.ts +37 -24
  55. package/dist/lib/components/ui/feedback/confirmation-dialog/index.d.ts +1 -1
  56. package/dist/lib/components/ui/feedback/confirmation-dialog/partials/confirmation-dialog-form-error.vue.d.ts +3 -0
  57. package/dist/lib/components/ui/feedback/skeleton/skeleton-text.vue.d.ts +1 -1
  58. package/dist/lib/components/ui/feedback/spinner/spinner.compound-variants.d.ts +4 -0
  59. package/dist/lib/components/ui/fields/attributes/form-schema.d.ts +24 -22
  60. package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
  61. package/dist/lib/components/ui/fields/inline-edit/index.d.ts +1 -0
  62. package/dist/lib/components/ui/fields/inline-edit/inline-edit-form-field.vue.d.ts +7 -0
  63. package/dist/lib/components/ui/fields/number-field/number-field.vue.d.ts +2 -0
  64. package/dist/lib/components/ui/fields/slider/partials/slider-field-row.vue.d.ts +32 -0
  65. package/dist/lib/components/ui/fields/slider/slider-field.vue.d.ts +22 -1
  66. package/dist/lib/components/ui/fields/slider/slider-form-field.vue.d.ts +13 -1
  67. package/dist/lib/components/ui/forms/form-scope-schema.d.ts +43 -0
  68. package/dist/lib/components/ui/forms/form-scope.vue.d.ts +33 -0
  69. package/dist/lib/components/ui/layout/doc/doc.vue.d.ts +5 -5
  70. package/dist/lib/components/ui/layout/doc/index.d.ts +8 -1
  71. package/dist/lib/components/ui/layout/doc/partials/default-doc-link.vue.d.ts +14 -0
  72. package/dist/lib/components/ui/layout/doc/partials/doc-copy-page.vue.d.ts +6 -0
  73. package/dist/lib/components/ui/layout/doc/partials/doc-link.vue.d.ts +3 -7
  74. package/dist/lib/components/ui/layout/doc/types.d.ts +6 -1
  75. package/dist/lib/components/ui/media/file-preview/file-preview-pdf.lazy.d.ts +8 -0
  76. package/dist/lib/components/ui/media/file-preview/file-preview.vue.d.ts +9 -0
  77. package/dist/lib/components/ui/media/file-preview/index.d.ts +3 -0
  78. package/dist/lib/components/ui/media/file-preview/partials/file-preview-body.vue.d.ts +21 -0
  79. package/dist/lib/components/ui/media/file-preview/partials/file-preview-card.vue.d.ts +15 -0
  80. package/dist/lib/components/ui/media/file-preview/partials/file-preview-download.vue.d.ts +11 -0
  81. package/dist/lib/components/ui/media/file-preview/partials/file-preview-media.vue.d.ts +17 -0
  82. package/dist/lib/components/ui/media/file-preview/partials/file-preview-pdf.vue.d.ts +21 -0
  83. package/dist/lib/components/ui/media/file-preview/partials/file-preview-status.vue.d.ts +22 -0
  84. package/dist/lib/components/ui/media/file-preview/partials/file-preview-text.vue.d.ts +16 -0
  85. package/dist/lib/components/ui/media/file-preview/types.d.ts +19 -0
  86. package/dist/lib/components/ui/media/file-preview/use-file-preview.d.ts +21 -0
  87. package/dist/lib/components/ui/media/file-preview/use-text-preview.d.ts +17 -0
  88. package/dist/lib/components/ui/media/files-form-field/files-form-field.vue.d.ts +29 -0
  89. package/dist/lib/components/ui/media/files-form-field/index.d.ts +2 -0
  90. package/dist/lib/components/ui/media/image-cropper/partials/image-cropper-canvas.vue.d.ts +1 -1
  91. package/dist/lib/components/ui/media/index.d.ts +2 -0
  92. package/dist/lib/components/ui/media/pdf-viewer/index.d.ts +6 -6
  93. package/dist/lib/components/ui/media/pdf-viewer/partials/pdf-page.vue.d.ts +1 -1
  94. package/dist/lib/components/ui/media/pdf-viewer/partials/pdf-reader-body.vue.d.ts +2 -2
  95. package/dist/lib/components/ui/media/pdf-viewer/pdf-reader.vue.d.ts +2 -2
  96. package/dist/lib/components/ui/media/upload/network-upload-field/use-image-preview.d.ts +9 -1
  97. package/dist/lib/components/ui/primitives/icon/icon-context.d.ts +1 -1
  98. package/dist/lib/components/ui/primitives/tabs/Tabs.vue.d.ts +3 -0
  99. package/dist/lib/components/ui/primitives/tabs/index.d.ts +1 -0
  100. package/dist/lib/components/ui/primitives/tabs/tabs-variant.d.ts +9 -0
  101. package/dist/lib/components/ui/selection/autocomplete/autocomplete-form-field.vue.d.ts +31 -5
  102. package/dist/lib/components/ui/selection/autocomplete/autocomplete-items.d.ts +23 -0
  103. package/dist/lib/components/ui/selection/autocomplete/autocomplete.vue.d.ts +48 -13
  104. package/dist/lib/components/ui/selection/autocomplete/filter-options.d.ts +2 -1
  105. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-box.vue.d.ts +13 -1
  106. package/dist/lib/components/ui/selection/autocomplete/partials/autocomplete-list.vue.d.ts +17 -3
  107. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-items.d.ts +15 -0
  108. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-open.d.ts +15 -0
  109. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete-texts.d.ts +14 -0
  110. package/dist/lib/components/ui/selection/autocomplete/use-autocomplete.d.ts +34 -0
  111. package/dist/lib/components/ui/selection/checkbox/Checkbox.vue.d.ts +21 -5
  112. package/dist/lib/components/ui/selection/checkbox/checkbox-form-field.vue.d.ts +17 -0
  113. package/dist/lib/components/ui/selection/checkbox/index.d.ts +2 -0
  114. package/dist/lib/components/ui/selection/index.d.ts +1 -0
  115. package/dist/lib/components/ui/selection/select-field/select-field.vue.d.ts +16 -1
  116. package/dist/lib/components/ui/selection/select-field/select-form-field.vue.d.ts +16 -2
  117. package/dist/lib/components/ui/selection/tree-select-field/index.d.ts +3 -0
  118. package/dist/lib/components/ui/selection/tree-select-field/partials/tree-select-panel.vue.d.ts +27 -0
  119. package/dist/lib/components/ui/selection/tree-select-field/partials/tree-select-trigger.vue.d.ts +28 -0
  120. package/dist/lib/components/ui/selection/tree-select-field/tree-select-field.vue.d.ts +8 -0
  121. package/dist/lib/components/ui/selection/tree-select-field/tree-select-form-field.vue.d.ts +8 -0
  122. package/dist/lib/components/ui/selection/tree-select-field/tree-select.utils.d.ts +29 -0
  123. package/dist/lib/components/ui/selection/tree-select-field/types.d.ts +28 -0
  124. package/dist/lib/components/ui/selection/tree-select-field/use-tree-select-field.d.ts +19 -0
  125. package/dist/lib/components/ui/widgets/charts/chart-container.vue.d.ts +1 -1
  126. package/dist/lib/components/ui/widgets/code-editor/code-editor-form-field.vue.d.ts +13 -0
  127. package/dist/lib/components/ui/widgets/code-editor/code-editor-options.d.ts +4 -1
  128. package/dist/lib/components/ui/widgets/code-editor/code-editor.vue.d.ts +7 -0
  129. package/dist/lib/components/ui/widgets/code-editor/index.d.ts +2 -0
  130. package/dist/lib/components/ui/widgets/code-editor/monaco-editor.vue.d.ts +3 -0
  131. package/dist/lib/i18n/resources/en/en.d.ts +40 -0
  132. package/dist/lib/i18n/resources/en/material.json.d.ts +43 -3
  133. package/dist/lib/i18n/resources/fr/fr.d.ts +40 -0
  134. package/dist/lib/i18n/resources/fr/material.json.d.ts +43 -3
  135. package/dist/lib/i18n/resources/resources.d.ts +80 -0
  136. package/dist/lib/i18n/translator.d.ts +1 -1
  137. package/dist/lib/nuxt/component-names.d.ts +1 -1
  138. package/dist/lib.js +2 -2
  139. package/dist/main.js +17 -17
  140. package/dist/map.js +4 -4
  141. package/dist/material-vue.css +1 -1
  142. package/dist/{pdf-outline-sidebar-oz9wdBhB.js → pdf-outline-sidebar-jJfhNjnN.js} +2 -2
  143. package/dist/{pdf-page-0rlq1Lo6.js → pdf-page-BW1hXzPu.js} +1 -1
  144. package/dist/{pdf-page-list-DfhjNHoI.js → pdf-page-list-D277RtWS.js} +2 -2
  145. package/dist/{pdf-reader-Dr8RVcs4.js → pdf-reader-BReUTsn_.js} +73 -71
  146. package/dist/{pdf-sidebar-VXPQbC_R.js → pdf-sidebar-Bk6PowY1.js} +3 -3
  147. package/dist/{pdf-thumbnail-sidebar-BpgXrVBj.js → pdf-thumbnail-sidebar-BcNDFsks.js} +3 -3
  148. package/dist/{pdf-toolbar-B6gRRcsv.js → pdf-toolbar-CHjaMhH9.js} +4 -4
  149. package/dist/pdf.js +2 -2
  150. package/dist/{sortable-provider-oEVjQiCl.js → sortable-provider-C83VBfGl.js} +2 -2
  151. package/dist/{stored-language-rjOMJkyX.js → stored-language-CC_vdcCN.js} +90 -6
  152. package/dist/{stx-player-DNADkQ31.js → stx-player-BMQ1D2Li.js} +20 -20
  153. package/dist/{translator-BCSJSO1-.js → translator-B-X_1taI.js} +1 -1
  154. package/dist/{use-heading-spy-d50_nxgR.js → use-heading-spy-C_ySuvTl.js} +1 -1
  155. package/dist/{use-material-translation-C5zPFhbk.js → use-material-translation-B6sIk0mF.js} +3 -3
  156. package/docs/components/README.md +2 -1
  157. package/docs/components/buttons-and-actions.md +19 -3
  158. package/docs/components/data-display.md +65 -11
  159. package/docs/components/data-table-filter-and-lists.md +94 -8
  160. package/docs/components/date-and-time.md +116 -9
  161. package/docs/components/feedback-and-status.md +57 -5
  162. package/docs/components/form-fields.md +21 -9
  163. package/docs/components/media-and-files.md +120 -6
  164. package/docs/components/navigation-and-layout.md +68 -7
  165. package/docs/components/primitives.md +25 -6
  166. package/docs/components/rich-widgets.md +21 -3
  167. package/docs/components/selection-and-choice.md +151 -22
  168. package/docs/guides/forms.md +16 -8
  169. package/docs/guides/i18n.md +50 -6
  170. package/docs/guides/ssr.md +2 -0
  171. package/docs/previews/buttons-actions-copybutton.webp +0 -0
  172. package/docs/previews/data-table-filter-lists-pagination.webp +0 -0
  173. package/docs/previews/date-time-daterangefield.webp +0 -0
  174. package/docs/previews/form-fields-slider.webp +0 -0
  175. package/docs/previews/media-files-filepreview.webp +0 -0
  176. package/docs/previews/navigation-layout-stepper.webp +0 -0
  177. package/docs/previews/selection-choice-checkbox.webp +0 -0
  178. package/docs/previews/selection-choice-treeselectfield.webp +0 -0
  179. package/docs/roadmap.md +47 -1
  180. package/docs/troubleshooting.md +213 -0
  181. package/lib/nuxt/component-names.ts +16 -0
  182. package/package.json +2 -2
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./utils-CykWQ0iJ.js";
2
- import { Gt as t, Jt as n, Ut as r, Wt as i, Xt as a, Yt as o, lp as s, qt as c } from "./components-AScwCcds.js";
3
- import { t as l } from "./use-material-translation-C5zPFhbk.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,21 +19,21 @@ var S = ["data-layout", "aria-label"], C = {
19
19
  Array
20
20
  ] }
21
21
  },
22
- setup(t) {
23
- let { containerRef: g, state: w } = a();
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 (a, l) => (v(), f("section", {
28
+ return (o, l) => (v(), f("section", {
29
29
  ref: T,
30
30
  "data-slot": "stx-player",
31
- "data-layout": t.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", a.$props.class))
34
- }, [t.layout === "mini" ? (v(), u(c, { 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", t.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
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)]),
@@ -45,24 +45,24 @@ var S = ["data-layout", "aria-label"], C = {
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(r, 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(n, { 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(n, { tone: "surface" })]),
62
+ trailing: x(() => [h(r, { tone: "surface" })]),
63
63
  _: 1
64
64
  })) : d("", !0)
65
- ], 2), t.showQueue ? (v(), u(i, {
65
+ ], 2), i.showQueue ? (v(), u(n, {
66
66
  key: 0,
67
67
  class: "min-h-72"
68
68
  })) : d("", !0)], 2))], 10, S));
@@ -97,12 +97,12 @@ var S = ["data-layout", "aria-label"], C = {
97
97
  ] }
98
98
  },
99
99
  emits: ["trackChange"],
100
- setup(e, { emit: n }) {
101
- let r = e, i = n;
100
+ setup(e, { emit: t }) {
101
+ let n = e, r = t;
102
102
  function a(e, t) {
103
- r.onTrackChange?.(e, t), i("trackChange", e, t);
103
+ n.onTrackChange?.(e, t), r("trackChange", e, t);
104
104
  }
105
- return (n, i) => (v(), u(t, {
105
+ return (t, r) => (v(), u(i, {
106
106
  tracks: e.tracks,
107
107
  "current-index": e.currentIndex,
108
108
  "default-index": e.defaultIndex,
@@ -113,7 +113,7 @@ var S = ["data-layout", "aria-label"], C = {
113
113
  default: x(() => [h(w, {
114
114
  layout: e.layout,
115
115
  "show-queue": e.showQueue,
116
- class: _(r.class)
116
+ class: _(n.class)
117
117
  }, null, 8, [
118
118
  "layout",
119
119
  "show-queue",
@@ -1,4 +1,4 @@
1
- import { a as e, i as t, r as n, t as r } from "./stored-language-rjOMJkyX.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-BqWxpaKu.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-rjOMJkyX.js";
2
- import { a as e } from "./i18n-provider-BqWxpaKu.js";
3
- import { i as t, r as n } from "./translator-BCSJSO1-.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
 
@@ -319,9 +319,25 @@ import { CopyButton } from '@nxgt/material-vue/components';
319
319
  | `#icon` slot | — | a copy icon | replaces the idle icon |
320
320
  | `variant`, `color` | as `IconButton` | `'ghost'`, `'default'` | the button style |
321
321
 
322
- The check mark stays for 1.5 s. Vue has no `CopyValue`: put the text next to a
323
- `CopyButton` as above. `copyToClipboard(text)` is the same copy without a
324
- button.
322
+ The check mark stays for 1.5 s. `copyToClipboard(text)` is the same copy
323
+ without a button.
324
+
325
+ `CopyValue` shows the value in a monospace chip with the button beside it:
326
+
327
+ ```vue
328
+ <script setup lang="ts">
329
+ import { CopyValue } from '@nxgt/material-vue/components';
330
+ </script>
331
+
332
+ <template>
333
+ <CopyValue value="https://app.example.com/orders/SO-1842" display="SO-1842" />
334
+ </template>
335
+ ```
336
+
337
+ | `CopyValue` prop | Type | Default | Effect |
338
+ | --- | --- | --- | --- |
339
+ | `value` | `string` | — (required) | what is copied, and shown when there is no `display` |
340
+ | `display` / default slot | `string` | `value` | what is shown instead (React's `display` node) |
325
341
 
326
342
  **Storybook:** `Buttons & actions/CopyButton` · **React:** `CopyButton`, `CopyValue`
327
343
 
@@ -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
 
@@ -656,7 +695,20 @@ left alone. Mermaid diagrams
656
695
  render in the browser only, after mount; on the server the fence is left
657
696
  empty.
658
697
 
659
- **Storybook:** `Data display/Markdown` · **React:** `Markdown`
698
+ `MermaidDiagram` renders one chart outside markdown, the same way: client-only,
699
+ with `mermaid` imported on mount.
700
+
701
+ ```vue
702
+ <script setup lang="ts">
703
+ import { MermaidDiagram } from '@nxgt/material-vue/components';
704
+ </script>
705
+
706
+ <template>
707
+ <MermaidDiagram chart="graph LR&#10; Draft --> Review --> Published" />
708
+ </template>
709
+ ```
710
+
711
+ **Storybook:** `Data display/Markdown` · **React:** `Markdown`, `MermaidDiagram`
660
712
 
661
713
  ---
662
714
 
@@ -803,7 +855,9 @@ import { QRCode } from '@nxgt/material-vue/components';
803
855
 
804
856
  <template>
805
857
  <QRCode value="https://example.com/tickets/DS-184" :size="160" level="H">
806
- <img src="/logo.svg" alt="" width="32" />
858
+ <template #logo>
859
+ <img src="/logo.svg" alt="" width="32" />
860
+ </template>
807
861
  </QRCode>
808
862
  </template>
809
863
  ```
@@ -816,7 +870,7 @@ import { QRCode } from '@nxgt/material-vue/components';
816
870
  | `fgColor`, `bgColor` | `string` | `'#000000'`, `'#FFFFFF'` | colours |
817
871
  | `title` | `string` | — | accessible label of the SVG |
818
872
  | `aria-label` | `string` | — | the SVG's name too, and wins over `title` |
819
- | 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 |
820
874
 
821
875
  The code is an `<svg role="img">` named by `aria-label`, else `title`, else
822
876
  "QR code" (`material.qrcode.label`, translated):
@@ -23,7 +23,7 @@ each other, so either can be used alone.
23
23
 
24
24
  ![DataTable](../previews/data-table-filter-lists-datatable.webp)
25
25
 
26
- TanStack Table v9 with sorting, filtering, selection, expandable rows, pagination or load-more, and column visibility.
26
+ TanStack Table v9 with sorting, filtering, selection, expandable rows, pagination or load-more, column visibility, skeleton rows while loading and your own empty state.
27
27
 
28
28
  ```vue
29
29
  <script setup lang="ts">
@@ -59,7 +59,8 @@ const columns = columnHelper.columns([
59
59
 
60
60
  Parts: `DataTable`; `DataTableColumnHeader` (a sortable header with a hide
61
61
  action); `DataTablePagination` and `DataTableViewOptions` (used by the table,
62
- exported for a table of your own); `useColumnHelpers<T>()` returns a
62
+ exported for a table of your own; `DataTablePagination` takes `disabled`);
63
+ `useColumnHelpers<T>()` returns a
63
64
  `columnHelper` bound to the table's features and `predefinedColumns()` —
64
65
  `select` (checkboxes) and `expand` (a toggle for the expanded row, named
65
66
  "Toggle row details" (`material.table.labels.toggle-row`) with `aria-expanded`);
@@ -78,6 +79,58 @@ table enables, for typing your own column defs.
78
79
  | `onLoadMore` / `isLoadingMore` | `() => void` / `boolean` | — | adds a "load more" button under the rows |
79
80
  | `options` | `{ showPage?: boolean; showSelected?: boolean }` | both shown | parts of the footer |
80
81
  | `showHeader` | `boolean` | hidden on mobile | header row |
82
+ | `loading` | `boolean` | `false` | skeleton rows, or the current rows dimmed; `aria-busy`; controls disabled |
83
+ | `loadingRows` | `number` | the page size | how many skeleton rows |
84
+ | `#empty="{ filtered }"` | slot | icon and "No data available" | content of the empty row, spanning every visible column |
85
+
86
+ ### Loading and empty states
87
+
88
+ ```vue
89
+ <script setup lang="ts">
90
+ import { DataTable, useColumnHelpers } from '@nxgt/material-vue/components';
91
+
92
+ type Person = { id: string; name: string; email: string };
93
+
94
+ defineProps<{ people: Person[]; isFetching: boolean }>();
95
+
96
+ const { columnHelper } = useColumnHelpers<Person>();
97
+ const columns = columnHelper.columns([
98
+ columnHelper.accessor('name', { header: 'Name' }),
99
+ columnHelper.accessor('email', { header: 'Email' }),
100
+ ]);
101
+ </script>
102
+
103
+ <template>
104
+ <DataTable
105
+ :data="people"
106
+ :columns="columns"
107
+ :loading="isFetching"
108
+ :loading-rows="5"
109
+ >
110
+ <template #empty="{ filtered }">
111
+ {{ filtered ? 'No person matches these filters.' : 'No people yet.' }}
112
+ </template>
113
+ </DataTable>
114
+ </template>
115
+ ```
116
+
117
+ While `loading` is `true`, the table is `aria-busy` and a visually
118
+ hidden status says "Loading data…" (`material.table.loading`). With no row to
119
+ show, the body is `loadingRows` skeleton rows (default: the page size), one
120
+ cell per visible column. With rows already shown — a refresh — the rows stay
121
+ where they are, dimmed and `inert`, rather than being swapped for
122
+ skeletons: the table keeps its height, so nothing below it jumps when the data
123
+ arrives. Sorting (`DataTableColumnHeader`), the `select` column's "Select
124
+ all", the toolbar's native controls (it
125
+ is a disabled `<fieldset>`, so every input or button you render there, a
126
+ filter or an export, is disabled
127
+ too) and pagination are disabled meanwhile.
128
+
129
+ When no row is left after filtering, the empty state is one `TableEmpty`
130
+ cell spanning every visible column: today's icon and "No data available" by
131
+ default, or the `#empty` slot, told whether rows exist but none matches the
132
+ filters (`filtered`); content that renders nothing gets the default.
133
+ `DataTableEmptyState` types the slot props.
81
134
 
82
135
  For the TanStack API itself (column helpers, features, row models), see
83
136
  [TanStack Table v9](https://tanstack.com/table/latest).
@@ -132,7 +185,10 @@ A field's `type` is one of `text`, `number`, `select`, `multi-select`, `date`,
132
185
  `chips-multiple`, `chips-single`, `slider`, `slider-range`, `switch` or
133
186
  `custom` (`render` returns a VNode); `schema.groups` puts fields in
134
187
  collapsible sections. The filter state lives in a Pinia store, so the app
135
- needs `app.use(createPinia())`.
188
+ needs `app.use(createPinia())`. A `number-range` or `slider-range` is a
189
+ `role="group"` named by its `label`, with its inputs named by their part
190
+ ("Min" / "Max", or the slider's "<label> Minimum" / "<label> Maximum"):
191
+ `getByRole('group', { name: 'Price' })`.
136
192
 
137
193
  Parts: `Filter` (one component, `variant` picks the layout);
138
194
  `FilterProvider` + `FilterContent` + `FilterActions` to lay the panel out
@@ -203,7 +259,34 @@ above shows `FilterPopover` open; every variant has its own story.
203
259
 
204
260
  ![Pagination](../previews/data-table-filter-lists-pagination.webp)
205
261
 
206
- Pagination primitives on Reka UI's `Pagination`, with first / last buttons and ellipses.
262
+ Pagination primitives on Reka UI's `Pagination`, with first / last buttons and ellipses. `PaginationBar` adds a sibling window with ellipses.
263
+
264
+ ```vue
265
+ <script setup lang="ts">
266
+ import { ref } from 'vue';
267
+ import { PaginationBar } from '@nxgt/material-vue/components';
268
+
269
+ const page = ref(1);
270
+ </script>
271
+
272
+ <template>
273
+ <PaginationBar v-model:page="page" :page-count="12" />
274
+ </template>
275
+ ```
276
+
277
+ | `PaginationBar` prop / event | Type | Default | Effect |
278
+ | --- | --- | --- | --- |
279
+ | `page` / `v-model:page` | `number` | — (required) | current page, from 1; controlled |
280
+ | `page-count` | `number` | — (required) | number of pages |
281
+ | `sibling-count` | `number` | `1` | pages on each side of the current one before an ellipsis |
282
+ | `disabled` | `boolean` | `false` | disables every button |
283
+ | `@update:page`, `@page-change` | `(page: number) => void` | — | a page was chosen (React's `onPageChange`) |
284
+
285
+ `paginationItems(page, pageCount, siblingCount?)` is the window it renders, as
286
+ `(number | 'ellipsis')[]` (`PaginationItemToken`). `PaginationLink` is a page
287
+ as an anchor (`href`, `is-active`), for server-rendered lists.
288
+
289
+ The parts, composed by hand:
207
290
 
208
291
  ```vue
209
292
  <script setup lang="ts">
@@ -243,9 +326,8 @@ const page = ref(1);
243
326
  Parts: `Pagination` (the root), `PaginationContent` (its slot gives the
244
327
  `items` window), `PaginationItem` (`value`, `is-active`),
245
328
  `PaginationPrevious`, `PaginationNext`, `PaginationFirst`, `PaginationLast`,
246
- `PaginationEllipsis`. There is no `PaginationBar` / `paginationItems` in Vue:
247
- the root computes the window itself. For a table, `DataTablePagination` is
248
- already wired.
329
+ `PaginationEllipsis`. Composed by hand, the root computes the window itself.
330
+ For a table, `DataTablePagination` is already wired.
249
331
 
250
332
  | Prop | Type | Default | Effect |
251
333
  | --- | --- | --- | --- |
@@ -258,7 +340,7 @@ already wired.
258
340
  | `aria-label` | `string` | `'pagination'` (translated) | the `nav`'s name; the buttons are named "First", "Previous", "Next", "Last" and "Page {n}" (`material.pagination.*`). An `aria-label` on a part wins |
259
341
 
260
342
  **Emits:** `update:page` with `number`.
261
- **Storybook:** `Data table, filter & lists/Pagination` · **React:** `Pagination` (parts), `PaginationBar`
343
+ **Storybook:** `Data table, filter & lists/Pagination` · **React:** `Pagination` (parts), `PaginationFirst`, `PaginationLast`, `PaginationLink`, `PaginationBar`, `paginationItems`
262
344
 
263
345
  ---
264
346
 
@@ -310,6 +392,10 @@ own) and `TreeItem` (one row); `findTreeNode` and `flattenVisibleTree`.
310
392
  Arrow keys move and expand / collapse, Home / End jump, Enter and Space
311
393
  select.
312
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
+
313
399
  **Emits:** `update:selectedIds` and `update:expandedIds` with `string[]`, `select`.
314
400
  **Storybook:** `Data table, filter & lists/Tree` · **React:** `Tree`, `TreeItem`, `useTree`
315
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`