@nxgt/material-vue 0.27.0 → 0.29.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 (62) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +2 -2
  3. package/dist/{Button-DEUkxkrJ.js → Button-D1uzdbOJ.js} +1 -1
  4. package/dist/{HoverCardTrigger-TDll55LP.js → HoverCardTrigger-DaM5d4hE.js} +2 -2
  5. package/dist/{camera-UJXnnFeb.js → camera-DZ5nYW-v.js} +4 -4
  6. package/dist/{components-BZ1XAK37.js → components-Cteo76NG.js} +9603 -9508
  7. package/dist/components.js +5 -5
  8. package/dist/composables.js +2 -2
  9. package/dist/dnd.js +1 -1
  10. package/dist/{form-scope-BFGca0h6.js → form-scope-BvdPqk6T.js} +1 -1
  11. package/dist/{i18n-provider-DBusa725.js → i18n-provider-BPK68eqM.js} +1 -1
  12. package/dist/i18n.js +4 -4
  13. package/dist/{icon-button-jHB4MhQ5.js → icon-button-D60G5B_Z.js} +1 -1
  14. package/dist/lib/components/ui/feedback/toast/index.d.ts +2 -1
  15. package/dist/lib/components/ui/feedback/toast/toast-data.d.ts +23 -0
  16. package/dist/lib/components/ui/feedback/toast/toast.vue.d.ts +11 -4
  17. package/dist/lib/components/ui/feedback/toast/toasts.d.ts +26 -6
  18. package/dist/lib/components/ui/fields/contacts/consts.d.ts +1 -1
  19. package/dist/lib/components/ui/primitives/tabs/Tabs.vue.d.ts +6 -2
  20. package/dist/lib/components/ui/primitives/tabs/TabsList.vue.d.ts +2 -2
  21. package/dist/lib/components/ui/primitives/tabs/TabsTrigger.vue.d.ts +6 -4
  22. package/dist/lib/components/ui/primitives/tabs/tabs-classes.d.ts +27 -0
  23. package/dist/lib/components/ui/primitives/tabs/tabs-indicator.vue.d.ts +25 -0
  24. package/dist/lib/components/ui/primitives/tabs/tabs-variant.d.ts +5 -8
  25. package/dist/lib/components/ui/widgets/emoji/types.d.ts +1 -6
  26. package/dist/lib/i18n/resources/en/en.d.ts +14 -0
  27. package/dist/lib/i18n/resources/en/material.json.d.ts +15 -1
  28. package/dist/lib/i18n/resources/fr/fr.d.ts +14 -0
  29. package/dist/lib/i18n/resources/fr/material.json.d.ts +15 -1
  30. package/dist/lib/i18n/resources/resources.d.ts +28 -0
  31. package/dist/lib/i18n/translator.d.ts +1 -1
  32. package/dist/lib.js +2 -2
  33. package/dist/main.js +10 -10
  34. package/dist/map.js +4 -4
  35. package/dist/material-vue.css +1 -1
  36. package/dist/{pdf-outline-sidebar-ChjAXoUX.js → pdf-outline-sidebar-Bln9F9gF.js} +2 -2
  37. package/dist/{pdf-page-D4OTPcpF.js → pdf-page-BVtXjnHS.js} +1 -1
  38. package/dist/{pdf-page-list-CFgKYLur.js → pdf-page-list-D3eng5h4.js} +2 -2
  39. package/dist/{pdf-reader-B1hw81cG.js → pdf-reader-CPms5uyj.js} +8 -8
  40. package/dist/{pdf-sidebar-Do5HzrDq.js → pdf-sidebar-lzBEntPH.js} +3 -3
  41. package/dist/{pdf-thumbnail-sidebar-CMge0C9-.js → pdf-thumbnail-sidebar-DB3PmiGW.js} +3 -3
  42. package/dist/{pdf-toolbar-BBio9baU.js → pdf-toolbar-mWlGTWU1.js} +4 -4
  43. package/dist/pdf.js +2 -2
  44. package/dist/{sortable-provider-DMVbbmUU.js → sortable-provider-Dml11nQY.js} +2 -2
  45. package/dist/{stored-language-AJpwOMsA.js → stored-language-UnInBB6T.js} +30 -2
  46. package/dist/{stx-player-gtyStl7e.js → stx-player-BpQHnHaa.js} +2 -2
  47. package/dist/{translator-D2_swsMz.js → translator-J3_vaV_A.js} +1 -1
  48. package/dist/{use-heading-spy-BbEbAfPK.js → use-heading-spy-_dk8kGwL.js} +1 -1
  49. package/dist/{use-material-translation-BHtgyMz3.js → use-material-translation-Do6ztdtV.js} +3 -3
  50. package/docs/components/data-display.md +1 -1
  51. package/docs/components/feedback-and-status.md +35 -6
  52. package/docs/components/primitives.md +81 -18
  53. package/docs/components/rich-widgets.md +10 -0
  54. package/docs/guides/i18n.md +19 -2
  55. package/docs/guides/theming.md +17 -3
  56. package/docs/previews/data-display-codegroup.webp +0 -0
  57. package/docs/previews/primitives-tabs.webp +0 -0
  58. package/docs/previews/rich-widgets-emojipicker.webp +0 -0
  59. package/docs/roadmap.md +14 -1
  60. package/docs/troubleshooting.md +83 -0
  61. package/package.json +2 -2
  62. package/dist/lib/components/ui/widgets/emoji/emoji.groups.d.ts +0 -6
