@nxgt/material-vue 0.41.0 → 0.42.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 (60) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +2 -1
  3. package/dist/{Button-BG3oDkrl.js → Button-CWmzvv9p.js} +1 -1
  4. package/dist/{HoverCardTrigger-Dh1ar_W4.js → HoverCardTrigger-C5AbAyHE.js} +2 -2
  5. package/dist/{camera-DV9jcl_3.js → camera-C-9slBUV.js} +4 -4
  6. package/dist/{components-C1JYYoQ4.js → components-DX4EycMi.js} +10541 -10262
  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-CeD0v_mw.js → form-scope-CN68PTXc.js} +1 -1
  11. package/dist/{i18n-provider-D5XyLQ3P.js → i18n-provider-DmZSTBnp.js} +1 -1
  12. package/dist/i18n.js +4 -4
  13. package/dist/{icon-button-DXrmfs6e.js → icon-button-DErh1aJS.js} +1 -1
  14. package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
  15. package/dist/lib/components/ui/layout/account-menu/account-menu-avatar.vue.d.ts +9 -0
  16. package/dist/lib/components/ui/layout/account-menu/account-menu.vue.d.ts +38 -0
  17. package/dist/lib/components/ui/layout/account-menu/index.d.ts +3 -0
  18. package/dist/lib/components/ui/layout/account-menu/types.d.ts +10 -0
  19. package/dist/lib/components/ui/layout/app-shell/activity-layout.vue.d.ts +21 -5
  20. package/dist/lib/components/ui/layout/app-shell/app-brand-logo.vue.d.ts +20 -0
  21. package/dist/lib/components/ui/layout/app-shell/app-brand.vue.d.ts +30 -0
  22. package/dist/lib/components/ui/layout/app-shell/app-header.vue.d.ts +13 -5
  23. package/dist/lib/components/ui/layout/app-shell/app-sidebar.vue.d.ts +12 -2
  24. package/dist/lib/components/ui/layout/app-shell/index.d.ts +1 -1
  25. package/dist/lib/components/ui/layout/app-shell/types.d.ts +10 -0
  26. package/dist/lib/components/ui/layout/index.d.ts +1 -0
  27. package/dist/lib/i18n/resources/en/en.d.ts +4 -0
  28. package/dist/lib/i18n/resources/en/material.json.d.ts +4 -0
  29. package/dist/lib/i18n/resources/fr/fr.d.ts +4 -0
  30. package/dist/lib/i18n/resources/fr/material.json.d.ts +4 -0
  31. package/dist/lib/i18n/resources/resources.d.ts +8 -0
  32. package/dist/lib/i18n/translator.d.ts +1 -1
  33. package/dist/lib/nuxt/component-names.d.ts +1 -1
  34. package/dist/lib.js +1 -1
  35. package/dist/main.js +17 -17
  36. package/dist/map.js +4 -4
  37. package/dist/material-vue.css +1 -1
  38. package/dist/{pdf-outline-sidebar-CgmJjhUz.js → pdf-outline-sidebar-BU4o_hBI.js} +2 -2
  39. package/dist/{pdf-page-BjQNWV72.js → pdf-page-anNe78Lc.js} +1 -1
  40. package/dist/{pdf-page-list-nG0l4SjH.js → pdf-page-list-CSY76HH6.js} +2 -2
  41. package/dist/{pdf-reader-Dekd4Ymw.js → pdf-reader-BhZNrW7R.js} +8 -8
  42. package/dist/{pdf-sidebar-gDj63dRH.js → pdf-sidebar-DcIpG7eh.js} +3 -3
  43. package/dist/{pdf-thumbnail-sidebar-C5ikii1Z.js → pdf-thumbnail-sidebar-CUC0V4if.js} +3 -3
  44. package/dist/{pdf-toolbar-DmmrSlCq.js → pdf-toolbar-C373S-ul.js} +4 -4
  45. package/dist/pdf.js +2 -2
  46. package/dist/{print-format.utils-CiUiQE-i.js → print-format.utils-C81c0weJ.js} +2 -2
  47. package/dist/{sortable-provider-lK0srHJF.js → sortable-provider-CL2UGGn8.js} +2 -2
  48. package/dist/{stored-language-dM2e5qaS.js → stored-language-Wtcj2HsU.js} +8 -0
  49. package/dist/{stx-player-C2VQCXFc.js → stx-player-CMmM3F5Z.js} +12 -12
  50. package/dist/{translator-DXL5RDRg.js → translator-D5Dp-dob.js} +1 -1
  51. package/dist/{use-heading-spy-Btwc0yad.js → use-heading-spy-BIBHtUXN.js} +1 -1
  52. package/dist/{use-material-translation-Du3Wvsk1.js → use-material-translation-COoBTGpB.js} +3 -3
  53. package/docs/components/README.md +2 -1
  54. package/docs/components/navigation-and-layout.md +56 -1
  55. package/docs/guides/app-shell.md +99 -19
  56. package/docs/guides/i18n.md +1 -1
  57. package/docs/previews/navigation-layout-accountmenu.webp +0 -0
  58. package/docs/roadmap.md +9 -0
  59. package/lib/nuxt/component-names.ts +1 -0
  60. 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 { Lf as n } from "./components-C1JYYoQ4.js";
