@nxgt/material-vue 0.25.2 → 0.26.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 (51) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +1 -1
  3. package/dist/{Button-C1aaI0Gw.js → Button-16J7vTpE.js} +1 -1
  4. package/dist/{HoverCardTrigger-JtHn0IFQ.js → HoverCardTrigger-C41002JS.js} +2 -2
  5. package/dist/{camera-BacVyXl0.js → camera-DvKuqT4L.js} +4 -4
  6. package/dist/{components-DRBKkOqk.js → components-CCWod7cJ.js} +10461 -10194
  7. package/dist/components.js +8 -8
  8. package/dist/composables.js +2 -2
  9. package/dist/dnd.js +1 -1
  10. package/dist/{form-scope-VxlJuvFe.js → form-scope-x-7TYn2s.js} +1 -1
  11. package/dist/{i18n-provider-D0oYv0XS.js → i18n-provider-CaaZCmaR.js} +1 -1
  12. package/dist/i18n.js +4 -4
  13. package/dist/{icon-button-B2FQtIHS.js → icon-button-CDdNsZuZ.js} +1 -1
  14. package/dist/lib/components/ui/collections/data-table/data-table-server-footer.vue.d.ts +35 -0
  15. package/dist/lib/components/ui/collections/data-table/data-table.vue.d.ts +18 -30
  16. package/dist/lib/components/ui/collections/data-table/index.d.ts +3 -1
  17. package/dist/lib/components/ui/collections/data-table/partials/data-table-body.vue.d.ts +4 -0
  18. package/dist/lib/components/ui/collections/data-table/partials/data-table-page-control.vue.d.ts +15 -0
  19. package/dist/lib/components/ui/collections/data-table/partials/data-table-row.vue.d.ts +31 -0
  20. package/dist/lib/components/ui/collections/data-table/types.d.ts +66 -0
  21. package/dist/lib/components/ui/collections/data-table/use-data-table.d.ts +13 -3
  22. package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
  23. package/dist/lib/i18n/resources/en/en.d.ts +5 -0
  24. package/dist/lib/i18n/resources/en/material.json.d.ts +6 -1
  25. package/dist/lib/i18n/resources/fr/fr.d.ts +5 -0
  26. package/dist/lib/i18n/resources/fr/material.json.d.ts +6 -1
  27. package/dist/lib/i18n/resources/resources.d.ts +10 -0
  28. package/dist/lib/i18n/translator.d.ts +1 -1
  29. package/dist/lib/nuxt/component-names.d.ts +1 -1
  30. package/dist/lib.js +2 -2
  31. package/dist/main.js +17 -17
  32. package/dist/map.js +4 -4
  33. package/dist/material-vue.css +1 -1
  34. package/dist/{pdf-outline-sidebar-jJfhNjnN.js → pdf-outline-sidebar-D3vq2qfS.js} +2 -2
  35. package/dist/{pdf-page-BW1hXzPu.js → pdf-page-BrJbyjQj.js} +1 -1
  36. package/dist/{pdf-page-list-D277RtWS.js → pdf-page-list-BCgYEszV.js} +2 -2
  37. package/dist/{pdf-reader-BReUTsn_.js → pdf-reader-BAzevSJU.js} +8 -8
  38. package/dist/{pdf-sidebar-Bk6PowY1.js → pdf-sidebar-DSh2Mend.js} +3 -3
  39. package/dist/{pdf-thumbnail-sidebar-BcNDFsks.js → pdf-thumbnail-sidebar-D_EMcbKF.js} +3 -3
  40. package/dist/{pdf-toolbar-CHjaMhH9.js → pdf-toolbar-BcL7HlfS.js} +4 -4
  41. package/dist/pdf.js +2 -2
  42. package/dist/{sortable-provider-C83VBfGl.js → sortable-provider-DMpmM7Ke.js} +2 -2
  43. package/dist/{stored-language-CC_vdcCN.js → stored-language-Bxhsx4iB.js} +12 -2
  44. package/dist/{stx-player-BMQ1D2Li.js → stx-player-MldK_ZAZ.js} +17 -17
  45. package/dist/{translator-B-X_1taI.js → translator-CLI_7Crg.js} +1 -1
  46. package/dist/{use-heading-spy-C_ySuvTl.js → use-heading-spy-VPuFj2Gt.js} +1 -1
  47. package/dist/{use-material-translation-B6sIk0mF.js → use-material-translation-CONqD9wJ.js} +3 -3
  48. package/docs/components/data-table-filter-and-lists.md +173 -5
  49. package/docs/roadmap.md +6 -0
  50. package/lib/nuxt/component-names.ts +1 -0
  51. package/package.json +2 -2