@@ -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-BZ1XAK37.js";
4
- import { t as r } from "./use-material-translation-BHtgyMz3.js";
3
+ import { Ef as n } from "./components-Cteo76NG.js";
4
+ import { t as r } from "./use-material-translation-Do6ztdtV.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-BZ1XAK37.js";
2
+ import { kp as t } from "./components-Cteo76NG.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-BZ1XAK37.js";
3
- import { t as n } from "./pdf-page-D4OTPcpF.js";
2
+ import { Ef as t } from "./components-Cteo76NG.js";
3
+ import { t as n } from "./pdf-page-BVtXjnHS.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-BZ1XAK37.js";
3
- import { t as n } from "./use-material-translation-BHtgyMz3.js";
4
- import { t as r } from "./Button-DEUkxkrJ.js";
5
- import { o as i } from "./HoverCardTrigger-TDll55LP.js";
6
- import { n as a } from "./pdf-page-D4OTPcpF.js";
7
- import { t as o } from "./pdf-page-list-CFgKYLur.js";
8
- import { t as s } from "./pdf-sidebar-Do5HzrDq.js";
9
- import { t as c } from "./pdf-toolbar-BBio9baU.js";
2
+ import { On as t } from "./components-Cteo76NG.js";
3
+ import { t as n } from "./use-material-translation-Do6ztdtV.js";
4
+ import { t as r } from "./Button-D1uzdbOJ.js";
5
+ import { o as i } from "./HoverCardTrigger-DaM5d4hE.js";
6
+ import { n as a } from "./pdf-page-BVtXjnHS.js";
7
+ import { t as o } from "./pdf-page-list-D3eng5h4.js";
8
+ import { t as s } from "./pdf-sidebar-lzBEntPH.js";
9
+ import { t as c } from "./pdf-toolbar-mWlGTWU1.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-BHtgyMz3.js";
4
- import { t as r } from "./pdf-outline-sidebar-ChjAXoUX.js";
5
- import { t as i } from "./pdf-thumbnail-sidebar-CMge0C9-.js";
3
+ import { t as n } from "./use-material-translation-Do6ztdtV.js";
4
+ import { t as r } from "./pdf-outline-sidebar-Bln9F9gF.js";
5
+ import { t as i } from "./pdf-thumbnail-sidebar-DB3PmiGW.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-BZ1XAK37.js";
4
- import { t as r } from "./use-material-translation-BHtgyMz3.js";
5
- import { t as i } from "./pdf-page-D4OTPcpF.js";
3
+ import { Ef as n } from "./components-Cteo76NG.js";
4
+ import { t as r } from "./use-material-translation-Do6ztdtV.js";
5
+ import { t as i } from "./pdf-page-BVtXjnHS.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-BHtgyMz3.js";
4
- import { d as r } from "./Button-DEUkxkrJ.js";
5
- import { t as i } from "./icon-button-jHB4MhQ5.js";
3
+ import { t as n } from "./use-material-translation-Do6ztdtV.js";
4
+ import { d as r } from "./Button-D1uzdbOJ.js";
5
+ import { t as i } from "./icon-button-D60G5B_Z.js";
6
6
  import { a, i as o, n as s, o as c, r as l, t as u } from "./DropdownMenuTrigger-BsuyNVzV.js";
