@nxgt/material 3.55.0 → 3.57.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 (119) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +8 -4
  3. package/dist/{async-slice-yhXT9cml.js → async-slice-B3RMgJUn.js} +2 -2
  4. package/dist/{button-CD7f4KFL.js → button-hnUZpzYu.js} +1 -1
  5. package/dist/charts.js +3 -3
  6. package/dist/{chip-Cl7Wm8Pn.js → chip-kplPa8ZV.js} +2 -2
  7. package/dist/{code-editor-tOhXy6Qo.js → code-editor-Ba61Cd-g.js} +6 -6
  8. package/dist/code-editor.js +5 -5
  9. package/dist/{code-language-items-BxlY6onk.js → code-language-items-CsDC4Ky4.js} +2 -2
  10. package/dist/components/components.css +4 -0
  11. package/dist/components/ui/display/secret-card/secret-card.css +65 -0
  12. package/dist/components/ui/display/vote-tally/vote-tally.css +94 -0
  13. package/dist/{components-B58ZWZYR.js → components-BgckuX2C.js} +16870 -16252
  14. package/dist/components.js +26 -26
  15. package/dist/{dialog-BzfHcRo6.js → dialog-DS8O4Kkb.js} +4 -4
  16. package/dist/dnd.js +1 -1
  17. package/dist/{dropdown-menu-DUZF4xAJ.js → dropdown-menu-CvVQJpsn.js} +1 -1
  18. package/dist/editor.js +9 -9
  19. package/dist/{empty-state-D9o2id1p.js → empty-state-4gmbCkcw.js} +2 -2
  20. package/dist/{escape-owner-BYs2_7T5.js → escape-owner-BJvicDmr.js} +1 -1
  21. package/dist/{field-blur-r1tHwqdF.js → field-blur-Cnjk0o9A.js} +1 -1
  22. package/dist/{field-blur-context-ZsK8OTNg.js → field-blur-context-CYlx9KnB.js} +1 -1
  23. package/dist/hooks.js +28 -28
  24. package/dist/{hover-card-NfF8taLj.js → hover-card-DW4XnExA.js} +2 -2
  25. package/dist/{i18n-provider-TQAEPlJ5.js → i18n-provider-DBr59j1p.js} +1 -1
  26. package/dist/i18n.js +5 -5
  27. package/dist/{icon-badge-BlaY_a-P.js → icon-badge-4FMy66AC.js} +3 -3
  28. package/dist/{icon-button-B8tdlP6R.js → icon-button-QF7smCoV.js} +1 -1
  29. package/dist/lib/components/ui/actions/link/link.d.ts +8 -0
  30. package/dist/lib/components/ui/collections/data-table/partials/data-table-page-control.d.ts +1 -1
  31. package/dist/lib/components/ui/collections/data-table/partials/data-table-row.d.ts +1 -1
  32. package/dist/lib/components/ui/collections/data-table/partials/layouts/data-table-card.d.ts +1 -1
  33. package/dist/lib/components/ui/collections/server-table/partials/server-table-past-end.d.ts +1 -1
  34. package/dist/lib/components/ui/display/index.d.ts +2 -0
  35. package/dist/lib/components/ui/display/list-tile/partials/list-tile-line.d.ts +1 -1
  36. package/dist/lib/components/ui/display/period-cards/period-cards.d.ts +1 -1
  37. package/dist/lib/components/ui/display/secret-card/index.d.ts +2 -0
  38. package/dist/lib/components/ui/display/secret-card/secret-card.d.ts +11 -0
  39. package/dist/lib/components/ui/display/secret-card/types.d.ts +49 -0
  40. package/dist/lib/components/ui/display/secret-card/use-secret-card.d.ts +20 -0
  41. package/dist/lib/components/ui/display/vote-tally/announcer.d.ts +11 -0
  42. package/dist/lib/components/ui/display/vote-tally/index.d.ts +2 -0
  43. package/dist/lib/components/ui/display/vote-tally/partials/tally-bar.d.ts +8 -0
  44. package/dist/lib/components/ui/display/vote-tally/partials/tally-row.d.ts +9 -0
  45. package/dist/lib/components/ui/display/vote-tally/partials/tally-voters.d.ts +9 -0
  46. package/dist/lib/components/ui/display/vote-tally/types.d.ts +73 -0
  47. package/dist/lib/components/ui/display/vote-tally/use-announcement.d.ts +7 -0
  48. package/dist/lib/components/ui/display/vote-tally/use-tally-summary.d.ts +3 -0
  49. package/dist/lib/components/ui/display/vote-tally/vote-tally.d.ts +10 -0
  50. package/dist/lib/components/ui/display/vote-tally/vote-tally.utils.d.ts +18 -0
  51. package/dist/lib/components/ui/feedback/countdown-timer/countdown-ring.d.ts +7 -0
  52. package/dist/lib/components/ui/feedback/countdown-timer/countdown-timer.d.ts +7 -1
  53. package/dist/lib/components/ui/fields/contacts/placeholders.d.ts +1 -1
  54. package/dist/lib/components/ui/layout/account-menu/account-menu.d.ts +1 -1
  55. package/dist/lib/components/ui/layout/app-shell/app-sidebar-icon-item.d.ts +1 -1
  56. package/dist/lib/components/ui/layout/app-shell/app-sidebar-label-item.d.ts +1 -1
  57. package/dist/lib/components/ui/layout/app-shell/app-sidebar.d.ts +1 -1
  58. package/dist/lib/components/ui/layout/pager/pager.d.ts +1 -1
  59. package/dist/lib/components/ui/layout/see-also/see-also.d.ts +1 -1
  60. package/dist/lib/components/ui/layout/toc/toc.d.ts +1 -1
  61. package/dist/lib/components/ui/primitives/index.d.ts +1 -0
  62. package/dist/lib/components/ui/primitives/link/index.d.ts +2 -0
  63. package/dist/lib/components/ui/primitives/link/link-component-provider.d.ts +26 -0
  64. package/dist/lib/components/ui/widgets/education/index.d.ts +1 -0
  65. package/dist/lib/components/ui/widgets/education/navigator/index.d.ts +2 -0
  66. package/dist/lib/components/ui/widgets/education/navigator/partials/navigator-item.d.ts +10 -0
  67. package/dist/lib/components/ui/widgets/education/navigator/question-navigator.d.ts +27 -0
  68. package/dist/lib/components/ui/widgets/education/questions/index.d.ts +1 -1
  69. package/dist/lib/components/ui/widgets/education/questions/partials/question-media.d.ts +5 -0
  70. package/dist/lib/components/ui/widgets/education/questions/partials/question-verdict.d.ts +12 -0
  71. package/dist/lib/components/ui/widgets/education/questions/question-options.d.ts +2 -2
  72. package/dist/lib/components/ui/widgets/education/questions/question-text.d.ts +1 -1
  73. package/dist/lib/components/ui/widgets/education/questions/question.d.ts +1 -1
  74. package/dist/lib/components/ui/widgets/education/questions/types.d.ts +33 -1
  75. package/dist/lib/hooks/index.d.ts +1 -0
  76. package/dist/lib/hooks/use-countdown.d.ts +18 -0
  77. package/dist/lib/i18n/resources/en/en.d.ts +32 -0
  78. package/dist/lib/i18n/resources/en/material.json.d.ts +33 -1
  79. package/dist/lib/i18n/resources/fr/fr.d.ts +32 -0
  80. package/dist/lib/i18n/resources/fr/material.json.d.ts +33 -1
  81. package/dist/lib/i18n/resources/resources.d.ts +64 -0
  82. package/dist/lib/i18n/translator.d.ts +1 -1
  83. package/dist/lib.js +1 -1
  84. package/dist/main.js +43 -43
  85. package/dist/map.js +8 -8
  86. package/dist/material.css +1 -1
  87. package/dist/pdf-reader.js +1 -1
  88. package/dist/print.js +2 -2
  89. package/dist/{public-tWgM204K.js → public-DVZJVdxD.js} +9 -9
  90. package/dist/{rich-code-block-0OcsqURY.js → rich-code-block-DL5-9JIi.js} +5 -5
  91. package/dist/{sortable-provider-DzFAlPU_.js → sortable-provider-DrpBwx75.js} +2 -2
  92. package/dist/{spinner-GKZcQmrR.js → spinner-D4XGdse6.js} +1 -1
  93. package/dist/{stored-language-B39mABN7.js → stored-language-C89mti-z.js} +66 -2
  94. package/dist/theme-picker.js +2 -2
  95. package/dist/{theme-toggle-C8idk_jQ.js → theme-toggle-BFhqLmgx.js} +1 -1
  96. package/dist/{toolbar-items-BrJI-BCs.js → toolbar-items-SZ5pyAFz.js} +4 -4
  97. package/dist/{translate-material-hUA-UAP6.js → translate-material-BkqnImND.js} +2 -2
  98. package/dist/{translator-1ql2sxrx.js → translator-B5fId5Rw.js} +1 -1
  99. package/dist/{use-chat-composer-DQpb8ZXV.js → use-chat-composer-BG5B4DGB.js} +16 -16
  100. package/dist/{use-heading-spy-vlPOI1Aj.js → use-heading-spy-51GQw8tH.js} +32 -5
  101. package/dist/{use-material-translation-B1p5y3s9.js → use-material-translation-BQOr4Oks.js} +2 -2
  102. package/docs/components/README.md +1 -1
  103. package/docs/components/buttons-and-actions.md +1 -0
  104. package/docs/components/data-display.md +147 -2
  105. package/docs/components/data-table-filter-and-lists.md +3 -3
  106. package/docs/components/feedback-and-status.md +12 -5
  107. package/docs/components/navigation-and-layout.md +4 -4
  108. package/docs/components/primitives.md +62 -0
  109. package/docs/components/rich-widgets.md +51 -0
  110. package/docs/components/selection-and-choice.md +1 -1
  111. package/docs/guides/app-shell.md +2 -2
  112. package/docs/guides/helpers.md +40 -0
  113. package/docs/previews/data-display-secretcard.webp +0 -0
  114. package/docs/previews/data-display-votetally.webp +0 -0
  115. package/lib/components/components.css +4 -0
  116. package/lib/components/ui/display/secret-card/secret-card.css +65 -0
  117. package/lib/components/ui/display/vote-tally/vote-tally.css +94 -0
  118. package/package.json +1 -1
  119. package/dist/lib/components/ui/feedback/countdown-timer/use-countdown.d.ts +0 -6