4
- import { t as r } from "./use-material-translation-Du3Wvsk1.js";
3
+ import { Rf as n } from "./components-DX4EycMi.js";
4
+ import { t as r } from "./use-material-translation-COoBTGpB.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 { Kp as t } from "./components-C1JYYoQ4.js";
2
+ import { qp as t } from "./components-DX4EycMi.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 { Lf as t } from "./components-C1JYYoQ4.js";
3
- import { t as n } from "./pdf-page-BjQNWV72.js";
2
+ import { Rf as t } from "./components-DX4EycMi.js";
3
+ import { t as n } from "./pdf-page-anNe78Lc.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 { kn as t } from "./components-C1JYYoQ4.js";
3
- import { t as n } from "./use-material-translation-Du3Wvsk1.js";
4
- import { t as r } from "./Button-BG3oDkrl.js";
5
- import { o as i } from "./HoverCardTrigger-Dh1ar_W4.js";
6
- import { n as a } from "./pdf-page-BjQNWV72.js";
7
- import { t as o } from "./pdf-page-list-nG0l4SjH.js";
8
- import { t as s } from "./pdf-sidebar-gDj63dRH.js";
9
- import { t as c } from "./pdf-toolbar-DmmrSlCq.js";
2
+ import { kn as t } from "./components-DX4EycMi.js";
3
+ import { t as n } from "./use-material-translation-COoBTGpB.js";
4
+ import { t as r } from "./Button-CWmzvv9p.js";
5
+ import { o as i } from "./HoverCardTrigger-C5AbAyHE.js";
6
+ import { n as a } from "./pdf-page-anNe78Lc.js";
7
+ import { t as o } from "./pdf-page-list-CSY76HH6.js";
8
+ import { t as s } from "./pdf-sidebar-DcIpG7eh.js";
9
+ import { t as c } from "./pdf-toolbar-C373S-ul.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-Du3Wvsk1.js";
4
- import { t as r } from "./pdf-outline-sidebar-CgmJjhUz.js";
5
- import { t as i } from "./pdf-thumbnail-sidebar-C5ikii1Z.js";
3
+ import { t as n } from "./use-material-translation-COoBTGpB.js";
4
+ import { t as r } from "./pdf-outline-sidebar-BU4o_hBI.js";
5
+ import { t as i } from "./pdf-thumbnail-sidebar-CUC0V4if.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 { Lf as n } from "./components-C1JYYoQ4.js";
4
- import { t as r } from "./use-material-translation-Du3Wvsk1.js";
5
- import { t as i } from "./pdf-page-BjQNWV72.js";
3
+ import { Rf as n } from "./components-DX4EycMi.js";
4
+ import { t as r } from "./use-material-translation-COoBTGpB.js";
5
+ import { t as i } from "./pdf-page-anNe78Lc.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-Du3Wvsk1.js";
4
- import { f as r } from "./Button-BG3oDkrl.js";
5
- import { t as i } from "./icon-button-DXrmfs6e.js";
3
+ import { t as n } from "./use-material-translation-COoBTGpB.js";
4
+ import { f as r } from "./Button-CWmzvv9p.js";
5
+ import { t as i } from "./icon-button-DErh1aJS.js";
6
6
  import { a, i as o, n as s, o as c, r as l, t as u } from "./DropdownMenuTrigger-C7uYPf_9.js";