7
- import { o as d } from "./HoverCardTrigger-TDll55LP.js";
7
+ import { o as d } from "./HoverCardTrigger-DaM5d4hE.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-BHtgyMz3.js";
2
- import { t as n } from "./Button-DEUkxkrJ.js";
1
+ import { n as e, t } from "./use-material-translation-Do6ztdtV.js";
2
+ import { t as n } from "./Button-D1uzdbOJ.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-BHtgyMz3.js";
3
- import { t as n } from "./icon-button-jHB4MhQ5.js";
2
+ import { t } from "./use-material-translation-Do6ztdtV.js";
3
+ import { t as n } from "./icon-button-D60G5B_Z.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";
@@ -55,7 +55,21 @@ var e = {
55
55
  picker: "Emoji",
56
56
  pick: "Choose an emoji",
57
57
  search: "Search emoji",
58
- empty: "No emoji match"
58
+ empty: "No emoji match",
59
+ groups: {
60
+ all: "All",
61
+ recent: "Recent",
62
+ general: "General",
63
+ people_body: "People & body",
64
+ components: "Components",
65
+ animals_nature: "Animals & nature",
66
+ food_drink: "Food & drink",
67
+ travel_places: "Travel & places",
68
+ activities: "Activities",
69
+ objects: "Objects",
70
+ symbols: "Symbols",
71
+ flags: "Flags"
72
+ }
59
73
  },
60
74
  mentions: { empty: "No matches" },