@@ -1,3 +1,3 @@
1
- import { t as e } from "./public-tWgM204K.js";
1
+ import { t as e } from "./public-DVZJVdxD.js";
2
2
  import { configurePdfReader as t } from "@nxgt/material-core/pdf-reader";
3
3
  export { e as PdfReader, t as configurePdfReader };
package/dist/print.js CHANGED
@@ -1,5 +1,5 @@
1
- import { r as e } from "./i18n-provider-TQAEPlJ5.js";
2
- import { t } from "./translate-material-hUA-UAP6.js";
1
+ import { r as e } from "./i18n-provider-DBr59j1p.js";
2
+ import { t } from "./translate-material-BkqnImND.js";
3
3
  import { Children as n, createContext as r, isValidElement as i, useCallback as a, useContext as o, useEffect as s, useMemo as c, useState as l } from "react";
4
4
  import { jsx as u, jsxs as d } from "react/jsx-runtime";
5
5
  import { resolvePdfFileName as f } from "@nxgt/material-core/pdf-file-name";
@@ -3,21 +3,21 @@ import { n as t } from "./ui.utils-CAnKTCw5.js";
3
3
  import { t as n } from "./utils-BLfntITJ.js";
4
4
  import { o as r } from "./dist-DVRQkaTK.js";
5
5
  import { c as i, i as a, l as o, n as s, o as c, s as l } from "./dist-lmHmz_bu.js";
6
- import { n as u, s as d } from "./spinner-GKZcQmrR.js";
6
+ import { n as u, s as d } from "./spinner-D4XGdse6.js";
7
7
  import { i as f } from "./dist-C4wtXCjf.js";
8
8
  import { r as p, t as m } from "./in-dialog-Vwkyp-M5.js";
9
- import { C as h, D as g, E as _, O as v, S as y, T as b, _ as x, f as S, g as ee, k as te, r as ne, t as C, u as re, w } from "./dialog-BzfHcRo6.js";
10
- import { c as T, d as ie, f as E, i as ae, l as oe, n as se, o as D, t as ce, u as O } from "./dropdown-menu-DUZF4xAJ.js";
11
- import { S as le, a as k, d as A, g as ue, h as de, i as fe, m as pe, n as j, r as me, t as M, u as he, w as ge } from "./toolbar-items-BrJI-BCs.js";
12
- import { t as N } from "./use-material-translation-B1p5y3s9.js";
13
- import { t as _e } from "./button-CD7f4KFL.js";
14
- import { n as ve, t as ye } from "./icon-button-B8tdlP6R.js";
9
+ import { C as h, D as g, E as _, O as v, S as y, T as b, _ as x, f as S, g as ee, k as te, r as ne, t as C, u as re, w } from "./dialog-DS8O4Kkb.js";
10
+ import { c as T, d as ie, f as E, i as ae, l as oe, n as se, o as D, t as ce, u as O } from "./dropdown-menu-CvVQJpsn.js";
11
+ import { S as le, a as k, d as A, g as ue, h as de, i as fe, m as pe, n as j, r as me, t as M, u as he, w as ge } from "./toolbar-items-SZ5pyAFz.js";
12
+ import { t as N } from "./use-material-translation-BQOr4Oks.js";
13
+ import { t as _e } from "./button-hnUZpzYu.js";
14
+ import { n as ve, t as ye } from "./icon-button-QF7smCoV.js";
15
15
  import { t as be } from "./useEvent-2fKq-6w8.js";