7
- import { o as d } from "./HoverCardTrigger-Dh1ar_W4.js";
7
+ import { o as d } from "./HoverCardTrigger-C5AbAyHE.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-Du3Wvsk1.js";
2
- import { t as n } from "./Button-BG3oDkrl.js";
1
+ import { n as e, t } from "./use-material-translation-COoBTGpB.js";
2
+ import { t as n } from "./Button-CWmzvv9p.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,5 +1,5 @@
1
- import { i as e, r as t, t as n } from "./stored-language-dM2e5qaS.js";
2
- import { r } from "./translator-DXL5RDRg.js";
1
+ import { i as e, r as t, t as n } from "./stored-language-Wtcj2HsU.js";
2
+ import { r } from "./translator-D5Dp-dob.js";
3
3
  import { t as i } from "./_baseIsEqual-BUD8VQJE.js";
4
4
  import { E as a } from "./number.utils-C1eB3GDr.js";
5
5
  import { defineComponent as o, getCurrentInstance as s, h as c, inject as l, provide as u, useId as d } from "vue";
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./utils-CykWQ0iJ.js";
2
- import { t } from "./use-material-translation-Du3Wvsk1.js";
3
- import { t as n } from "./icon-button-DXrmfs6e.js";
2
+ import { t } from "./use-material-translation-COoBTGpB.js";
3
+ import { t as n } from "./icon-button-DErh1aJS.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";
@@ -789,6 +789,10 @@ var e = {
789
789
  "open-menu": "Open menu",
790
790
  navigation: "Navigation"
791
791
  },
792
+ "account-menu": {
793
+ trigger: "Account menu, {name}",
794
+ "sign-out": "Sign out"
795
+ },
792
796
  filter: {
793
797
  title: "Filters",
794
798
  trigger: "Filters",
@@ -2033,6 +2037,10 @@ var e = {
2033
2037
  "open-menu": "Ouvrir le menu",
2034
2038
  navigation: "Navigation"
2035
2039
  },
2040
+ "account-menu": {
2041
+ trigger: "Menu du compte, {name}",
2042
+ "sign-out": "Se déconnecter"
2043
+ },
2036
2044
  filter: {
2037
2045
  title: "Filtres",
2038
2046
  trigger: "Filtres",
@@ -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, Xt as i, Yp as a, Yt as o, en as s, tn as c } from "./components-C1JYYoQ4.js";
3
- import { t as l } from "./use-material-translation-Du3Wvsk1.js";
2
+ import { $t as t, Jt as n, Qt as r, Xp as i, Xt as a, Yt as o, en as s, tn as c } from "./components-DX4EycMi.js";
3
+ import { t as l } from "./use-material-translation-COoBTGpB.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,7 +19,7 @@ var S = ["data-layout", "aria-label"], C = {
19
19
  Array
20
20
  ] }
21
21
  },