61
75
  cards: {
@@ -1240,7 +1254,21 @@ var e = {
1240
1254
  picker: "Emoji",
1241
1255
  pick: "Choisir un emoji",
1242
1256
  search: "Chercher un emoji",
1243
- empty: "Aucun emoji"
1257
+ empty: "Aucun emoji",
1258
+ groups: {
1259
+ all: "Tous",
1260
+ recent: "Récents",
1261
+ general: "Général",
1262
+ people_body: "Personnes et corps",
1263
+ components: "Composants",
1264
+ animals_nature: "Animaux et nature",
1265
+ food_drink: "Nourriture et boissons",
1266
+ travel_places: "Voyages et lieux",
1267
+ activities: "Activités",
1268
+ objects: "Objets",
1269
+ symbols: "Symboles",
1270
+ flags: "Drapeaux"
1271
+ }
1244
1272
  },
1245
1273
  mentions: { empty: "Aucun résultat" },
1246
1274
  cards: {
@@ -1,6 +1,6 @@
1
1
  import { t as e } from "./utils-CykWQ0iJ.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-BZ1XAK37.js";
3
- import { t as l } from "./use-material-translation-BHtgyMz3.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-Cteo76NG.js";
3
+ import { t as l } from "./use-material-translation-Do6ztdtV.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 = {
@@ -1,4 +1,4 @@
1
- import { a as e, i as t, r as n, t as r } from "./stored-language-AJpwOMsA.js";
1
+ import { a as e, i as t, r as n, t as r } from "./stored-language-UnInBB6T.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-DBusa725.js";
1
+ import { a as e } from "./i18n-provider-BPK68eqM.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-AJpwOMsA.js";
2
- import { a as e } from "./i18n-provider-DBusa725.js";
3
- import { i as t, r as n } from "./translator-D2_swsMz.js";
1
+ import "./stored-language-UnInBB6T.js";
2
+ import { a as e } from "./i18n-provider-BPK68eqM.js";
3
+ import { i as t, r as n } from "./translator-J3_vaV_A.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]])) {
@@ -631,7 +631,7 @@ function openEvent() {
631
631
 
632
632
  ![CodeBlock](../previews/data-display-codeblock.webp)
633
633
 
634
- Highlighted code with line numbers and copy, a Monaco diff, a collapsible JSON tree, and tabbed samples (`Tabs variant="line"`).
634
+ Highlighted code with line numbers and copy, a Monaco diff, a collapsible JSON tree, and tabbed samples (`Tabs`, underlined).
635
635
 
636
636
  <img src="../previews/data-display-codegroup.webp" height="96" alt="CodeGroup">
637
637
  <img src="../previews/data-display-jsonviewer.webp" height="96" alt="JsonViewer">
@@ -572,7 +572,7 @@ async function upload() {
572
572
  | --- | --- | --- | --- |
573
573
  | `title` / `description` | `string` | preset title for `success`, `error`, `loading` | toast text |
574
574
  | `icon` | a VNode (`toast`) · icon name (`toasts.*`) | preset icon | leading icon |
575
- | `action` | `{ label, onClick? }` or `(dismiss) => VNode` | a translated “Close” for `success`, `info`, `error`; a preset keeps the caller's own | trailing button: `onClick`, then the toast closes; or what the function renders, given `dismiss`. Brings the X button and a persistent toast, see below |
575
+ | `action` | `{ label, onClick? }` (`label`: a string or a component) or `(dismiss) => VNode` | a translated “Close” for `success`, `info`, `error`; every preset keeps the caller's own, in either call | trailing button: `onClick` (given the click event, optional when you call it yourself), then the toast closes; or what the function renders, given `dismiss`. Brings the X button and a persistent toast, see below |
576
576
  | `dismissLabel` | `string` | the translated “Close” when an `action` is given | accessible name of the X icon button beside the action; `''` hides it |
577
577
  | `role` | `'status' \| 'alert'` | none (`toasts.error`: `'alert'`) | live-region role. Toasts are already announced politely by the `Toaster`'s region; `alert` interrupts, and may be read twice by some screen readers |
578
578
  | `options` | vue-sonner `ExternalToast` | `duration: 5000` for presets, `Infinity` for `loading` and for any toast with your own `action` (a preset's fallback "Close" still times out at 5000) unless a `duration` is passed | passed to Sonner (`id`, `duration`, `position`…) |
@@ -586,7 +586,7 @@ stylesheet comes with this package's (`styles.css`, or the compiled
586
586
  stylesheet). `Toast` is the component each toast renders, and `sonnerToast`
587
587
  is vue-sonner's own `toast`, for anything the presets do not cover.
588
588
 
589
- **Types:** `ToasterProps` (vue-sonner's), from `@nxgt/material-vue/components`.
589
+ **Types:** `ToastAction`, `ToastProps`, `ToastMethodProps`, `ToastData`, `ToastArgs`, `PresetProps` (the props of `success`, `warning`, `info` and `error`: use it, not `Parameters<typeof toasts.success>[0]`, which is now a union with the `(message, data)` form), and `ToasterProps` (vue-sonner's), from `@nxgt/material-vue/components`.
590
590
 
591
591
  **On a phone** (under 600px wide, where vue-sonner lays toasts out across the
592
592
  screen), a `Toaster` with no `position` shows its toasts at the top
@@ -603,6 +603,27 @@ raise its `top` when a sticky header must stay visible:
603
603
  <Toaster position="bottom-center" /> <!-- bottom-center everywhere -->
604
604
  ```
605
605
 
606
+ **vue-sonner's call works too.** Every preset (`custom`, `success`, `info`,
607
+ `warning`, `error`, `loading`) also takes vue-sonner's own `(message, data)`: the
608
+ message (a string or a component) is the title, `data.description` and `data.action` are shown, and the
609
+ rest of `data` (`id`, `duration`, `onDismiss`…) is passed to vue-sonner as the
610
+ options. `data` is typed `ToastData`, vue-sonner's `ExternalToast` without `icon`
611
+ (a preset draws its own) and `cancel` (the X button stands for it): passing
612
+ either is a type error rather than a silent no-op.
613
+
614
+ ```ts
615
+ toasts.success('Staff member restored', {
616
+ description: 'They can sign in again.',
617
+ action: { label: 'Send an invitation', onClick: openInvite },
618
+ });
619
+ // the same as
620
+ toasts.success({
621
+ title: 'Staff member restored',
622
+ description: 'They can sign in again.',
623
+ action: { label: 'Send an invitation', onClick: openInvite },
624
+ });
625
+ ```
626
+
606
627
  **An action keeps the toast open.** `toast()` itself does it, so a preset and a
607
628
  direct call behave alike: any toast with an `action` is persistent by default
608
629
  (`options.duration: Infinity`, WCAG 2.2.1: a timed toast can vanish before a
@@ -620,10 +641,18 @@ elsewhere) is ignored, so a tapped toast still goes after its duration. On a
620
641
  touch screen a tap no longer fans out a stack of two or more toasts either: pass
621
642
  `expand` to keep the stack open. Sonner does not close a toast on Escape (it
622
643
  only collapses the stack); this `Toast` does when focus is inside it, with or
623
- without an action. The close button is reached with Tab. **Focus after a
624
- dismiss:** the dismissed toast leaves the page with the focused control in it,
625
- so focus falls back to `document.body`; move it yourself (`onClick`, or after
626
- `toasts.dismiss()`) when the flow needs it elsewhere.
644
+ without an action. The close button is reached with Tab.
645
+
646
+ **Focus after an action.** A click on a toast's action first gives the focus
647
+ back to the control that had it before the focus entered the toast, then runs
648
+ your `onClick`, then closes the toast; a function action's `dismiss` does the
649
+ same before it closes. So an action that opens a `Dialog` (or moves the focus
650
+ anywhere else) keeps it there: the dialog holds the control it focused once
651
+ the toast has gone, and gives the focus back to that earlier control when it
652
+ closes (WCAG 2.4.3). An action that moves the focus nowhere leaves it on that
653
+ earlier control. A toast closed by its X or by Escape takes its focused
654
+ control with it, so the focus falls back to `document.body`; move it yourself
655
+ (after `toasts.dismiss()`) when the flow needs it elsewhere.
627
656
 
628
657
  **Announced to screen readers:** `Toaster` renders a polite live region
629
658
  (`<section aria-live="polite">`) that is in the page before the first toast,
@@ -584,12 +584,12 @@ use `DataTable` on the [Data table, filter & lists](data-table-filter-and-lists.
584
584
 
585
585
  ![Tabs](../previews/primitives-tabs.webp)
586
586
 
587
- Tabs whose triggers can take an icon. `variant="line"` is the quieter pill style.
587
+ Tabs whose triggers can take an icon, in two looks: `underline` (the default) and the `segmented` pill.
588
588
 
589
589
  ```vue
590
590
  <script setup lang="ts">
591
+ import { Calendar, User } from '@lucide/vue';
591
592
  import {
592
- Icon,
593
593
  Tabs,
594
594
  TabsContent,
595
595
  TabsList,
@@ -600,8 +600,11 @@ import {
600
600
  <template>
601
601
  <Tabs default-value="account" class="w-md">
602
602
  <TabsList>
603
- <TabsTrigger value="account"><Icon name="user" />Account</TabsTrigger>
604
- <TabsTrigger value="calendar"><Icon name="calendar" />Calendar</TabsTrigger>
603
+ <TabsTrigger value="account" :icon="User">Account</TabsTrigger>
604
+ <TabsTrigger value="calendar">
605
+ <template #icon><Calendar /></template>
606
+ Calendar
607
+ </TabsTrigger>
605
608
  </TabsList>
606
609
  <TabsContent value="account">Your profile.</TabsContent>
607
610
  <TabsContent value="calendar">Your events.</TabsContent>
@@ -609,36 +612,96 @@ import {
609
612
  </template>
610
613
  ```
611
614
 
612
- Parts: `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`. An icon goes in the
613
- trigger's slot, before the label; there is no `icon` prop in Vue. By default
614
- (`variant="bar"`) the list is one muted pill with the active trigger raised.
615
- `variant="line"` is React's quieter style: a full-width, scrolling strip of
616
- light tabs, the active one on the background — `TabsList` and `TabsTrigger`
617
- read it from `Tabs`.
615
+ Parts: `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`. `TabsList` and
616
+ `TabsTrigger` read the look from `Tabs`. A trigger's icon goes before its
617
+ label, as `@nxgt/material`'s `icon`: pass a component to the `icon` prop, or
618
+ fill the `#icon` slot. An icon written in the default slot still renders
619
+ beside the label.
620
+
621
+ **`underline`** (the default) lays the tabs over a border; the active one
622
+ takes the primary colour, and a bar under it slides to the tab clicked or
623
+ reached with the arrow keys. The list scrolls sideways when the tabs do not
624
+ fit (its scrollbar hidden), which suits a phone or a narrow popover.
625
+
626
+ **`segmented`** is a neutral pill track: the active tab sits on a raised
627
+ surface that slides between tabs, and its label turns to the foreground
628
+ colour and semibold — the state never rests on the surface alone. It suits a
629
+ few short choices, such as a view switch.
618
630
 
619
631
  ```vue
620
632
  <template>
621
- <Tabs default-value="ts" variant="line">
633
+ <Tabs default-value="week" variant="segmented">
622
634
  <TabsList>
623
- <TabsTrigger value="ts">TypeScript</TabsTrigger>
624
- <TabsTrigger value="bash">Bash</TabsTrigger>
635
+ <TabsTrigger value="day">Day</TabsTrigger>
636
+ <TabsTrigger value="week">Week</TabsTrigger>
637
+ <TabsTrigger value="month">Month</TabsTrigger>
625
638
  </TabsList>
626
- <TabsContent value="ts">const n = 1;</TabsContent>
627
- <TabsContent value="bash">bun install</TabsContent>
639
+ <TabsContent value="day">Today.</TabsContent>
640
+ <TabsContent value="week">This week.</TabsContent>
641
+ <TabsContent value="month">This month.</TabsContent>
628
642
  </Tabs>
629
643
  </template>
630
644
  ```
631
645
 
646
+ `orientation="vertical"` lays the tabs in a column, the indicator on their
647
+ left side; the arrow keys become Up and Down.
648
+
649
+ ```vue
650
+ <template>
651
+ <Tabs default-value="profile" orientation="vertical">
652
+ <TabsList>
653
+ <TabsTrigger value="profile">Profile</TabsTrigger>
654
+ <TabsTrigger value="security">Security</TabsTrigger>
655
+ </TabsList>
656
+ <TabsContent value="profile">Your profile.</TabsContent>
657
+ <TabsContent value="security">Your sessions.</TabsContent>
658
+ </Tabs>
659
+ </template>
660
+ ```
661
+
662
+ The sliding indicator is reka's `TabsIndicator`, which renders once mounted:
663
+ server HTML, and a page without JavaScript, show a static indicator under (or
664
+ behind) the active tab instead, and hydrating swaps one for the other without
665
+ a mismatch. The list then carries `data-indicator="ready"`. The focus ring is
666
+ an outline in the primary colour inside the tab, so a scrolling list never
667
+ clips it.
668
+
669
+ With `TabsList asChild`, only the static indicator is drawn.
670
+
671
+ `dir="rtl"` on `Tabs` (or reka's `ConfigProvider`) runs the tabs from the
672
+ right: the indicator follows them, and the Left arrow key moves to the next
673
+ tab. Under `prefers-reduced-motion: reduce` the indicator jumps to the active
674
+ tab instead of sliding.
675
+
676
+ **A tab with only an icon needs a name.** A screen reader announces a
677
+ `TabsTrigger` by its text; with an icon and no label it is only "tab". Give it
678
+ `aria-label`, and `title` for sighted users who hover it:
679
+
680
+ ```vue
681
+ <TabsTrigger value="recent" :icon="Clock" aria-label="Recent" title="Recent" />
682
+ ```
683
+
684
+ The emoji picker's group tabs are named this way, from
685
+ `material.emoji.groups.*`.
686
+
687
+ **The default look changed.** A `Tabs` with no `variant` used to render the
688
+ `bar` look (one muted pill with the active trigger raised); it now renders
689
+ `underline`. `variant="bar"` and `variant="line"` are deprecated aliases:
690
+ `bar` renders as `underline`, `line` as `segmented`. Both still type-check and
691
+ render; write `underline` or `segmented` instead.
692
+
632
693
  | Prop | Type | Default | Effect |
633
694
  | --- | --- | --- | --- |
634
- | `variant` (`Tabs`) | `'bar' \| 'line'` | `'bar'` | the look of the list and its triggers |
695
+ | `variant` (`Tabs`) | `'underline' \| 'segmented' \| 'bar' \| 'line'` | `'underline'` | the look; `bar` and `line` are deprecated aliases of `underline` and `segmented` |
635
696
  | `default-value` / `v-model` (`Tabs`) | `string \| number` | — | the selected tab, uncontrolled / controlled |
636
- | `orientation` (`Tabs`) | `'horizontal' \| 'vertical'` | `'horizontal'` | arrow-key direction |
697
+ | `orientation` (`Tabs`) | `'horizontal' \| 'vertical'` | `'horizontal'` | a row or a column, and the arrow keys' direction |
698
+ | `dir` (`Tabs`) | `'ltr' \| 'rtl'` | `'ltr'` | the reading direction: which way the tabs run and the arrow keys move |
637
699
  | `activation-mode` (`Tabs`) | `'automatic' \| 'manual'` | `'automatic'` | select on focus, or only on Enter / click |
638
700
  | `value` (`TabsTrigger`, `TabsContent`) | `string \| number` | — (required) | pairs a trigger with its panel |
701
+ | `icon` (`TabsTrigger`) | `Component` | — | shown before the label; or the `#icon` slot |
639
702
 
640
703
  **Emits:** `update:modelValue` on `Tabs` — `string | number`.
641
- **Storybook:** `Primitives/Tabs` (story `Line`) · **React:** `Tabs` (same parts)
704
+ **Storybook:** `Primitives/Tabs` (stories `Underline`, `Segmented`, `WithIcons`, `Scrolling`, `RightToLeft`, `Vertical`, `DeprecatedAliases`, `Hydrated`) · **React:** `Tabs` (same parts, the same variants)
642
705
 
643
706
  ---
644
707
 
@@ -272,6 +272,16 @@ const emojis: EmojiItem[] = [
272
272
  Parts: `EmojiPicker` (a button opening a popover), `EmojiList` (the searchable
273
273
  grid on its own, same `emojis` / `@select`), `expandEmojiShortcodes`.
274
274
 
275
+ The category tabs are icon-only. Each is named from
276
+ `material.emoji.groups.<group>` (for example `people_body`, `flags`): that name
277
+ is its `aria-label` and `title`, in the provider's language (see
278
+ [i18n](../guides/i18n.md)). An `EmojiItem` with no `group` falls under
279
+ `general`; a `group` is lowercased, with ` & ` and spaces turned into `_`
280
+ ("Food & Drink" becomes `food_drink`). A group the bundles do not name gets
281
+ its words, capitalised, as an English name (`hand_signs` becomes "Hand
282
+ signs"), and an icon of its own is not provided: it shows the generic grid
283
+ icon.
284
+
275
285
  | Prop | Type | Default | Effect |
276
286
  | --- | --- | --- | --- |
277
287
  | `emojis` | `EmojiItem[]` | — (required) | the set offered; `group` drives the category tabs, `shortcodes` the search |
@@ -405,10 +405,27 @@ and description and against the English ones, ignoring accents: in French,
405
405
 
406
406
  Some strings are still English in every language, among them the filter's
407
407
  validation messages, the camera's error messages, the editor's callout type
408
- names and default block content, and the names of the emoji picker's category
409
- tabs. File sizes (`2.0 KB`) and the filter chips' dates are not formatted for
408
+ names and default block content. File sizes (`2.0 KB`) and the filter chips' dates are not formatted for
410
409
  the provider's language.
411
410
 
411
+ The emoji picker's category tabs are icon-only; their name, read by screen
412
+ readers and shown as a tooltip, comes from `material.emoji.groups.<group>`.
413
+ Both languages ship `all`, `recent`, `general`, `people_body`, `components`,
414
+ `animals_nature`, `food_drink`, `travel_places`, `activities`, `objects`,
415
+ `symbols` and `flags`:
416
+
417
+ ```ts
418
+ // English / French values of the shipped keys
419
+ material.emoji.groups.people_body; // "People & body" / "Personnes et corps"
420
+ material.emoji.groups.food_drink; // "Food & drink" / "Nourriture et boissons"
421
+ material.emoji.groups.flags; // "Flags" / "Drapeaux"
422
+ ```
423
+
424
+ A group your `EmojiItem`s name that is not in this list (`hand_signs`) reads
425
+ as its words in English (`Hand signs`). As for every `material.*` key above,
426
+ the components read their own bundle: your own translator sees a key you add,
427
+ the tab does not.
428
+
412
429
  That override is visible only through **your** translator. The components
413
430
  translate through the package's own bundle, so defining
414
431
  `material.command.title` in your resources changes what
@@ -190,9 +190,23 @@ Storybook's `Guides/Theming/Contrast` measures all of this in both themes:
190
190
  the surfaces' text, the error text (`HelperText` and a field's message on
191
191
  `--background` and `--card`), the `Alert`s and the `Timeline`, `Button` and
192
192
  `IconButton` in every variant and colour, and a `Badge` link in every colour,
193
- at rest, hovered, focused and pressed, and the tracks; `Data display/ListTile`
194
- measures the selected tile. Run them against your own tokens after an
195
- override.
193
+ at rest, hovered, focused and pressed, the tracks and the tabs;
194
+ `Data display/ListTile` measures the selected tile. Run them against your own
195
+ tokens after an override.
196
+
197
+ **Tabs.** An `underline` tab's active label is `--primary` in the light
198
+ theme (7.3:1 on `--background` and `--card`, 6.7:1 on `--muted`) and
199
+ `--primary` mixed with 60 % white in the dark one (10.0:1, 9.0:1 and 7.4:1);
200
+ its bar and every tab's focus outline are `--primary` in the light theme and
201
+ `--track-fill` in the dark one (at least 6.7:1 and 9.0:1 on those surfaces).
202
+ An inactive label is `--muted-foreground`, and turns to `--foreground` when
203
+ hovered. A `segmented` tab's active label is `--foreground` on the raised
204
+ surface (19.9:1 light, 17.0:1 dark); that surface is only 1.1:1 to 1.2:1 on
205
+ the track, so it has a `--muted-foreground` border (5.0:1 light, 4.7:1 dark
206
+ on the track), and the label also changes colour and weight. Storybook's
207
+ `Guides/Theming/Contrast` story `Tabs` measures each label at rest, hovered
208
+ and focused, the outline, the bar and the border, on the three surfaces, in
209
+ both themes.
196
210
 
197
211
  `--muted-foreground` is 5.5:1 on `--background` and `--card`, 5.1:1 on the
198
212
  "paper" tint (`bg-background-paper`, 5 % primary) and 5.0:1 on `--muted` in
Binary file
package/docs/roadmap.md CHANGED
@@ -95,6 +95,18 @@ Nothing in progress right now; see Next.
95
95
 
96
96
  ## Shipped
97
97
 
98
+ - **Toast actions that work everywhere** — `toasts.success(message, { action })`
99
+ and every other preset take sonner's own call shape and keep their action,
100
+ and a dialog a toast's action opens keeps the focus, then gives it back to
101
+ where it was before the toast. 0.29.0.
102
+
103
+ - **Tabs in two looks** — `underline`, the new default, whose bar slides to
104
+ the active tab, and a neutral `segmented` pill; a list that scrolls
105
+ sideways on a phone instead of widening the page, right-to-left, a visible
106
+ focus ring, labels that read 4.5:1 in both themes, and an indicator that
107
+ stops sliding under `prefers-reduced-motion`. `bar` and `line` still work,
108
+ as deprecated aliases of `underline` and `segmented`. 0.28.0.
109
+
98
110
  - **Accessible by default** — error text in its own `--error-text` colour that
99
111
  reads 4.5:1 in both themes, the empty part of sliders and meters in a
100
112
  `--track` colour that reads 3:1, buttons, badges and status colours that
@@ -127,7 +139,8 @@ Nothing in progress right now; see Next.
127
139
  form, date fields that disable chosen days, `SliderField` ticks and
128
140
  leading/trailing content, a presence status on `Avatar`, a logo slot on the
129
141
  QR code, slots on `AccordionCard`, an option slot on `SelectField`, Monaco's
130
- own options on `CodeEditor` and a line variant for `Tabs`. 0.25.0.
142
+ own options on `CodeEditor` and a line variant for `Tabs` (`line` is now a
143
+ deprecated alias of `segmented`). 0.25.0.
131
144
  - **DateRangeField presets and time** — one-click ranges such as Today, Last 7
132
145
  days and This month, or your own, beside the calendar (`presets`), and a time
133
146
  of day on each end of the range (`with-time`), and `min-date` / `max-date`.