16
16
  import { n as xe, t as Se } from "./toggle-group-BkH23Jk4.js";
17
- import { o as Ce } from "./escape-owner-BYs2_7T5.js";
17
+ import { o as Ce } from "./escape-owner-BJvicDmr.js";
18
18
  import { n as we } from "./use-slice-reducer-BGkeGtML.js";
19
19
  import { r as Te } from "./use-csp-nonce-ZKs9k-ov.js";
20
- import { n as Ee, t as De } from "./empty-state-D9o2id1p.js";
20
+ import { n as Ee, t as De } from "./empty-state-4gmbCkcw.js";
21
21
  import { t as Oe } from "./client-only.utils-3nHNzCki.js";
22
22
  import { t as ke } from "./use-scroll-away-PQRFZkT2.js";
23
23
  import * as P from "react";
@@ -1,7 +1,7 @@
1
- import { r as e } from "./spinner-GKZcQmrR.js";
2
- import { c as t, n, t as r } from "./dropdown-menu-DUZF4xAJ.js";
3
- import { t as i } from "./use-material-translation-B1p5y3s9.js";
4
- import { a, i as o, n as s, o as c, t as l } from "./code-language-items-BxlY6onk.js";
1
+ import { r as e } from "./spinner-D4XGdse6.js";
2
+ import { c as t, n, t as r } from "./dropdown-menu-CvVQJpsn.js";
3
+ import { t as i } from "./use-material-translation-BQOr4Oks.js";
4
+ import { a, i as o, n as s, o as c, t as l } from "./code-language-items-CsDC4Ky4.js";
5
5
  import { i as u, n as d, r as f, s as p } from "./dist-C3GR7kJd.js";
6
6
  import { t as m } from "./code-block-spec-CGLKRhwD.js";
7
7
  import { Suspense as h, lazy as g, useEffect as _, useRef as v, useState as y } from "react";