@@ -1,7 +1,7 @@
1
1
  import { n as e } from "./rolldown-runtime-CWhphoD1.js";
2
2
  import { t } from "./utils-CykWQ0iJ.js";
3
- import { Ef as n } from "./components-DRBKkOqk.js";
4
- import { t as r } from "./use-material-translation-B6sIk0mF.js";
3
+ import { Ef as n } from "./components-CCWod7cJ.js";
4
+ import { t as r } from "./use-material-translation-CONqD9wJ.js";
5
5
  import { Fragment as i, computed as a, createBlock as o, createCommentVNode as s, createElementBlock as c, createElementVNode as l, defineComponent as u, normalizeClass as d, normalizeStyle as f, openBlock as p, ref as m, renderList as h, toDisplayString as g, unref as _, withCtx as v } from "vue";
6
6
  //#region lib/components/ui/media/pdf-viewer/partials/pdf-outline-row.vue?vue&type=script&setup=true&lang.ts
7
7
  var y = {
@@ -1,5 +1,5 @@
1
1
  import { t as e } from "./utils-CykWQ0iJ.js";
2
- import { Op as t } from "./components-DRBKkOqk.js";
2
+ import { Op as t } from "./components-CCWod7cJ.js";
3
3
  import { computed as n, createBlock as r, createCommentVNode as i, createElementBlock as a, createElementVNode as o, defineComponent as s, nextTick as c, normalizeClass as l, normalizeStyle as u, onBeforeUnmount as d, openBlock as f, ref as p, shallowRef as m, unref as h, watch as g } from "vue";
4
4
  import { DEFAULT_PAGE_SIZE as _, escapeHtml as v, findTextMatches as y, highlightItemHtml as b, placeholderSize as x, rotatedSize as S } from "@nxgt/material-core/pdf-viewer";
5
5
  //#region lib/components/ui/media/pdf-viewer/pdf-note-icons.ts
@@ -1,6 +1,6 @@
1
1
  import { n as e } from "./rolldown-runtime-CWhphoD1.js";
2
- import { Ef as t } from "./components-DRBKkOqk.js";
3
- import { t as n } from "./pdf-page-BW1hXzPu.js";
2
+ import { Ef as t } from "./components-CCWod7cJ.js";
3
+ import { t as n } from "./pdf-page-BrJbyjQj.js";
4
4
  import { Fragment as r, createBlock as i, createElementBlock as a, createElementVNode as o, defineComponent as s, openBlock as c, renderList as l, withCtx as u } from "vue";
5
5
  //#region lib/components/ui/media/pdf-viewer/partials/pdf-page-list.vue?vue&type=script&setup=true&lang.ts
6
6
  var d = { class: "flex flex-col items-center gap-4 px-4 py-4" }, f = /*@__PURE__*/ s({
@@ -1,12 +1,12 @@
1
1
  import { t as e } from "./utils-CykWQ0iJ.js";
2
- import { On as t } from "./components-DRBKkOqk.js";
3
- import { t as n } from "./use-material-translation-B6sIk0mF.js";
4
- import { t as r } from "./Button-C1aaI0Gw.js";
5
- import { o as i } from "./HoverCardTrigger-JtHn0IFQ.js";
6
- import { n as a } from "./pdf-page-BW1hXzPu.js";
7
- import { t as o } from "./pdf-page-list-D277RtWS.js";
8
- import { t as s } from "./pdf-sidebar-Bk6PowY1.js";
9
- import { t as c } from "./pdf-toolbar-CHjaMhH9.js";
2
+ import { On as t } from "./components-CCWod7cJ.js";
3
+ import { t as n } from "./use-material-translation-CONqD9wJ.js";
4
+ import { t as r } from "./Button-16J7vTpE.js";
5
+ import { o as i } from "./HoverCardTrigger-C41002JS.js";
6
+ import { n as a } from "./pdf-page-BrJbyjQj.js";
7
+ import { t as o } from "./pdf-page-list-BCgYEszV.js";
8
+ import { t as s } from "./pdf-sidebar-DSh2Mend.js";
9
+ import { t as c } from "./pdf-toolbar-BcL7HlfS.js";
10
10
  import { computed as l, createBlock as u, createCommentVNode as d, createElementBlock as f, createElementVNode as p, createTextVNode as m, createVNode as h, defineComponent as g, nextTick as _, normalizeClass as v, onBeforeUnmount as y, onMounted as b, openBlock as x, reactive as S, ref as C, shallowRef as w, toDisplayString as T, toRef as E, unref as D, useId as O, watch as k, withCtx as A } from "vue";
11
11
  import { AlertCircle as j, FileX2 as M, KeyRound as N, Loader2 as P } from "@lucide/vue";
12
12
  import { pdfSourceKey as F, toPdfjsParams as I } from "@nxgt/material-core/pdf-source";
@@ -1,8 +1,8 @@
1
1
  import { n as e } from "./rolldown-runtime-CWhphoD1.js";
2
2
  import { t } from "./utils-CykWQ0iJ.js";
3
- import { t as n } from "./use-material-translation-B6sIk0mF.js";
4
- import { t as r } from "./pdf-outline-sidebar-jJfhNjnN.js";
5
- import { t as i } from "./pdf-thumbnail-sidebar-BcNDFsks.js";
3
+ import { t as n } from "./use-material-translation-CONqD9wJ.js";
4
+ import { t as r } from "./pdf-outline-sidebar-D3vq2qfS.js";
5
+ import { t as i } from "./pdf-thumbnail-sidebar-D_EMcbKF.js";
6
6
  import { createElementBlock as a, createElementVNode as o, createVNode as s, defineComponent as c, normalizeClass as l, openBlock as u, toDisplayString as d, unref as f, useId as p, useTemplateRef as m } from "vue";
7
7
  //#region lib/components/ui/media/pdf-viewer/partials/pdf-sidebar.vue?vue&type=script&setup=true&lang.ts
8
8
  var h = ["aria-label"], g = ["aria-label"], _ = [
@@ -1,8 +1,8 @@
1
1
  import { n as e } from "./rolldown-runtime-CWhphoD1.js";
2
2
  import { t } from "./utils-CykWQ0iJ.js";
3
- import { Ef as n } from "./components-DRBKkOqk.js";
4
- import { t as r } from "./use-material-translation-B6sIk0mF.js";
5
- import { t as i } from "./pdf-page-BW1hXzPu.js";
3
+ import { Ef as n } from "./components-CCWod7cJ.js";
4
+ import { t as r } from "./use-material-translation-CONqD9wJ.js";
5
+ import { t as i } from "./pdf-page-BrJbyjQj.js";
6
6
  import { Fragment as a, createCommentVNode as o, createElementBlock as s, createElementVNode as c, createVNode as l, defineComponent as u, normalizeClass as d, openBlock as f, renderList as p, toDisplayString as m, unref as h, withCtx as g } from "vue";
7
7
  //#region lib/components/ui/media/pdf-viewer/partials/pdf-thumbnail-sidebar.vue?vue&type=script&setup=true&lang.ts
8
8
  var _ = ["aria-label"], v = {
@@ -1,10 +1,10 @@
1
1
  import { n as e } from "./rolldown-runtime-CWhphoD1.js";
2
2
  import { t } from "./utils-CykWQ0iJ.js";
3
- import { t as n } from "./use-material-translation-B6sIk0mF.js";
4
- import { s as r } from "./Button-C1aaI0Gw.js";
5
- import { t as i } from "./icon-button-B2FQtIHS.js";
3
+ import { t as n } from "./use-material-translation-CONqD9wJ.js";
4
+ import { s as r } from "./Button-16J7vTpE.js";
5
+ import { t as i } from "./icon-button-CDdNsZuZ.js";
6
6
  import { a, i as o, n as s, o as c, r as l, t as u } from "./DropdownMenuTrigger-BHAhXGUO.js";
7
- import { o as d } from "./HoverCardTrigger-JtHn0IFQ.js";
7
+ import { o as d } from "./HoverCardTrigger-C41002JS.js";
8
8
  import { Fragment as f, computed as p, createBlock as m, createCommentVNode as h, createElementBlock as g, createElementVNode as _, createTextVNode as v, createVNode as y, defineComponent as b, nextTick as x, normalizeClass as S, openBlock as C, ref as w, renderList as T, toDisplayString as E, unref as D, useTemplateRef as O, watch as k, withCtx as A } from "vue";
9
9
  import { ChevronDown as j, ChevronLeft as M, ChevronRight as N, ChevronUp as P, Download as F, Maximize2 as I, Minimize2 as L, Moon as R, Printer as z, RotateCw as B, Search as V, Sidebar as H, ZoomIn as U, ZoomOut as W } from "@lucide/vue";
10
10
  //#region lib/components/ui/media/pdf-viewer/partials/pdf-toolbar-search.vue?vue&type=script&setup=true&lang.ts
package/dist/pdf.js CHANGED
@@ -1,5 +1,5 @@
1
- import { n as e, t } from "./use-material-translation-B6sIk0mF.js";
2
- import { t as n } from "./Button-C1aaI0Gw.js";
1
+ import { n as e, t } from "./use-material-translation-CONqD9wJ.js";
2
+ import { t as n } from "./Button-16J7vTpE.js";
3
3
  import { computed as r, createBlock as i, createTextVNode as a, defineComponent as o, getCurrentInstance as s, nextTick as c, onMounted as l, onScopeDispose as u, openBlock as d, ref as f, renderSlot as p, shallowRef as m, toDisplayString as ee, toValue as h, unref as g, watch as te, withCtx as ne } from "vue";
4
4
  import { contentDisposition as re, pdfBytesResponse as ie, toWebStream as ae } from "@nxgt/material-core/pdf/http";
5
5
  import { badge as oe, dataTable as se, defaultPdfTheme as ce, definePdfDocument as _, divider as v, header as y, heading as b, image as x, keyValue as S, keyValueOf as C, link as w, metric as T, page as E, pageBreak as D, pageNumber as O, pdfDocument as k, resolvePdfColor as A, resolvePdfTheme as j, row as M, section as N, spacer as P, stack as F, table as I, text as L, totals as R } from "@nxgt/material-core/pdf/model";
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./utils-CykWQ0iJ.js";
2
- import { t } from "./use-material-translation-B6sIk0mF.js";
3
- import { t as n } from "./icon-button-B2FQtIHS.js";
2
+ import { t } from "./use-material-translation-CONqD9wJ.js";
3
+ import { t as n } from "./icon-button-CDdNsZuZ.js";
4
4
  import { Teleport as r, computed as i, createBlock as a, createCommentVNode as o, createElementBlock as s, createVNode as c, defineComponent as l, inject as u, mergeProps as d, normalizeStyle as f, onUnmounted as p, openBlock as m, provide as h, reactive as g, ref as _, renderSlot as v, toValue as y, unref as b, useAttrs as x, watch as S, watchEffect as C, withCtx as w, withModifiers as T } from "vue";
5
5
  import { GripVertical as E } from "@lucide/vue";
6
6
  import { Primitive as D } from "reka-ui";
@@ -341,7 +341,12 @@ var e = {
341
341
  "go-to-last-page": "Go to last page",
342
342
  "rows-per-page": "Rows per page",
343
343
  "rows-selected": "{count} of {total} {count, plural, =0 {row selected} one {row selected} other {rows selected} }",
344
- "page-of": "Page {page} of {total}"
344
+ "page-of": "Page {page} of {total}",
345
+ label: "Pagination",
346
+ range: "{from}–{to} of {total}",
347
+ "range-open": "{from}–{to}",
348
+ total: "{total, plural, one {# row} other {# rows}}",
349
+ selected: "{count, plural, one {# row selected} other {# rows selected}}"
345
350
  },
346
351
  "delete-confirm": {
347
352
  title: "Delete {count} item(s)?",
@@ -1512,7 +1517,12 @@ var e = {
1512
1517
  "go-to-last-page": "Aller à la dernière page",
1513
1518
  "rows-per-page": "Lignes par page",
1514
1519
  "rows-selected": "{count} sur {total} {count, plural, =0 {ligne sélectionnée} one {ligne sélectionnée} other {lignes sélectionnées} }",
1515
- "page-of": "Page {page} sur {total}"
1520
+ "page-of": "Page {page} sur {total}",
1521
+ label: "Pagination",
1522
+ range: "{from}–{to} sur {total}",
1523
+ "range-open": "{from}–{to}",
1524
+ total: "{total, plural, one {# ligne} other {# lignes}}",
1525
+ selected: "{count, plural, one {# ligne sélectionnée} other {# lignes sélectionnées}}"
1516
1526
  },
1517
1527
  "delete-confirm": {
1518
1528
  title: "Supprimer {count} élément(s) ?",
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./utils-CykWQ0iJ.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";
2
+ import { $t as t, Jt as n, Mp as r, Qt as i, Yt as a, Zt as o, en as s, qt as c } from "./components-CCWod7cJ.js";
3
+ import { t as l } from "./use-material-translation-CONqD9wJ.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,26 +19,26 @@ var S = ["data-layout", "aria-label"], C = {
19
19
  Array
20
20
  ] }
21
21
  },
22
- setup(i) {
23
- let { containerRef: g, state: w } = o();
22
+ setup(a) {
23
+ let { containerRef: g, state: w } = s();
24
24
  function T(e) {
25
25
  g.value = e instanceof HTMLElement ? e : null;
26
26
  }
27
27
  let { t: E } = l();
28
- return (o, l) => (v(), f("section", {
28
+ return (s, l) => (v(), f("section", {
29
29
  ref: T,
30
30
  "data-slot": "stx-player",
31
- "data-layout": i.layout,
31
+ "data-layout": a.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", o.$props.class))
34
- }, [i.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", s.$props.class))
34
+ }, [a.layout === "mini" ? (v(), u(o, { key: 0 })) : (v(), f("div", {
35
35
  key: 1,
36
- class: _(b(e)("grid gap-3", i.showQueue && "@min-[768px]:grid-cols-[minmax(0,1fr)_18rem] @min-[768px]:items-stretch"))
36
+ class: _(b(e)("grid gap-3", a.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(s, { variant: "title-medium" }, {
38
+ b(w).current?.kind === "audio" ? (v(), f("div", C, [h(r, { variant: "title-medium" }, {
39
39
  default: x(() => [m(y(b(w).current.title), 1)]),
40
40
  _: 1
41
- }), b(w).current.artist ? (v(), u(s, {
41
+ }), b(w).current.artist ? (v(), u(r, {
42
42
  key: 0,
43
43
  class: "text-muted-foreground"
44
44
  }, {
@@ -50,7 +50,7 @@ var S = ["data-layout", "aria-label"], C = {
50
50
  key: 0,
51
51
  tone: "overlay"
52
52
  }, {
53
- trailing: x(() => [h(r, { tone: "overlay" })]),
53
+ trailing: x(() => [h(i, { tone: "overlay" })]),
54
54
  _: 1
55
55
  }))]),
56
56
  _: 1
@@ -59,10 +59,10 @@ var S = ["data-layout", "aria-label"], C = {
59
59
  key: 1,
60
60
  tone: "surface"
61
61
  }, {
62
- trailing: x(() => [h(r, { tone: "surface" })]),
62
+ trailing: x(() => [h(i, { tone: "surface" })]),
63
63
  _: 1
64
64
  })) : d("", !0)
65
- ], 2), i.showQueue ? (v(), u(n, {
65
+ ], 2), a.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 a(e, t) {
102
+ function i(e, t) {
103
103
  n.onTrackChange?.(e, t), r("trackChange", e, t);
104
104
  }
105
- return (t, r) => (v(), u(i, {
105
+ return (t, r) => (v(), u(a, {
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": a
111
+ "on-track-change": i
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-CC_vdcCN.js";
1
+ import { a as e, i as t, r as n, t as r } from "./stored-language-Bxhsx4iB.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-D0oYv0XS.js";
1
+ import { a as e } from "./i18n-provider-CaaZCmaR.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-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";
1
+ import "./stored-language-Bxhsx4iB.js";
2
+ import { a as e } from "./i18n-provider-CaaZCmaR.js";
3
+ import { i as t, r as n } from "./translator-CLI_7Crg.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]])) {
@@ -12,7 +12,7 @@ each other, so either can be used alone.
12
12
 
13
13
  | | Component | What it does |
14
14
  | --- | --- | --- |
15
- | <img src="../previews/data-table-filter-lists-datatable.webp" width="96" alt=""> | [DataTable](#datatable) | TanStack Table v9 with sorting, selection, expansion and pagination |
15
+ | <img src="../previews/data-table-filter-lists-datatable.webp" width="96" alt=""> | [DataTable](#datatable) | TanStack Table v9 with sorting, selection, expansion and pagination, client-side or one server page at a time |
16
16
  | <img src="../previews/data-table-filter-lists-filter.webp" width="96" alt=""> | [Filter](#filter) | Schema-driven filters with chips, presets and persistence |
17
17
  | <img src="../previews/data-table-filter-lists-filter.webp" width="96" alt=""> | [FilterInline, FilterPopover…](#filterinline-filterpopover-filterdialog-filtersheet-filterdrawer) | The filter panel inline or in an overlay |
18
18
  | <img src="../previews/data-table-filter-lists-pagination.webp" width="96" alt=""> | [Pagination](#pagination) | Pagination primitives |
@@ -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, column visibility, skeleton rows while loading and your own empty state.
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 — or one server page at a time, with controlled sorting and selection, cursor Previous / Next links and row links.
27
27
 
28
28
  ```vue
29
29
  <script setup lang="ts">
@@ -58,8 +58,9 @@ const columns = columnHelper.columns([
58
58
  ```
59
59
 
60
60
  Parts: `DataTable`; `DataTableColumnHeader` (a sortable header with a hide
61
- action); `DataTablePagination` and `DataTableViewOptions` (used by the table,
62
- exported for a table of your own; `DataTablePagination` takes `disabled`);
61
+ action); `DataTablePagination`, `DataTableServerFooter` and
62
+ `DataTableViewOptions` (used by the table, exported for a table or a footer
63
+ of your own; both footers take `disabled`);
63
64
  `useColumnHelpers<T>()` returns a
64
65
  `columnHelper` bound to the table's features and `predefinedColumns()` —
65
66
  `select` (checkboxes) and `expand` (a toggle for the expanded row, named
@@ -82,6 +83,15 @@ table enables, for typing your own column defs.
82
83
  | `loading` | `boolean` | `false` | skeleton rows, or the current rows dimmed; `aria-busy`; controls disabled |
83
84
  | `loadingRows` | `number` | the page size | how many skeleton rows |
84
85
  | `#empty="{ filtered }"` | slot | icon and "No data available" | content of the empty row, spanning every visible column |
86
+ | `manual` | `boolean` | `false` | `data` is one server page: no client filtering, sorting or paging; the footer is the server footer ([server-side data](#server-side-data)) |
87
+ | `v-model:sorting` | `SortingState` | held inside | controlled sorting; `update:sorting` gets the new state |
88
+ | `getRowId` | `(row, index) => string` | the index | the row's id, the key of the row selection |
89
+ | `v-model:row-selection` | `RowSelectionState` | held inside | controlled selection, by id; may hold rows of other pages |
90
+ | `pagination` | `{ totalCount?, offset?, previous?, next? }` | — | the server footer's range ("41–60 of 133") and Previous / Next: an href (a link) or a callback (a button) |
91
+ | `#footer="{ table }"` / `footer` | slot / `false \| (table) => VNode` | the pagination | replaces the footer; `false` removes it |
92
+ | `getRowHref` | `(row) => string \| undefined` | — | the first data cell links to the row's page; a click elsewhere on the row follows it |
93
+ | `@row-click` / `onRowClick` | `(row, event) => void` | — | a click on the row (not on a control); alone (without `getRowHref`), it makes the row focusable and Enter calls it |
94
+ | `linkComponent` | `Component` (given `href`) | `<a>` | the link for `getRowHref` and the page links: wrap your router's |
85
95
 
86
96
  ### Loading and empty states
87
97
 
@@ -132,10 +142,168 @@ default, or the `#empty` slot, told whether rows exist but none matches the
132
142
  filters (`filtered`); content that renders nothing gets the default.
133
143
  `DataTableEmptyState` types the slot props.
134
144
 
145
+ ### Server-side data
146
+
147
+ When the server filters, sorts and pages, hand the table **one page** and
148
+ turn on `manual`: the rows are rendered as given — no client slicing,
149
+ sorting or filtering — and the footer becomes the server footer. Keep the
150
+ query (sorting, cursors, filters) in the URL, so the page renders on the
151
+ server, survives a reload and works with no JavaScript.
152
+
153
+ ```vue
154
+ <!-- pages/students.vue (vue-router; Nuxt's useFetch works the same way) -->
155
+ <script setup lang="ts">
156
+ import type { RowSelectionState, SortingState } from '@tanstack/vue-table';
157
+ import { computed, defineComponent, h, ref, watch } from 'vue';
158
+ import { RouterLink as Link, useRoute, useRouter } from 'vue-router';
159
+ import {
160
+ Button,
161
+ DataTable,
162
+ DataTableColumnHeader,
163
+ useColumnHelpers,
164
+ } from '@nxgt/material-vue/components';
165
+
166
+ type Student = { id: string; name: string; email: string };
167
+ type Page = { nodes: Student[]; endCursor: string; hasNextPage: boolean; totalCount: number };
168
+ const PAGE_SIZE = 20;
169
+
170
+ // RouterLink takes `to`; the table hands its links an `href`.
171
+ const RouterLink = defineComponent({
172
+ props: { href: { type: String, required: true } },
173
+ setup: (props, { slots }) => () => h(Link, { to: props.href }, slots),
174
+ });
175
+
176
+ const route = useRoute();
177
+ const router = useRouter();
178
+ // The cursors of the pages before this one: Previous drops the last.
179
+ const trail = computed(() => [route.query.after ?? []].flat() as string[]);
180
+ const sorting = computed<SortingState>(() => {
181
+ const [id = 'name', dir = 'asc'] = String(route.query.sort ?? '').split('.');
182
+ return [{ id, desc: dir === 'desc' }];
183
+ });
184
+ const page = ref<Page>({ nodes: [], endCursor: '', hasNextPage: false, totalCount: 0 });
185
+ const loading = ref(true);
186
+ watch(
187
+ () => route.fullPath,
188
+ async () => {
189
+ loading.value = true;
190
+ const [sort] = sorting.value;
191
+ page.value = await api.students({
192
+ first: PAGE_SIZE,
193
+ after: trail.value.at(-1),
194
+ sort: sort.id,
195
+ dir: sort.desc ? 'desc' : 'asc',
196
+ });
197
+ loading.value = false;
198
+ },
199
+ { immediate: true },
200
+ );
201
+
202
+ const rowSelection = ref<RowSelectionState>({});
203
+ const selected = computed(() =>
204
+ Object.keys(rowSelection.value).filter((id) => rowSelection.value[id]),
205
+ );
206
+
207
+ const { columnHelper, predefinedColumns } = useColumnHelpers<Student>();
208
+ const columns = columnHelper.columns([
209
+ predefinedColumns().select,
210
+ columnHelper.accessor('name', {
211
+ header: ({ column }) => h(DataTableColumnHeader, { column, title: 'Name' }),
212
+ }),
213
+ columnHelper.accessor('email', { header: 'Email' }),
214
+ ]);
215
+
216
+ const pageHref = (cursors: string[]) =>
217
+ router.resolve({ query: { ...route.query, after: cursors } }).href;
218
+ const pagination = computed(() => ({
219
+ totalCount: page.value.totalCount,
220
+ offset: trail.value.length * PAGE_SIZE,
221
+ previous: trail.value.length ? pageHref(trail.value.slice(0, -1)) : null,
222
+ next: page.value.hasNextPage ? pageHref([...trail.value, page.value.endCursor]) : null,
223
+ }));
224
+ function sortBy([sort]: SortingState) {
225
+ // A new order starts at the first page.
226
+ router.push({
227
+ query: { ...route.query, sort: `${sort?.id ?? 'name'}.${sort?.desc ? 'desc' : 'asc'}`, after: [] },
228
+ });
229
+ }
230
+ async function archive() {
231
+ await api.archiveStudents(selected.value);
232
+ rowSelection.value = {};
233
+ }
234
+ </script>
235
+
236
+ <template>
237
+ <DataTable
238
+ manual
239
+ :data="page.nodes"
240
+ :columns="columns"
241
+ :loading="loading"
242
+ :sorting="sorting"
243
+ @update:sorting="sortBy"
244
+ :get-row-id="(student) => student.id"
245
+ v-model:row-selection="rowSelection"
246
+ :get-row-href="(student) => `/students/${student.id}`"
247
+ :link-component="RouterLink"
248
+ :pagination="pagination"
249
+ >
250
+ <template #actions>
251
+ <Button :disabled="!selected.length" @click="archive">
252
+ Archive {{ selected.length }} selected
253
+ </Button>
254
+ </template>
255
+ </DataTable>
256
+ </template>
257
+ ```
258
+
259
+ - **Paging.** `pagination.previous` and `next` are each an href, rendered as
260
+ a link (`rel="prev"` / `rel="next"`, through `linkComponent`), or a
261
+ callback, rendered as a button; absent or `null`, the button is disabled,
262
+ and so are both while `loading`. `totalCount` and `offset` (the 0-based
263
+ index of the page's first row) make the range: "41–60 of 133"
264
+ (`material.table.pagination.range`), "41–60" without a total, "133 rows"
265
+ without an offset. Cursor paging knows the offset from the trail's length
266
+ times the page size; leave it out if pages can be short.
267
+ - **Sorting** is `v-model:sorting`: the headers show it, and
268
+ `update:sorting` hands you the new `SortingState` to put in the URL.
269
+ Filters work the same way: render them in `#actions` and keep their values
270
+ in the URL; `manual` turns client filtering off.
271
+ - **Selection** is `v-model:row-selection`, keyed by `getRowId`, so it
272
+ survives a page change: ids of other pages stay in it, and the footer
273
+ counts them all ("3 rows selected", `material.table.pagination.selected`).
274
+ `#actions="{ table }"` reads it too (`table.store.state.rowSelection`). To
275
+ keep it across a reload, put it in the URL or `sessionStorage`. Unbound,
276
+ both models are held inside, as before. Bind both halves (`v-model`, or
277
+ the prop with its `update:` listener): Vue keeps a local copy of a model
278
+ whose listener is missing, so `:sorting` alone is not read-only, unlike
279
+ React's `sorting` without `onSortingChange`.
280
+ - **Row links.** With `getRowHref`, the first data column's cell is a real
281
+ link to the row's page — a tab stop, opened by Enter, in a new tab by
282
+ Ctrl/Cmd+click, followed with no JavaScript. A click anywhere else on the
283
+ row follows the same link, except on a control (a checkbox, a button, a
284
+ link, a menu, a field, or anything marked `data-row-click-ignore`) and at
285
+ the end of a text selection. The link wraps what the first data column
286
+ renders, so that column must render plain content, not a link or a
287
+ button of its own. `@row-click="(row, event) => …"` is for what is not a
288
+ navigation (opening a sheet). Alone, it makes the row focusable and Enter
289
+ calls it; with `getRowHref`, the link stays the keyboard target, and
290
+ calling `event.preventDefault()` in it stops a mouse click from following
291
+ the link. A focusable row has no role of its own: for anything a screen
292
+ reader user must reach, prefer `getRowHref` or a button in a cell.
293
+ - **Your own footer.** The `#footer="{ table }"` slot or the `footer` prop
294
+ (`(table) => VNode`) replaces the footer — `DataTableServerFooter` is the
295
+ default one, to wrap or reuse — and `:footer="false"` removes it.
296
+ - **Loading and empty** behave as above: keep `loading` on while the next
297
+ page is fetched, and the current rows stay in place, dimmed.
298
+
299
+ The server footer's strings are `material.table.pagination.*`
300
+ (`previous`, `next`, `label`, `range`, `range-open`, `total`, `selected`),
301
+ in English and French.
302
+
135
303
  For the TanStack API itself (column helpers, features, row models), see
136
304
  [TanStack Table v9](https://tanstack.com/table/latest).
137
305
 
138
- **Storybook:** `Data table, filter & lists/DataTable` · **React:** `DataTable`, `DataTableColumnHeader`, `DataTablePagination`, `DataTableViewOptions`
306
+ **Storybook:** `Data table, filter & lists/DataTable`, `…/DataTable/Server-side data` · **React:** `DataTable`, `DataTableColumnHeader`, `DataTablePagination`, `DataTableServerFooter`, `DataTableViewOptions`
139
307
 
140
308
  ---
141
309
 
package/docs/roadmap.md CHANGED
@@ -92,6 +92,12 @@ Nothing in progress right now; see Next.
92
92
 
93
93
  ## Shipped
94
94
 
95
+ - **DataTable server-side data** — hand the table one page from your loader
96
+ (`manual`): no client paging or sorting, `v-model:sorting` and
97
+ `v-model:row-selection` by row id (`getRowId`), a footer with "41–60 of 133"
98
+ and Previous / Next as links that work with no JavaScript (`pagination`,
99
+ `DataTableServerFooter`, `#footer`), and rows that link to their page
100
+ (`getRowHref`, `@row-click`, `linkComponent`). 0.26.0.
95
101
  - **TreeSelectField** — pick one or several items from a tree inside a form
96
102
  field, as chips when several, with an optional filter that keeps each match
97
103
  under its ancestors (`TreeSelectField`, `TreeSelectFormField`). 0.25.0.
@@ -160,6 +160,7 @@ export const COMPONENT_NAMES = [
160
160
  'DataTable',
161
161
  'DataTableColumnHeader',
162
162
  'DataTablePagination',
163
+ 'DataTableServerFooter',
163
164
  'DataTableViewOptions',
164
165
  'DateField',
165
166
  'DateFormField',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/material-vue",
3
- "version": "0.25.2",
3
+ "version": "0.26.0",
4
4
  "license": "MIT",
5
5
  "bugs": {
6
6
  "email": "stevegires@gmail.com"
@@ -119,7 +119,7 @@
119
119
  "docs:previews": "vitest run --project previews"
120
120
  },
121
121
  "dependencies": {
122
- "@nxgt/material-core": "^0.3.2",
122
+ "@nxgt/material-core": "^0.4.0",
123
123
  "@floating-ui/dom": "^1.8.0",
124
124
  "@fortawesome/fontawesome-common-types": "7.3.1",
125
125
  "@lucide/vue": "^1.45.0",