22
- setup(i) {
22
+ setup(a) {
23
23
  let { containerRef: g, state: w } = c();
24
24
  function T(e) {
25
25
  g.value = e instanceof HTMLElement ? e : null;
@@ -28,17 +28,17 @@ var S = ["data-layout", "aria-label"], C = {
28
28
  return (c, 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
33
  class: _(b(e)("@container rounded outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50", c.$props.class))
34
- }, [i.layout === "mini" ? (v(), u(r, { key: 0 })) : (v(), f("div", {
34
+ }, [a.layout === "mini" ? (v(), u(r, { 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(a, { variant: "title-medium" }, {
38
+ b(w).current?.kind === "audio" ? (v(), f("div", C, [h(i, { variant: "title-medium" }, {
39
39
  default: x(() => [m(y(b(w).current.title), 1)]),
40
40
  _: 1
41
- }), b(w).current.artist ? (v(), u(a, {
41
+ }), b(w).current.artist ? (v(), u(i, {
42
42
  key: 0,
43
43
  class: "text-muted-foreground"
44
44
  }, {
@@ -62,7 +62,7 @@ var S = ["data-layout", "aria-label"], C = {
62
62
  trailing: x(() => [h(t, { tone: "surface" })]),
63
63
  _: 1
64
64
  })) : d("", !0)
65
- ], 2), i.showQueue ? (v(), u(o, {
65
+ ], 2), a.showQueue ? (v(), u(o, {
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-dM2e5qaS.js";
1
+ import { a as e, i as t, r as n, t as r } from "./stored-language-Wtcj2HsU.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-D5XyLQ3P.js";
1
+ import { a as e } from "./i18n-provider-DmZSTBnp.js";
2
2
  import { c as t, d as n, l as r, n as i, s as a, u as o } from "./number.utils-C1eB3GDr.js";
3
3
  import { computed as s, getCurrentInstance as c, inject as l, onMounted as u, ref as d, shallowRef as f, toValue as p, watch as m } from "vue";
4
4
  import { injectConfigProviderContext as h } from "reka-ui";
@@ -1,6 +1,6 @@
1
- import "./stored-language-dM2e5qaS.js";
2
- import { a as e } from "./i18n-provider-D5XyLQ3P.js";
3
- import { i as t, r as n } from "./translator-DXL5RDRg.js";
1
+ import "./stored-language-Wtcj2HsU.js";
2
+ import { a as e } from "./i18n-provider-DmZSTBnp.js";
3
+ import { i as t, r as n } from "./translator-D5Dp-dob.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]])) {
@@ -71,9 +71,10 @@ Collections you query: data table, filter, pagination, tree, drag and drop. 6 en
71
71
 
72
72
  ## [Navigation & layout](navigation-and-layout.md)
73
73
 
74
- Where a page sits and how you move: app shell, sidebar, app bars, breadcrumb, page layouts, grid, resizable, skip link, pager, TOC, steppers, hero, doc site shell, signed-out page frame. 18 entries.
74
+ Where a page sits and how you move: app shell, account menu, sidebar, app bars, breadcrumb, page layouts, grid, resizable, skip link, pager, TOC, steppers, hero, doc site shell, signed-out page frame. 19 entries.
75
75
 
76
76
  [<img src="../previews/navigation-layout-appshell.webp" height="72" alt="ActivityLayout">](navigation-and-layout.md#activitylayout)
77
+ [<img src="../previews/navigation-layout-accountmenu.webp" height="72" alt="AccountMenu">](navigation-and-layout.md#accountmenu)
77
78
  [<img src="../previews/navigation-layout-sidebar.webp" height="72" alt="Sidebar">](navigation-and-layout.md#sidebar)
78
79
  [<img src="../previews/navigation-layout-breadcrumb.webp" height="72" alt="Breadcrumb">](navigation-and-layout.md#breadcrumb)
79
80
  [<img src="../previews/navigation-layout-stepper.webp" height="72" alt="Stepper">](navigation-and-layout.md#stepper)
@@ -8,6 +8,7 @@ documentation-site shell. Everything is imported from
8
8
  | | Component | What it does |
9
9
  | --- | --- | --- |
10
10
  | <img src="../previews/navigation-layout-appshell.webp" width="96" alt=""> | [ActivityLayout](#activitylayout) | App shell: sidebar, header, breadcrumbs, titled page |
11
+ | <img src="../previews/navigation-layout-accountmenu.webp" width="96" alt=""> | [AccountMenu](#accountmenu) | The app bar's avatar menu: the account, the app's links, Sign out |
11
12
  | <img src="../previews/navigation-layout-authcard.webp" width="96" alt=""> | [AuthCard](#authcard) | Signed-out page frame: one `main`, a centred card, its title the `h1` |
12
13
  | <img src="../previews/navigation-layout-sidebar.webp" width="96" alt=""> | [Sidebar](#sidebar) | Collapsible navigation sidebar |
13
14
  | <img src="../previews/navigation-layout-topappbar.webp" width="96" alt=""> | [TopAppBar, BottomAppBar](#topappbar) | Top and bottom bars |
@@ -75,7 +76,9 @@ which work alone; `findActiveMenuItem`, `isMenuItemActive` and
75
76
  | `items` | `MenuItem[]` | — | `label` (already translated), `url`, `icon` (a Font Awesome name), `footer` |
76
77
  | `linkComponent` | `Component` | `RouterLink` | The link each menu entry renders, given `href`; pass it as `link-component` (see the [app shell guide](../guides/app-shell.md)) |
77
78
  | `avatarSrc` | `string` | — | avatar image in the header |
78
- | `#brand` | slot | — | sidebar header |
79
+ | `logo` (or `#logo`), `name` | `AppLogo` (`string \| AppLogoImage`, `AppLogoImage` = `{ src, alt? }`), `string` | — | the app's logo alone in the collapsed rail, named by `name` (also its tooltip); logo and name side by side in the phone's menu sheet; `#logo` wins over `logo` |
80
+ | `#brand` | slot | — | replaces the sidebar header, in the rail and the menu sheet; wins over `logo` and `name` |
81
+ | `#header-title` | slot | — | the header's leading title (the context: an organisation's name), after the phone's menu button, truncated when long |
79
82
  | `#header-actions`, `#avatar` | slots | — | header, before the avatar; `#avatar` replaces it |
80
83
  | `title`, `subtitle`, `#actions` | `string`, `string`, slot | — | `ActivityContent`'s header card |
81
84
  | `#header` / `hideHeader` | slot / `boolean` | — / `false` | `ActivityContent`: replace or drop the header card |
@@ -100,6 +103,58 @@ entry, and an icon-only one (no label) falls back to its `aria-label`.
100
103
 
101
104
  ---
102
105
 
106
+ ## AccountMenu
107
+
108
+ ![AccountMenu](../previews/navigation-layout-accountmenu.webp)
109
+
110
+ The account menu of an app bar, for `ActivityLayout`'s `#avatar`: an avatar
111
+ button named "Account menu, Ada Lovelace" (the picture, else the initials on
112
+ `bg-primary`) that opens a menu aligned to its end, headed by the avatar, the
113
+ name and a second line, then the app's links and a destructive Sign out.
114
+
115
+ ```vue
116
+ <script setup lang="ts">
117
+ import { AccountMenu, ActivityLayout } from '@nxgt/material-vue/components';
118
+ </script>
119
+
120
+ <template>
121
+ <ActivityLayout :items="items">
122
+ <template #header-title>{{ school.name }}</template>
123
+ <template #avatar>
124
+ <AccountMenu
125
+ :name="user.name"
126
+ :secondary="user.email"
127
+ :src="user.picture"
128
+ :items="[{ label: t('menu.profile'), url: '/profile', icon: 'user' }]"
129
+ :signing-out="pending"
130
+ @sign-out="signOut"
131
+ />
132
+ </template>
133
+ <RouterView />
134
+ </ActivityLayout>
135
+ </template>
136
+ ```
137
+
138
+ | Prop / event | Type | Default | Effect |
139
+ | --- | --- | --- | --- |
140
+ | `name` | `string` | — | the trigger's name ("Account menu, {name}", `material.account-menu.trigger`), the menu's header, the initials |
141
+ | `secondary` | `string` | — | a second line under the name, the e-mail say |
142
+ | `src` | `string` | — | the avatar's picture; the initials until it loads, or without one |
143
+ | `items` | `AccountMenuItem[]` | `[]` | `{ label: string; url: string; icon?: IconName }`: links between the header and Sign out; `label` already translated, `icon` a [Font Awesome](https://fontawesome.com/icons) symbol id of the duotone sprite (see [Icon](primitives.md#icon)) |
144
+ | `linkComponent` | `Component` | `RouterLink` | the link each item renders, as in the app shell |
145
+ | `@sign-out` | event | — | Sign out was chosen; the menu stays open, so `signing-out` shows |
146
+ | `signOutLabel` | `string` | "Sign out" | `material.account-menu.sign-out` |
147
+ | `signingOut` | `boolean` | `false` | Sign out is busy (`aria-busy`, a spinner) and ignores a second selection |
148
+
149
+ Enter, Space or ArrowDown on the avatar opens the menu on its first item; the
150
+ arrows move, Escape closes it and gives the focus back to the avatar. The
151
+ items are real links (`<a href>`), named by their label. Its links need
152
+ `vue-router`, as the shell's do.
153
+
154
+ **Storybook:** `Navigation & layout/AccountMenu` · **React:** `AccountMenu` (prop `onSignOut`)
155
+
156
+ ---
157
+
103
158
  ## AuthCard
104
159
 
105
160
  ![AuthCard](../previews/navigation-layout-authcard.webp)
@@ -17,10 +17,7 @@ const items: MenuItem[] = [
17
17
  </script>
18
18
 
19
19
  <template>
20
- <ActivityLayout :items="items" avatar-src="/me.png">
21
- <template #brand>
22
- <strong>Acme</strong>
23
- </template>
20
+ <ActivityLayout :items="items" logo="/logo.svg" name="Acme" avatar-src="/me.png">
24
21
  <RouterView />
25
22
  </ActivityLayout>
26
23
  </template>
@@ -179,7 +176,11 @@ partitionMenu(items); // { content, footer }
179
176
  | `content-as` | `'main' \| 'div'` | `'main'` | The content region's element: the page's `<main>` landmark, or a `div` when your app renders its own `<main>` |
180
177
  | `scroll` | `'document' \| 'container'` | `'document'` | Who scrolls: the page (header and sidebar sticky), or the content region inside a shell one screen high |
181
178
  | `class` | `string` | — | Classes for the outer element (`[data-slot=activity-layout]`), which takes the other attributes too |
182
- | `#brand` | slot | an atom icon | Replaces the sidebar header: a logo, a product name. Always fill it; the default is a placeholder |
179
+ | `logo` | `string \| { src, alt? }` | an atom badge | The app's logo, an image URL: alone in the collapsed rail, beside `name` in the phone's menu sheet (see [The brand](#the-brand-logo-and-name)) |
180
+ | `#logo` | slot | — | The logo as a node (an inline SVG), in place of `logo`; it wins when both are given |
181
+ | `name` | `string` | — | The app's name, already translated: the logo's accessible name and tooltip in the rail, shown beside it in the sheet |
182
+ | `#brand` | slot | — | Replaces the sidebar header entirely, in the rail and in the menu sheet; wins over `logo` and `name` |
183
+ | `#header-title` | slot | — | The header's leading title: the context the app is in (the active organisation's name), right after the phone's menu button, truncated when long |
183
184
  | `#header-actions` | slot | — | In the header, before the avatar: notifications, help, `LanguageSwitcher`, `ThemeToggle` |
184
185
  | `#avatar` | slot | the `avatar-src` image | Replaces the avatar entirely |
185
186
  | default | slot | — | The routed content, usually `<RouterView />` |
@@ -214,32 +215,26 @@ the slots.
214
215
  ```vue
215
216
  <script setup lang="ts">
216
217
  import {
218
+ AccountMenu,
217
219
  ActivityLayout,
218
- Avatar,
219
- AvatarFallback,
220
- AvatarImage,
221
220
  LanguageSwitcher,
222
221
  type MenuItem,
223
222
  ThemeToggle,
224
223
  } from '@nxgt/material-vue/components';
225
224
 
226
- defineProps<{ items: MenuItem[]; user: { name: string; picture?: string } }>();
225
+ defineProps<{ items: MenuItem[]; user: { name: string; email: string; picture?: string } }>();
226
+ const emit = defineEmits<{ 'sign-out': [] }>();
227
227
  </script>
228
228
 
229
229
  <template>
230
- <ActivityLayout :items="items">
231
- <template #brand>
232
- <img src="/logo.svg" alt="Acme" class="h-8" />
233
- </template>
230
+ <ActivityLayout :items="items" logo="/logo.svg" name="Acme">
231
+ <template #header-title>Northwind school</template>
234
232
  <template #header-actions>
235
233
  <LanguageSwitcher />
236
234
  <ThemeToggle />
237
235
  </template>
238
236
  <template #avatar>
239
- <Avatar>
240
- <AvatarImage :src="user.picture ?? ''" :alt="user.name" />
241
- <AvatarFallback>{{ user.name.slice(0, 2) }}</AvatarFallback>
242
- </Avatar>
237
+ <AccountMenu :name="user.name" :secondary="user.email" :src="user.picture" @sign-out="emit('sign-out')" />
243
238
  </template>
244
239
  <RouterView />
245
240
  </ActivityLayout>
@@ -254,7 +249,92 @@ entry closes it — so does any route change, Back included — and focus goes t
254
249
  the page's `<main>`, unless the new screen already focused an element inside it
255
250
  (an error page's `h1`), which keeps it; closed with Escape, the close button or the overlay,
256
251
  focus returns to the menu button. Its entries show their labels, so they have
257
- no tooltip: none opens over the first entry as focus moves in.
252
+ no tooltip: none opens over the first entry as focus moves in. It shows the
253
+ app's `logo` and `name` (or `#brand`) above the entries, as the expanded
254
+ sidebar does.
255
+
256
+ ### The app bar: a title, the actions, the account
257
+
258
+ The header reads, from the start: the phone's menu button, `#header-title`
259
+ (the context the app is in, the active school or organisation), then
260
+ `#header-actions` (language, notifications) and `#avatar` at the end. The
261
+ title takes the room left and ends in an ellipsis when it does not fit, so
262
+ the avatar always stays on screen. It is not a heading: the screen's `h1` is
263
+ `ActivityContent`'s title.
264
+
265
+ `AccountMenu` is the `#avatar` most apps want: an avatar button named
266
+ "Account menu, Ada Lovelace" (`material.account-menu.trigger`) that opens a
267
+ menu aligned to its end, headed by the avatar, the name and `secondary`, then
268
+ the app's `items` and a destructive Sign out
269
+ (`material.account-menu.sign-out`). Choosing Sign out emits `sign-out` and
270
+ keeps the menu open: pass `signing-out` while the request runs, and the item
271
+ shows a spinner and ignores a second selection. Without `src`, or until it
272
+ loads, the avatar shows the initials on `bg-primary`.
273
+
274
+ ```vue
275
+ <AccountMenu
276
+ :name="user.name"
277
+ :secondary="user.email"
278
+ :src="user.picture"
279
+ :items="[
280
+ { label: t('menu.profile'), url: '/profile', icon: 'user' },
281
+ { label: t('menu.settings'), url: '/settings', icon: 'gear' },
282
+ ]"
283
+ :signing-out="pending"
284
+ @sign-out="signOut"
285
+ />
286
+ ```
287
+
288
+ Each entry is an `AccountMenuItem`, exported with it:
289
+
290
+ ```ts
291
+ type AccountMenuItem = {
292
+ label: string; // already translated
293
+ url: string; // rendered as a real link through `link-component`
294
+ icon?: IconName; // optional, decorative: the label names the entry
295
+ };
296
+ ```
297
+
298
+ `icon` is an `IconName`, a symbol id of the Font Awesome duotone sprite you serve (`user`, `gear`, `arrow-right-from-bracket`; browse the names at <https://fontawesome.com/icons>, and see [getting started](../getting-started.md#icons) for serving the sprite).
299
+
300
+ ### The brand: logo and name
301
+
302
+ `logo` (or the `#logo` slot) and `name` put the app's brand in the sidebar
303
+ header, in the shape each place has room for. `logo` is typed `AppLogo`
304
+ (`string | AppLogoImage`, where `AppLogoImage` is `{ src: string; alt?: string }`
305
+ and a string is the image URL), both exported from
306
+ `@nxgt/material-vue/components`:
307
+
308
+ - **The collapsed rail** (desktop, about 70 px wide) shows the **logo alone**.
309
+ `name` is its accessible name (an `<img alt>`, or `role="img"` with an
310
+ `aria-label` around the `#logo` node) and shows as a tooltip on hover, to
311
+ its right.
312
+ - **The expanded sidebar and the phone's menu sheet** show the **logo and the
313
+ name side by side**. The name is on screen there, so the logo is decorative
314
+ (`alt=""` or `aria-hidden`), and no tooltip repeats it.
315
+
316
+ ```vue
317
+ <ActivityLayout :items="items" logo="/logo.svg" :name="t('app.name')">…</ActivityLayout>
318
+ <!-- An image with its own alt, used when there is no name: -->
319
+ <ActivityLayout :items="items" :logo="{ src: '/logo.svg', alt: 'Acme' }">…</ActivityLayout>
320
+ <!-- A node, an inline SVG for instance: -->
321
+ <ActivityLayout :items="items" name="Acme">
322
+ <template #logo><AcmeMark class="size-8" /></template>
323
+ …
324
+ </ActivityLayout>
325
+ ```
326
+
327
+ Without a logo the header keeps its placeholder atom badge (named by `name`
328
+ in the rail when there is one); fill `logo` in a real app. `#brand` is the
329
+ full override: one node rendered as given, collapsed or expanded alike, in the
330
+ rail and in the menu sheet. `AppSidebar` takes the same props and slots, and
331
+ `AppHeader` takes them for its menu sheet.
332
+
333
+ `#brand` is rendered twice, once in the rail and once in the menu sheet (the
334
+ rail stays in the page on a phone, hidden): an `id` inside it appears twice
335
+ while the sheet is open, so leave ids out of it. A link in `#brand` (a home
336
+ link around the logo) is the sheet's first focusable element, so it takes
337
+ the focus when the sheet opens.
258
338
 
259
339
  ## Landmarks, headings and scrolling
260
340
 
@@ -375,7 +455,7 @@ home included, is a `RouterLink`, so it goes through the router's base: under
375
455
  `AppSidebar`, `AppHeader` and `AppBreadcrumb` are exported for a layout of your
376
456
  own. `AppSidebar` and `AppHeader` take the same `items`; `AppSidebar` also
377
457
  takes `expanded` (`v-model:expanded`) and `with-toggle-icon`, and has the
378
- `#brand` slot; `AppHeader` takes `avatar-src` and `show-menu-icon` (default
458
+ `#brand` slot, `logo`, `name` and `#logo`; `AppHeader` takes `avatar-src`, `logo`, `name` and `show-menu-icon` (default
379
459
  `true`), with `#actions`, `#avatar` and a default slot for the middle of the
380
460
  bar. The lower-level `Sidebar`, `TopAppBar`, `NavigationRail` and `Breadcrumb`
381
461
  primitives are on the
@@ -292,7 +292,7 @@ the `LocationPreview` trigger (`material.map.preview` / `preview-named`),
292
292
  (`material['duration-field']['hours-label']` / `minutes-label`), and
293
293
  the delete button of the deprecated `ContactItem` / `OpeningHoursItem`
294
294
  (`material.contacts.buttons.remove`,
295
- `material['opening-hours'].buttons.remove`). `FacetGroup`'s toggle is named "Level, 2 selected" / "Level, 2 sélectionnés" (`material.facet-group.name`, an ICU plural over `{title}` and `{count}`) and its Clear button "Clear Level" / "Effacer Level" (`material.facet-group.clear-label`, with the visible `material.facet-group.clear`). When the form of a
295
+ `material['opening-hours'].buttons.remove`). `FacetGroup`'s toggle is named "Level, 2 selected" / "Level, 2 sélectionnés" (`material.facet-group.name`, an ICU plural over `{title}` and `{count}`) and its Clear button "Clear Level" / "Effacer Level" (`material.facet-group.clear-label`, with the visible `material.facet-group.clear`). `AccountMenu`'s avatar button is named "Account menu, {name}" / "Menu du compte, {name}" (`material.account-menu.trigger`, over `{name}`) and its last item reads "Sign out" / "Se déconnecter" (`material.account-menu.sign-out`), unless you pass `sign-out-label`. When the form of a
296
296
  `ConfirmationDialog` with `form-options` cannot be loaded, it says so in the
297
297
  form's place: "The form could not be loaded." / "Le formulaire n’a pas pu être
298
298
  chargé." (`material.confirm-dialog.form-failed`). The keys and the French are the
package/docs/roadmap.md CHANGED
@@ -167,6 +167,15 @@ version they shipped in, and planned entries may name the major they target.
167
167
 
168
168
  ## Shipped
169
169
 
170
+ - **The app's brand, a header title and an account menu** — the app shell
171
+ takes the app's `logo` and `name`: the logo alone in the collapsed rail,
172
+ named by the app's name and shown as its tooltip, the logo and the name side
173
+ by side expanded and in the phone's menu sheet, which now shows `brand` too;
174
+ `#header-title`, the context the app is in, right after the phone's menu
175
+ button and truncated when long; and `AccountMenu`, the avatar button named
176
+ "Account menu, <name>" that opens the account's name and e-mail, the app's
177
+ links and a busy-aware Sign out (`sign-out`).
178
+
170
179
  - **Rows a user adds, removes and reorders** — `RepeatableGroup` and
171
180
  `RepeatableGroupFormField` over the form library's field array: rows from
172
181
  the `row` slot, a list for assistive tech, Remove and move buttons named after
@@ -4,6 +4,7 @@ export const COMPONENT_NAMES = [
4
4
  'AccordionContent',
5
5
  'AccordionItem',
6
6
  'AccordionTrigger',
7
+ 'AccountMenu',
7
8
  'ActionCard',
8
9
  'Activity',
9
10
  'ActivityContent',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxgt/material-vue",
3
- "version": "0.41.0",
3
+ "version": "0.42.0",
4
4
  "license": "MIT",
5
5
  "bugs": {
6
6
  "email": "stevegires@gmail.com"
@@ -124,7 +124,7 @@
124
124
  "docs:previews": "vitest run --project previews"
125
125
  },
126
126
  "dependencies": {
127
- "@nxgt/material-core": "^0.11.1",
127
+ "@nxgt/material-core": "^0.12.0",
128
128
  "@floating-ui/dom": "^1.8.0",
129
129
  "@fortawesome/fontawesome-common-types": "7.3.1",
130
130
  "@lucide/vue": "^1.45.0",