@@ -143,7 +143,7 @@ function M({ node: e, editor: t, updateAttributes: n, editable: r, wrap: a, onWr
143
143
  }
144
144
  //#endregion
145
145
  //#region lib/components/ui/widgets/editor/extensions/code/code-block-open.tsx
146
- var N = g(() => import("./code-editor-tOhXy6Qo.js").then((e) => e.n).then((e) => ({ default: e.CodeEditor })));
146
+ var N = g(() => import("./code-editor-Ba61Cd-g.js").then((e) => e.n).then((e) => ({ default: e.CodeEditor })));
147
147
  function P() {
148
148
  let { t: e } = i();
149
149
  return /* @__PURE__ */ b("div", {
@@ -1,7 +1,7 @@
1
1
  import { t as e } from "./utils-BLfntITJ.js";
2
2
  import { t } from "./dist-DVRQkaTK.js";
3
- import { t as n } from "./use-material-translation-B1p5y3s9.js";
4
- import { t as r } from "./icon-button-B8tdlP6R.js";
3
+ import { t as n } from "./use-material-translation-BQOr4Oks.js";
4
+ import { t as r } from "./icon-button-QF7smCoV.js";
5
5
  import { t as i } from "./compose-refs-DZ0iIVF-.js";
6
6
  import a, { cloneElement as o, createContext as s, forwardRef as c, memo as l, useCallback as u, useContext as d, useEffect as f, useId as p, useLayoutEffect as m, useMemo as h, useReducer as g, useRef as _, useState as v } from "react";
7
7
  import { jsx as y } from "react/jsx-runtime";
@@ -3,7 +3,7 @@ import { n as t, t as n } from "./utils-BLfntITJ.js";
3
3
  import { i as r, o as i } from "./dist-DVRQkaTK.js";
4
4
  import { a, c as o, i as s, l as c, n as l, o as u, r as d, s as f, t as p, u as m } from "./dist-lmHmz_bu.js";
5
5
  import { a as h, c as g, d as _, i as v, l as y, o as b, r as x, t as S, u as C } from "./floating-ui.dom-KkZSW8_E.js";
6
- import { t as w } from "./use-material-translation-B1p5y3s9.js";
6
+ import { t as w } from "./use-material-translation-BQOr4Oks.js";
7
7
  import * as T from "react";
8
8
  import { useLayoutEffect as E, useRef as D, useSyncExternalStore as O } from "react";
9
9
  import { jsx as k, jsxs as A } from "react/jsx-runtime";
@@ -321,6 +321,10 @@ var t = {
321
321
  correct: "Correct!",
322
322
  incorrect: "Not quite",
323
323
  unanswered: "Not answered",
324
+ pending: "Awaiting grading",
325
+ "points-of": "{awarded} / {possible} points",
326
+ audio: "Audio for this question",
327
+ document: "Document for this question",
324
328
  "correct-answer": "Correct answer: {answer}",
325
329
  "your-answer": "Your answer: {answer}",
326
330
  streak: "{count} in a row",
@@ -340,6 +344,14 @@ var t = {
340
344
  "retry-wrong": "{count, plural, one {Retry # question} other {Retry # questions}}",
341
345
  retrying: "Retry round: {count, plural, one {# question} other {# questions}}"
342
346
  },
347
+ "question-navigator": {
348
+ label: "Questions",
349
+ item: "Question {number}",
350
+ answered: "answered",
351
+ unanswered: "not answered",
352
+ flagged: "flagged",
353
+ current: "current"
354
+ },
343
355
  "step-dots": {
344
356
  label: "Progress",
345
357
  step: "Step {current} of {total}",
@@ -352,7 +364,8 @@ var t = {
352
364
  "countdown-timer": {
353
365
  label: "Time remaining",
354
366
  left: "{time} left",
355
- expired: "Time is up"
367
+ expired: "Time is up",
368
+ warning: "Little time left"
356
369
  },
357
370
  "option-tile": {
358
371
  correct: "correct answer",
@@ -1334,7 +1347,26 @@ var t = {
1334
1347
  busy: "Busy",
1335
1348
  active: "Active"
1336
1349
  },
1350
+ "vote-tally": {
1351
+ votes: "{count, plural, one {# vote} other {# votes}}",
1352
+ share: "{percent}%",
1353
+ leader: "Leader",
1354
+ tie: "Tie",
1355
+ selected: "Your vote",
1356
+ empty: "No votes yet",
1357
+ more: "+{count} more",
1358
+ "voters-of": "Voters for {label}",
1359
+ abstained: "{count, plural, one {# abstention} other {# abstentions}}",
1360
+ cast: "{count, plural, one {# vote cast} other {# votes cast}}",
1361
+ leads: "{names} leads with {votes}",
1362
+ "tied-between": "Tie between {names}, {votes} each"
1363
+ },
1337
1364
  "seat-ring": { empty: "Empty seat" },
1365
+ "secret-card": {
1366
+ reveal: "Show secret",
1367
+ revealed: "Secret revealed",
1368
+ hidden: "Secret hidden"
1369
+ },
1338
1370
  "country-field": { placeholder: "Country" },
1339
1371
  "duration-field": {
1340
1372
  hours: "h",
@@ -3162,6 +3194,10 @@ var t = {
3162
3194
  correct: "Bonne réponse !",
3163
3195
  incorrect: "Pas tout à fait",
3164
3196
  unanswered: "Sans réponse",
3197
+ pending: "En attente de correction",
3198
+ "points-of": "{awarded} / {possible} points",
3199
+ audio: "Audio de cette question",
3200
+ document: "Document de cette question",
3165
3201
  "correct-answer": "Bonne réponse : {answer}",
3166
3202
  "your-answer": "Votre réponse : {answer}",
3167
3203
  streak: "{count} d’affilée",
@@ -3181,6 +3217,14 @@ var t = {
3181
3217
  "retry-wrong": "{count, plural, one {Refaire # question} other {Refaire # questions}}",
3182
3218
  retrying: "Nouvel essai : {count, plural, one {# question} other {# questions}}"
3183
3219
  },
3220
+ "question-navigator": {
3221
+ label: "Questions",
3222
+ item: "Question {number}",
3223
+ answered: "répondue",
3224
+ unanswered: "sans réponse",
3225
+ flagged: "marquée",
3226
+ current: "en cours"
3227
+ },
3184
3228
  "step-dots": {
3185
3229
  label: "Progression",
3186
3230
  step: "Étape {current} sur {total}",
@@ -3193,7 +3237,8 @@ var t = {
3193
3237
  "countdown-timer": {
3194
3238
  label: "Temps restant",
3195
3239
  left: "Plus que {time}",
3196
- expired: "Temps écoulé"
3240
+ expired: "Temps écoulé",
3241
+ warning: "Il reste peu de temps"
3197
3242
  },
3198
3243
  "option-tile": {
3199
3244
  correct: "bonne réponse",
@@ -4175,7 +4220,26 @@ var t = {
4175
4220
  busy: "Occupé",
4176
4221
  active: "Actif"
4177
4222
  },
4223
+ "vote-tally": {
4224
+ votes: "{count, plural, one {# vote} other {# votes}}",
4225
+ share: "{percent} %",
4226
+ leader: "En tête",
4227
+ tie: "Égalité",
4228
+ selected: "Votre vote",
4229
+ empty: "Aucun vote pour l'instant",
4230
+ more: "+{count} de plus",
4231
+ "voters-of": "Votants pour {label}",
4232
+ abstained: "{count, plural, one {# abstention} other {# abstentions}}",
4233
+ cast: "{count, plural, one {# vote exprimé} other {# votes exprimés}}",
4234
+ leads: "{names} est en tête avec {votes}",
4235
+ "tied-between": "Égalité entre {names}, {votes} chacun"
4236
+ },
4178
4237
  "seat-ring": { empty: "Place libre" },
4238
+ "secret-card": {
4239
+ reveal: "Afficher le secret",
4240
+ revealed: "Secret révélé",
4241
+ hidden: "Secret masqué"
4242
+ },
4179
4243
  "country-field": { placeholder: "Pays" },
4180
4244
  "duration-field": {
4181
4245
  hours: "h",
@@ -1,10 +1,10 @@
1
1
  import { t as e } from "./utils-BLfntITJ.js";
2
2
  import { a as t, i as n, n as r, o as i, r as a, t as o } from "./bubble-input-B7BVd9fX.js";
3
3
  import { a as s, r as c } from "./icon-sprites-CaYXzOL4.js";
4
- import { t as l } from "./use-material-translation-B1p5y3s9.js";
4
+ import { t as l } from "./use-material-translation-BQOr4Oks.js";
5
5
  import { t as u } from "./use-theme-BCLireTd.js";
6
6
  import { a as d, r as f } from "./theme-context-Cofk6vgs.js";
7
- import { t as p } from "./theme-toggle-C8idk_jQ.js";
7
+ import { t as p } from "./theme-toggle-BFhqLmgx.js";
8
8
  import { n as m, t as h } from "./nxgt-theme-ivgNca3z.js";
9
9
  import { useContext as g } from "react";
10
10
  import { jsx as _, jsxs as v } from "react/jsx-runtime";
@@ -1,4 +1,4 @@
1
- import { t as e } from "./use-material-translation-B1p5y3s9.js";
1
+ import { t as e } from "./use-material-translation-BQOr4Oks.js";
2
2
  import { i as t } from "./theme-store-BdPp43cV.js";
3
3
  import { n, t as r } from "./use-theme-BCLireTd.js";
4
4
  import { r as i } from "./theme-context-Cofk6vgs.js";
@@ -2,13 +2,13 @@ import { t as e } from "./utils-BLfntITJ.js";
2
2
  import { t } from "./warn-once-C_e47JM8.js";
3
3
  import { o as n, r, t as i } from "./dist-DVRQkaTK.js";
4
4
  import { a, c as o, i as s, l as c, r as l, s as u } from "./dist-lmHmz_bu.js";
5
- import { C as d, S as f, d as p, f as m, m as h, p as g, r as _, u as v } from "./spinner-GKZcQmrR.js";
5
+ import { C as d, S as f, d as p, f as m, m as h, p as g, r as _, u as v } from "./spinner-D4XGdse6.js";
6
6
  import { i as y, n as b, r as x, t as S } from "./dist-C4wtXCjf.js";
7
7
  import { a as C, c as w, l as T, o as E, r as ee, s as te, t as ne } from "./in-dialog-Vwkyp-M5.js";
8
- import { A as re, M as ie, N as ae, b as oe, d as se, f as ce, i as le, j as ue, l as de, n as fe, o as pe, r as me, t as he, u as ge, y as _e } from "./dropdown-menu-DUZF4xAJ.js";
8
+ import { A as re, M as ie, N as ae, b as oe, d as se, f as ce, i as le, j as ue, l as de, n as fe, o as pe, r as me, t as he, u as ge, y as _e } from "./dropdown-menu-CvVQJpsn.js";
9
9
  import { a as ve, n as ye, r as be, t as xe } from "./dist-B7RjY7SL.js";
10
- import { t as Se } from "./use-material-translation-B1p5y3s9.js";
11
- import { o as Ce } from "./escape-owner-BYs2_7T5.js";
10
+ import { t as Se } from "./use-material-translation-BQOr4Oks.js";
11
+ import { o as Ce } from "./escape-owner-BJvicDmr.js";
12
12
  import * as D from "react";
13
13
  import { createContext as O, useCallback as we, useContext as k, useEffect as A, useId as Te, useLayoutEffect as j, useMemo as M, useReducer as Ee, useRef as N, useState as P } from "react";
14
14
  import { Fragment as F, jsx as I, jsxs as L } from "react/jsx-runtime";
@@ -1,5 +1,5 @@
1
- import "./stored-language-B39mABN7.js";
2
- import { a as e, r as t, t as n } from "./translator-1ql2sxrx.js";
1
+ import "./stored-language-C89mti-z.js";
2
+ import { a as e, r as t, t as n } from "./translator-B5fId5Rw.js";
3
3
  //#region lib/i18n/translate-material.ts
4
4
  var r = /* @__PURE__ */ new WeakMap();
5
5
  function i(e) {
@@ -1,5 +1,5 @@
1
1
  import { t as e } from "./is-production-GLw59WU8.js";
2
- import { a as t, i as n, r, t as i } from "./stored-language-B39mABN7.js";
2
+ import { a as t, i as n, r, t as i } from "./stored-language-C89mti-z.js";
3
3
  import { t as a } from "./get-CTzJAwfc.js";
4
4
  //#region ../../node_modules/@formatjs/fast-memoize/index.js
5
5
  function o(e, t) {
@@ -5,39 +5,39 @@ import { t as r } from "./warn-once-C_e47JM8.js";
5
5
  import { n as i } from "./use-shape-morph-DXdy2X5g.js";
6
6
  import { n as a, o, t as s } from "./dist-DVRQkaTK.js";
7
7
  import { a as c, c as l, i as u, l as d, n as f, o as p, r as m, s as h, t as g } from "./dist-lmHmz_bu.js";
8
- import { _, a as v, b as y, c as b, g as x, h as S, i as C, n as w, o as T, r as E, s as D, t as O, v as k, x as A, y as ee } from "./spinner-GKZcQmrR.js";
8
+ import { _, a as v, b as y, c as b, g as x, h as S, i as C, n as w, o as T, r as E, s as D, t as O, v as k, x as A, y as ee } from "./spinner-D4XGdse6.js";
9
9
  import { a as j, i as M, n as N, r as te, t as ne } from "./dist-C4wtXCjf.js";
10
10
  import { i as P, n as re, t as ie } from "./in-dialog-Vwkyp-M5.js";
11
- import { A as F, C as ae, D as oe, E as se, O as I, S as ce, T as le, _ as ue, b as de, f as fe, g as pe, k as me, l as he, m as ge, n as _e, o as ve, r as ye, s as be, t as xe, u as Se, v as Ce, w as we, x as Te, y as Ee } from "./dialog-BzfHcRo6.js";
11
+ import { A as F, C as ae, D as oe, E as se, O as I, S as ce, T as le, _ as ue, b as de, f as fe, g as pe, k as me, l as he, m as ge, n as _e, o as ve, r as ye, s as be, t as xe, u as Se, v as Ce, w as we, x as Te, y as Ee } from "./dialog-DS8O4Kkb.js";
12
12
  import { A as De, C as Oe, D as ke, E as Ae, O as je, S as Me, T as Ne, _ as Pe, a as Fe, b as Ie, d as Le, f as Re, g as ze, h as Be, i as Ve, k as He, l as Ue, m as We, n as Ge, o as Ke, p as qe, r as Je, s as Ye, t as Xe, u as Ze, v as Qe, w as $e, x as et, y as tt } from "./floating-ui.dom-KkZSW8_E.js";
13
- import { D as nt, E as rt, N as it, O as at, c as L, d as ot, f as st, i as ct, k as lt, m as ut, n as dt, o as ft, p as pt, t as mt, u as ht } from "./dropdown-menu-DUZF4xAJ.js";
14
- import { a as gt, i as _t, n as vt } from "./field-blur-context-ZsK8OTNg.js";
15
- import { C as yt, S as bt, a as xt, c as St, d as Ct, l as wt, n as Tt, o as Et, s as Dt, u as Ot } from "./toolbar-items-BrJI-BCs.js";
13
+ import { D as nt, E as rt, N as it, O as at, c as L, d as ot, f as st, i as ct, k as lt, m as ut, n as dt, o as ft, p as pt, t as mt, u as ht } from "./dropdown-menu-CvVQJpsn.js";
14
+ import { a as gt, i as _t, n as vt } from "./field-blur-context-CYlx9KnB.js";
15
+ import { C as yt, S as bt, a as xt, c as St, d as Ct, l as wt, n as Tt, o as Et, s as Dt, u as Ot } from "./toolbar-items-SZ5pyAFz.js";
16
16
  import { a as kt, i as At, n as jt, o as Mt, t as Nt } from "./bubble-input-B7BVd9fX.js";
17
17
  import { i as Pt } from "./dist-B7RjY7SL.js";
18
18
  import { o as Ft, s as It, t as Lt } from "./use-click-lock-BZYtTmVt.js";
19
19
  import { a as Rt, r as zt } from "./icon-sprites-CaYXzOL4.js";
20
- import "./stored-language-B39mABN7.js";
21
- import { a as Bt } from "./translator-1ql2sxrx.js";
20
+ import "./stored-language-C89mti-z.js";
21
+ import { a as Bt } from "./translator-B5fId5Rw.js";
22
22
  import { h as Vt } from "./get-CTzJAwfc.js";
23
23
  import { t as Ht } from "./isEmpty-F_g3jLf8.js";
24
- import { t as R } from "./use-material-translation-B1p5y3s9.js";
25
- import { i as Ut, r as Wt, t as Gt } from "./button-CD7f4KFL.js";
26
- import { t as z } from "./icon-button-B8tdlP6R.js";
24
+ import { t as R } from "./use-material-translation-BQOr4Oks.js";
25
+ import { i as Ut, r as Wt, t as Gt } from "./button-hnUZpzYu.js";
26
+ import { t as z } from "./icon-button-QF7smCoV.js";
27
27
  import { t as Kt } from "./use-ssr-media-query-C6M5m8sf.js";
28
28
  import { n as qt, t as Jt } from "./util-4heV2LPI.js";
29
29
  import { t as Yt } from "./useEvent-2fKq-6w8.js";
30
- import { f as Xt } from "./field-blur-r1tHwqdF.js";
30
+ import { f as Xt } from "./field-blur-Cnjk0o9A.js";
31
31
  import { c as Zt, i as Qt, l as $t, o as en, r as tn, s as nn, t as rn } from "./dist-C3GR7kJd.js";
32
32
  import { i as an } from "./toggle-group-BkH23Jk4.js";
33
- import { a as on, i as sn, o as cn } from "./escape-owner-BYs2_7T5.js";
33
+ import { a as on, i as sn, o as cn } from "./escape-owner-BJvicDmr.js";
34
34
  import { n as ln } from "./use-slice-reducer-BGkeGtML.js";
35
35
  import { t as un } from "./browser-only-DQ94lWF8.js";
36
36
  import { t as dn } from "./compose-refs-DZ0iIVF-.js";
37
- import { n as fn, r as pn, t as mn } from "./chip-Cl7Wm8Pn.js";
38
- import { n as hn, t as gn } from "./icon-badge-BlaY_a-P.js";
37
+ import { n as fn, r as pn, t as mn } from "./chip-kplPa8ZV.js";
38
+ import { n as hn, t as gn } from "./icon-badge-4FMy66AC.js";
39
39
  import { r as _n } from "./use-csp-nonce-ZKs9k-ov.js";
40
- import { t as vn } from "./empty-state-D9o2id1p.js";
40
+ import { t as vn } from "./empty-state-4gmbCkcw.js";
41
41
  import { t as yn } from "./skeleton-DSdtr2qc.js";
42
42
  import { H as bn, V as xn, X as Sn, bt as Cn, it as wn, lt as Tn, n as En, t as Dn, xt as On, yt as kn } from "./dist-moutbfbd.js";
43
43
  import { t as An } from "./markdown-Dw_uH7j7.js";
@@ -11353,7 +11353,7 @@ var tC = (e = {}) => jn({
11353
11353
  let [{ default: t }, { lowlight: n }, { richCodeBlock: r }] = await Promise.all([
11354
11354
  import("./dist-CRMFGjng.js"),
11355
11355
  import("./lowlight-OtcjU2Sa.js"),
11356
- import("./rich-code-block-0OcsqURY.js")
11356
+ import("./rich-code-block-DL5-9JIi.js")
11357
11357
  ]);
11358
11358
  return [r(t, {
11359
11359
  lowlight: n,
@@ -1,4 +1,4 @@
1
- import { r as e } from "./i18n-provider-TQAEPlJ5.js";
1
+ import { r as e } from "./i18n-provider-DBr59j1p.js";
2
2
  import { _ as t, g as n, h as r, n as i, v as a, y as o } from "./number.utils-BG_eEt5g.js";
3
3
  import { useCallback as s, useEffect as c, useMemo as l, useRef as u, useState as d } from "react";
4
4
  import { busySettler as f, busySettler as p, createSubmitGuard as m, createSubmitGuard as h } from "@nxgt/material-core/submit-guard";
@@ -437,10 +437,37 @@ function j(e) {
437
437
  }), [t, r]);
438
438
  }
439
439
  //#endregion
440
+ //#region lib/hooks/use-countdown.ts
441
+ function M(e, { paused: t = !1, onExpire: n, key: r } = {}) {
442
+ let [i, a] = d(e), [o, s] = d({
443
+ duration: e,
444
+ key: r
445
+ }), l = u(e), f = u(n);
446
+ f.current = n;
447
+ let p = o.duration !== e || o.key !== r;
448
+ return p && (s({
449
+ duration: e,
450
+ key: r
451
+ }), a(e)), c(() => {
452
+ l.current = e;
453
+ }, [e, r]), c(() => {
454
+ if (t || l.current <= 0) return;
455
+ let e = Date.now() + l.current * 1e3, n = window.setInterval(() => {
456
+ let t = Math.max(0, (e - Date.now()) / 1e3);
457
+ l.current = t, a(t), t <= 0 && (window.clearInterval(n), f.current?.());
458
+ }, 250);
459
+ return () => window.clearInterval(n);
460
+ }, [
461
+ t,
462
+ e,
463
+ r
464
+ ]), p ? e : i;
465
+ }
466
+ //#endregion
440
467
  //#region lib/hooks/use-heading-spy.ts
441
- var M = "0px 0px -65% 0px", N = [0, 1];
442
- function P(e, t, n) {
443
- let r = n?.rootMargin ?? M, i = n?.threshold ?? N;
468
+ var N = "0px 0px -65% 0px", P = [0, 1];
469
+ function F(e, t, n) {
470
+ let r = n?.rootMargin ?? N, i = n?.threshold ?? P;
444
471
  c(() => {
445
472
  if (e.length === 0) return;
446
473
  let n = e.map((e) => document.getElementById(e)).filter((e) => e != null);
@@ -462,4 +489,4 @@ function P(e, t, n) {
462
489
  ]);
463
490
  }
464
491
  //#endregion
465
- export { A as a, j as i, p as n, k as o, h as r, P as t };
492
+ export { j as a, h as i, M as n, A as o, p as r, k as s, F as t };
@@ -1,5 +1,5 @@
1
- import { r as e } from "./i18n-provider-TQAEPlJ5.js";
2
- import { t } from "./translate-material-hUA-UAP6.js";
1
+ import { r as e } from "./i18n-provider-DBr59j1p.js";
2
+ import { t } from "./translate-material-BkqnImND.js";
3
3
  import { useCallback as n } from "react";
4
4
  //#region lib/i18n/use-material-translation.ts
5
5
  function r() {
@@ -64,7 +64,7 @@ Picking among options: select, combobox, autocomplete, checkbox, radio, card and
64
64
 
65
65
  ## [Data display](data-display.md)
66
66
 
67
- Read-only presentation: avatar, shape, badge, chip, stat and metric cards, card presets, list tile, timeline, code, markdown, terminal, API docs blocks, QR and barcode, two-step verification (enrolment secret, recovery codes), day separator. 28 entries.
67
+ Read-only presentation: avatar, shape, badge, chip, stat and metric cards, card presets, list tile, timeline, code, markdown, terminal, API docs blocks, QR and barcode, two-step verification (enrolment secret, recovery codes), seat ring, secret card, vote tally, day separator. 37 entries.
68
68
 
69
69
  [<img src="../previews/data-display-avatar.webp" height="72" alt="Avatar">](data-display.md#avatar)
70
70
  [<img src="../previews/data-display-shape.webp" height="72" alt="Shape">](data-display.md#shape)
@@ -869,6 +869,7 @@ stop, announced as a link.
869
869
  | `reloadDocument`, `preventScrollReset` | as `Link` | — | passed to the router link |
870
870
  | `viewTransition` | as `Link` | — | **Deprecated**, still passed to the router link: use `ActivityLayout`'s [route transition](../guides/app-shell.md#route-transitions) (React's `<ViewTransition>`); removed after React Router v9 |
871
871
  | `download` | `boolean \| string` | — | the anchor's `download` attribute: saves the target (under that name, for a string) instead of opening it |
872
+ | `linkComponent` | `LinkComponent` | React Router's `Link`, or the [`LinkComponentProvider`](./primitives.md#linkcomponentprovider)'s | renders the link with your own component, given `to` as `href` with `target` and `download`; `replace`, `state`, `reloadDocument`, `preventScrollReset` and `viewTransition` are router-only and not passed |
872
873
 
873
874
  For a file served by a route, skip the client navigation and save it:
874
875
 
@@ -9,6 +9,7 @@ from `@nxgt/material/components`.
9
9
  | <img src="../previews/data-display-avatar.webp" width="96" alt=""> | [Avatar](#avatar) | Image with a fallback and an optional online / offline dot |
10
10
  | <img src="../previews/data-display-avatargroup.webp" width="96" alt=""> | [AvatarGroup](#avatargroup) | Overlapping avatars with a `+N` overflow |
11
11
  | <img src="../previews/data-display-seatring.webp" width="96" alt=""> | [SeatRing](#seatring) | Participants as seats around a circle or in a grid, optionally pickable |
12
+ | <img src="../previews/data-display-secretcard.webp" width="96" alt=""> | [SecretCard](#secretcard) | A card whose secret is not rendered until it is revealed |
12
13
  | <img src="../previews/data-display-shape.webp" width="96" alt=""> | [Shape](#shape) | Material 3 Expressive's shapes as a backdrop, an icon container or a mask for a photo |
13
14
  | <img src="../previews/data-display-badge.webp" width="96" alt=""> | [Badge](#badge) | Status pill, 20 px, in seven tones, soft or outlined, with an optional decorative `icon` before the words |
14
15
  | <img src="../previews/data-display-countbadge.webp" width="96" alt=""> | [CountBadge](#countbadge) | Count in the corner of its children |
@@ -28,6 +29,7 @@ from `@nxgt/material/components`.
28
29
  | <img src="../previews/data-display-timeline.webp" width="96" alt=""> | [Timeline](#timeline) | Vertical event timeline: shaped markers, cards, alternating sides, day groups (`DaySeparator` headings, words from `material.day-separator.*`), relative times; `layout="auto"` needs a parent with a definite width (`rail` in a `w-fit` parent) |
29
30
  | <img src="../previews/data-display-changehistory.webp" width="96" alt=""> | [ChangeHistory](#changehistory) | History of the changes made to anything, as a rail or as cards |
30
31
  | <img src="../previews/data-display-zonedtime.webp" width="96" alt=""> | [ZonedTime](#zonedtime) | A moment written in a given time zone, identical on the server and the client |
32
+ | <img src="../previews/data-display-votetally.webp" width="96" alt=""> | [VoteTally](#votetally) | A live ranked tally of a vote, with counts, shares and voters |
31
33
  | <img src="../previews/data-display-eventchip.webp" width="96" alt=""> | [EventChip](#eventchip) | Calendar event pill, soft, solid or outlined |
32
34
  | <img src="../previews/data-display-codeblock.webp" width="96" alt=""> | [CodeBlock](#codeblock) | Code, JSON tree and tabbed samples |
33
35
  | <img src="../previews/data-display-markdown.webp" width="96" alt=""> | [Markdown](#markdown) | GFM with math and mermaid |
@@ -192,6 +194,80 @@ no `style` attribute and a strict CSP is fine.
192
194
 
193
195
  ---
194
196
 
197
+ ## SecretCard
198
+
199
+ ![SecretCard](../previews/data-display-secretcard.webp)
200
+
201
+ A card that holds a secret and shows it only after a deliberate tap or click: a
202
+ private role in a party game, a one-time code, a recovery key, a card to turn
203
+ over. It is hidden by default, can be hidden again, and can hide itself after a
204
+ delay. The secret (`back`) is **not rendered** while hidden, so it is not in the
205
+ markup, not read by a screen reader and not copied; it is not merely covered by CSS.
206
+
207
+ ```tsx
208
+ import { Button, SecretCard } from '@nxgt/material/components';
209
+
210
+ <SecretCard
211
+ title="Your role"
212
+ description="Look alone, then hide it."
213
+ back={<p>Night watch</p>}
214
+ autoHideMs={8000}
215
+ actions={<Button variant="outlined">Next player</Button>}
216
+ />
217
+ ```
218
+
219
+ | Prop | Type | Default | Notes |
220
+ | --- | --- | --- | --- |
221
+ | `title` | `ReactNode` | required | the name, always shown (a heading, `h3`) |
222
+ | `back` | `ReactNode` | required | the secret, rendered only while revealed |
223
+ | `description` | `ReactNode` | - | a hint, shown only while hidden |
224
+ | `front` | `ReactNode` | neutral pattern | the hidden face; put no secret in it |
225
+ | `icon` | `ReactNode` | - | decorative, before the title |
226
+ | `tone` | `Tone` | - | `data-tone`: a named tone or an app's |
227
+ | `actions` | `ReactNode` | - | buttons beside the reveal button |
228
+ | `revealed` + `onRevealedChange` | `boolean` + `(revealed) => void` | - | controlled |
229
+ | `defaultRevealed`, `onRevealedChange` | `boolean`, `(revealed) => void` | `false` | uncontrolled; never together with `revealed` |
230
+ | `autoHideMs` | `number` | - | hides again after this long shown; also calls `onRevealedChange(false)` |
231
+ | `disabled` | `boolean` | `false` | the card cannot be revealed; a revealed card can always be hidden |
232
+ | `labels` | `Partial<{ reveal, revealed, hidden }>` | `material.secret-card.*` | the button's name ("Show secret") and the announcements |
233
+
234
+ Controlled, with your own words:
235
+
236
+ ```tsx
237
+ const [open, setOpen] = useState(false);
238
+
239
+ <SecretCard
240
+ title="Recovery key"
241
+ back={<code>K7QF-92LM</code>}
242
+ revealed={open}
243
+ onRevealedChange={setOpen}
244
+ labels={{ reveal: 'Show the key', revealed: 'Key shown', hidden: 'Key hidden' }}
245
+ />
246
+ ```
247
+
248
+ **Accessibility**
249
+
250
+ - The reveal is a real `button`, a disclosure: one name ("Show secret"; the
251
+ icon changes) and `aria-expanded` for the state, with `aria-controls` on the
252
+ face. Only a click, a tap, Enter or Space reveals: no hover, so a scroll
253
+ never reveals it.
254
+ - A click is carried by `aria-expanded`, so nothing is announced for it. A
255
+ change a click does not explain (`autoHideMs`, the parent) is said through a
256
+ polite live region ("Secret revealed", "Secret hidden"). The message never
257
+ holds the secret, and nothing is said on the first render.
258
+ - When the face that held focus is swapped (a focusable `back` on auto-hide, a
259
+ focusable `front` on reveal), focus moves to the reveal button.
260
+ - `disabled` stops a reveal only: a revealed card can always be hidden.
261
+ - The flip runs only after a change (not on first render or hydration) and
262
+ without `prefers-reduced-motion`; with it the face changes at once.
263
+ - The card is full width up to 28rem, the button is at least 44px high, and
264
+ the layout follows `dir` (RTL) and dark mode.
265
+ - Keep `front` and `description` free of the secret: they are always in the page.
266
+
267
+ **Storybook:** `Data display/SecretCard` · **Vue:** not yet (#1938)
268
+
269
+ ---
270
+
195
271
  ## Shape
196
272
 
197
273
  ![Shape](../previews/data-display-shape.webp)
@@ -417,7 +493,7 @@ import { Link } from 'react-router';
417
493
  | `items` | `PeriodCardItem[]` | — | `id`, `title`, `start`, `end` (`Date`, ISO string or timestamp, read as local days; `'2026-09-01'` is that day, not UTC), and optionally `state`, `description`, `badges`, `footer`, `href` |
418
494
  | `today` | `DayInput \| false` | now | the day that tells the current period, by calendar day, both ends included; `false` turns the detection off |
419
495
  | `state` (per item) | `'past' \| 'current' \| 'upcoming'` | from `today` | an explicit state wins over the dates |
420
- | `linkComponent` | `LinkComponent` | `<a>` | the title's link, as the app shell's |
496
+ | `linkComponent` | `LinkComponent` | `<a>` | the title's link, as the app shell's. Defaults to the [`LinkComponentProvider`](./primitives.md#linkcomponentprovider)'s link when one is set. |
421
497
  | `emptyState` | `ReactNode` | — | shown instead of the list when `items` is empty |
422
498
  | `headingLevel` | `2 \| 3 \| 4` | `3` | the level of each card's heading |
423
499
  | `dateFormat` | `Intl.DateTimeFormatOptions` | day, short month, year | how the range reads, through `formatRange` |
@@ -882,7 +958,7 @@ A tile with an `href` ends with a discreet chevron after `trailing`: decorative
882
958
  | --- | --- | --- | --- |
883
959
  | `href` | `string` | — | renders an `<a>`, with a chevron by default |
884
960
  | `chevron` | `boolean` | `true` with an `href`, else `false` | the decorative chevron after `trailing` |
885
- | `linkComponent` | `AppShellLinkComponent` | plain `<a>` | the link element, as in the app shell: takes `href`, spreads every other prop, forwards `ref` |
961
+ | `linkComponent` | `AppShellLinkComponent` | plain `<a>` | the link element, as in the app shell: takes `href`, spreads every other prop, forwards `ref`. Defaults to the [`LinkComponentProvider`](./primitives.md#linkcomponentprovider)'s link when one is set. |
886
962
  | `onClick` | `MouseEventHandler` | — | renders a `<button>` |
887
963
  | `selected` | `boolean` | `false` | the current row: drawn selected; a link gets `aria-current="page"` (your own `aria-current` replaces it), a button `aria-pressed` |
888
964
  | `toggle` | `Switch` props | — | makes the tile a switch row (see below): the whole row toggles a Switch at its end; `href`, `onClick`, `actions`, `chips`, `chevron` and `linkComponent` are refused beside it by type |
@@ -1231,6 +1307,75 @@ the same way; pass the app's zone from the server so every screen agrees.
1231
1307
 
1232
1308
  ---
1233
1309
 
1310
+ ## VoteTally
1311
+
1312
+ ![VoteTally](../previews/data-display-votetally.webp)
1313
+
1314
+ A live ranked tally of a vote: a poll, a decision in a meeting, an election, a
1315
+ party-game ballot. One row per option with its count and share written as text
1316
+ ("Ada Example, 3 votes, 60%"), a bar beneath, and who voted for it. The leader,
1317
+ a tie and the viewer's own vote are marked in words. Counts can change while
1318
+ the page is open.
1319
+
1320
+ ```tsx
1321
+ import { VoteTally } from '@nxgt/material/components';
1322
+
1323
+ <VoteTally
1324
+ aria-label="Best day"
1325
+ options={[
1326
+ { id: 'mon', label: 'Monday', count: 5, voters: [{ id: 'ada', label: 'Ada Example' }] },
1327
+ { id: 'tue', label: 'Tuesday', count: 2 },
1328
+ ]}
1329
+ selectedId="tue"
1330
+ abstainCount={1}
1331
+ />
1332
+ ```
1333
+
1334
+ | Prop | Type | Default | Notes |
1335
+ | --- | --- | --- | --- |
1336
+ | `options` | `VoteOption[]` | required | `{ id, label, count, voters?: { id, label, avatar? }[], avatar? }` |
1337
+ | `total` | `number` | sum of counts | base of the shares; abstentions are never in it. `0` or less falls back to the sum. A `total` below the sum caps each share at 100 |
1338
+ | `sort` | `'count' \| 'none'` | `'count'` | highest first; equal counts keep their order and share a rank |
1339
+ | `selectedId` | `string` | — | the viewer's vote: "Your vote" badge and the primary fill |
1340
+ | `showLeader` | `boolean` | `true` | "Leader" on a single top option, "Tie" on each of several; no arbitrary leader. Off: no mark, no emphasis, and the status says the votes cast instead of who leads |
1341
+ | `showVoters` | `'none' \| 'avatars' \| 'names'` | `'avatars'` | avatars, or a line of names |
1342
+ | `maxVoters` | `number` | `5` | voters shown before "+N"; names mode ends with "+N more" |
1343
+ | `abstainCount` | `number` | — | shown under the bars ("2 abstentions") and added to the announcement |
1344
+ | `animated` | `boolean` | `true` | the bar's width eases when a count changes (only the width), never under `prefers-reduced-motion` |
1345
+ | `emptyLabel` | `ReactNode` | "No votes yet" | shown while no option has a vote, rows (if any) stay beneath it |
1346
+ | `announceDelay` | `number` | `1000` | ms a change waits before it is announced |
1347
+ | `renderOption`, `renderVoters` | `(row) => ReactNode` | — | replace a row's label cell or voters; `row` has `option`, `rank`, `percent`, `leader`, `tied`, `selected` |
1348
+ | `header`, `footer` | `ReactNode` | — | above and below the rows |
1349
+ | `aria-label` / `aria-labelledby` | `string` | — | names the list: always give one |
1350
+ | `className`, other `div` props | — | — | go to the root `div` |
1351
+
1352
+ **Shares.** Percentages are whole numbers. When `total` is the sum of the
1353
+ counts they add up to 100 (largest remainder: 1, 1 and 1 vote read 34%, 33%,
1354
+ 33%). With a larger `total` (voters still to come) each share is rounded on its
1355
+ own, so they add up to less.
1356
+
1357
+ **Accessibility**
1358
+
1359
+ - A `list` of `listitem`s. Each row's text is real: the label, then
1360
+ "3 votes" and "60%". The bars are `aria-hidden`.
1361
+ - Leader, tie and "Your vote" are words in badges, never colour alone.
1362
+ - A visually hidden polite status says where the vote stands ("Monday leads
1363
+ with 5 votes", "Tie between Ada and Bo, 3 votes each"), and any
1364
+ abstentions, once changes stop for
1365
+ `announceDelay`. It never says the page as loaded, and a burst of updates is
1366
+ one announcement.
1367
+ - Voters are a named group (`Voters for Monday`) of avatars each named by the
1368
+ voter, or a line of names.
1369
+ - One column at any width: on a phone the count wraps under the label, long
1370
+ labels wrap, and a bar keeps its own line so changing counts do not move rows.
1371
+ It mirrors in right-to-left, and the bars follow the theme in dark mode.
1372
+ - Bar widths are SVG attributes, so server markup has no `style` attribute and
1373
+ a strict CSP is fine.
1374
+
1375
+ **Storybook:** `Data display/VoteTally` · **Vue:** not yet (#1940)
1376
+
1377
+ ---
1378
+
1234
1379
  ## EventChip
1235
1380
 
1236
1381
  ![EventChip](../previews/data-display-eventchip.webp)