@eifi1/ui-kit 0.51.7 → 0.53.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 (775) hide show
  1. package/README.md +178 -67
  2. package/dist/account/account-parts.d.ts +10 -8
  3. package/dist/account/account-parts.js +1 -18
  4. package/dist/account/account-parts.js.map +1 -1
  5. package/dist/account/data-export-setting-labels.js +1 -1
  6. package/dist/account/data-export-setting-labels.js.map +1 -1
  7. package/dist/account/delete-account-setting-labels.js +1 -1
  8. package/dist/account/delete-account-setting-labels.js.map +1 -1
  9. package/dist/account/email-change-setting-labels.js +1 -1
  10. package/dist/account/email-change-setting-labels.js.map +1 -1
  11. package/dist/account/passkeys-setting.d.ts +9 -2
  12. package/dist/account/passkeys-setting.js +41 -29
  13. package/dist/account/passkeys-setting.js.map +1 -1
  14. package/dist/account/sessions-setting.d.ts +3 -2
  15. package/dist/account/sessions-setting.js +10 -8
  16. package/dist/account/sessions-setting.js.map +1 -1
  17. package/dist/admin/admin-action-confirm.js +1 -3
  18. package/dist/admin/admin-action-confirm.js.map +1 -1
  19. package/dist/admin/admin-parts.d.ts +6 -3
  20. package/dist/admin/admin-parts.js +6 -21
  21. package/dist/admin/admin-parts.js.map +1 -1
  22. package/dist/admin/mail-test-card-labels.js +1 -1
  23. package/dist/admin/mail-test-card-labels.js.map +1 -1
  24. package/dist/admin/plan-change-labels.d.ts +2 -2
  25. package/dist/admin/plan-change-labels.js +1 -17
  26. package/dist/admin/plan-change-labels.js.map +1 -1
  27. package/dist/admin/plan-change.d.ts +3 -42
  28. package/dist/admin/plan-change.js +1 -1
  29. package/dist/admin/plan-change.js.map +1 -1
  30. package/dist/admin/reviewer-scope-editor.js +3 -3
  31. package/dist/admin/reviewer-scope-editor.js.map +1 -1
  32. package/dist/auth/auth-errors-labels.d.ts +11 -0
  33. package/dist/auth/auth-errors-labels.js +13 -0
  34. package/dist/auth/auth-errors-labels.js.map +1 -0
  35. package/dist/auth/auth-errors.d.ts +7 -23
  36. package/dist/auth/auth-errors.js +2 -15
  37. package/dist/auth/auth-errors.js.map +1 -1
  38. package/dist/auth/complete-name-dialog.js +2 -1
  39. package/dist/auth/complete-name-dialog.js.map +1 -1
  40. package/dist/auth/forgot-password-form-labels.js +1 -1
  41. package/dist/auth/forgot-password-form-labels.js.map +1 -1
  42. package/dist/auth/forgot-password-form.js +7 -2
  43. package/dist/auth/forgot-password-form.js.map +1 -1
  44. package/dist/auth/register-form-labels.js +1 -1
  45. package/dist/auth/register-form-labels.js.map +1 -1
  46. package/dist/auth/reset-password-form-labels.js +1 -1
  47. package/dist/auth/reset-password-form-labels.js.map +1 -1
  48. package/dist/auth/reset-password-form.js +5 -1
  49. package/dist/auth/reset-password-form.js.map +1 -1
  50. package/dist/auth/sign-in-form-labels.js +1 -1
  51. package/dist/auth/sign-in-form-labels.js.map +1 -1
  52. package/dist/auth/status-parts.d.ts +0 -3
  53. package/dist/auth/status-parts.js +0 -4
  54. package/dist/auth/status-parts.js.map +1 -1
  55. package/dist/auth/verify-email-labels.js +1 -1
  56. package/dist/auth/verify-email-labels.js.map +1 -1
  57. package/dist/auth/verify-email.js +7 -2
  58. package/dist/auth/verify-email.js.map +1 -1
  59. package/dist/billing/cancel-subscription.d.ts +10 -7
  60. package/dist/billing/cancel-subscription.js +6 -6
  61. package/dist/billing/cancel-subscription.js.map +1 -1
  62. package/dist/billing/plan-card.d.ts +5 -3
  63. package/dist/billing/plan-card.js +7 -8
  64. package/dist/billing/plan-card.js.map +1 -1
  65. package/dist/billing/plan-change-labels.d.ts +42 -0
  66. package/dist/billing/plan-change-labels.js +22 -0
  67. package/dist/billing/plan-change-labels.js.map +1 -0
  68. package/dist/billing/plan-switch.d.ts +9 -6
  69. package/dist/billing/plan-switch.js +7 -5
  70. package/dist/billing/plan-switch.js.map +1 -1
  71. package/dist/chart.d.ts +4 -3
  72. package/dist/chart.js +72 -4
  73. package/dist/chart.js.map +1 -1
  74. package/dist/components/account-chips.d.ts +5 -5
  75. package/dist/components/account-chips.js +11 -13
  76. package/dist/components/account-chips.js.map +1 -1
  77. package/dist/components/alert-banner.d.ts +16 -5
  78. package/dist/components/alert-banner.js +46 -1
  79. package/dist/components/alert-banner.js.map +1 -1
  80. package/dist/components/amount-input.d.ts +13 -0
  81. package/dist/components/amount-input.js +1 -0
  82. package/dist/components/amount-input.js.map +1 -1
  83. package/dist/components/autocomplete.js +57 -65
  84. package/dist/components/autocomplete.js.map +1 -1
  85. package/dist/components/bulk-action-bar.d.ts +12 -2
  86. package/dist/components/bulk-action-bar.js +65 -21
  87. package/dist/components/bulk-action-bar.js.map +1 -1
  88. package/dist/components/button-look.d.ts +5 -5
  89. package/dist/components/button-look.js.map +1 -1
  90. package/dist/components/button.d.ts +2 -2
  91. package/dist/components/button.js.map +1 -1
  92. package/dist/components/chart-edge.d.ts +16 -0
  93. package/dist/components/chart-edge.js +14 -1
  94. package/dist/components/chart-edge.js.map +1 -1
  95. package/dist/components/chart-figure.d.ts +13 -0
  96. package/dist/components/chart-figure.js +15 -0
  97. package/dist/components/chart-figure.js.map +1 -0
  98. package/dist/components/chart-zoom-core.js +1 -4
  99. package/dist/components/chart-zoom-core.js.map +1 -1
  100. package/dist/components/chart.d.ts +8 -1
  101. package/dist/components/chart.js +19 -10
  102. package/dist/components/chart.js.map +1 -1
  103. package/dist/components/combobox-core.d.ts +111 -2
  104. package/dist/components/combobox-core.js +148 -42
  105. package/dist/components/combobox-core.js.map +1 -1
  106. package/dist/components/combobox.d.ts +1 -1
  107. package/dist/components/combobox.js +141 -130
  108. package/dist/components/combobox.js.map +1 -1
  109. package/dist/components/copy-button.d.ts +6 -5
  110. package/dist/components/copy-button.js +5 -3
  111. package/dist/components/copy-button.js.map +1 -1
  112. package/dist/components/country-select.js +2 -2
  113. package/dist/components/country-select.js.map +1 -1
  114. package/dist/components/data-table-column-settings.d.ts +1 -1
  115. package/dist/components/data-table-column-settings.js +33 -7
  116. package/dist/components/data-table-column-settings.js.map +1 -1
  117. package/dist/components/data-table-filters.d.ts +4 -2
  118. package/dist/components/data-table-filters.js +2 -2
  119. package/dist/components/data-table-filters.js.map +1 -1
  120. package/dist/components/data-table-fit.d.ts +6 -4
  121. package/dist/components/data-table-fit.js +4 -1
  122. package/dist/components/data-table-fit.js.map +1 -1
  123. package/dist/components/data-table-groups.d.ts +1 -1
  124. package/dist/components/data-table-groups.js.map +1 -1
  125. package/dist/components/data-table-head.d.ts +1 -1
  126. package/dist/components/data-table-head.js +15 -12
  127. package/dist/components/data-table-head.js.map +1 -1
  128. package/dist/components/data-table-mobile.d.ts +12 -7
  129. package/dist/components/data-table-mobile.js +43 -30
  130. package/dist/components/data-table-mobile.js.map +1 -1
  131. package/dist/components/data-table-pagination.js +11 -6
  132. package/dist/components/data-table-pagination.js.map +1 -1
  133. package/dist/components/data-table-resize.d.ts +1 -1
  134. package/dist/components/data-table-resize.js.map +1 -1
  135. package/dist/components/data-table-row-actions.d.ts +2 -2
  136. package/dist/components/data-table-row-actions.js.map +1 -1
  137. package/dist/components/data-table-row.d.ts +6 -2
  138. package/dist/components/data-table-row.js +17 -10
  139. package/dist/components/data-table-row.js.map +1 -1
  140. package/dist/components/data-table-selection.d.ts +1 -1
  141. package/dist/components/data-table-selection.js.map +1 -1
  142. package/dist/components/data-table-shared.d.ts +10 -2
  143. package/dist/components/data-table-shared.js +18 -0
  144. package/dist/components/data-table-shared.js.map +1 -1
  145. package/dist/components/data-table-sort.d.ts +21 -2
  146. package/dist/components/data-table-sort.js +24 -0
  147. package/dist/components/data-table-sort.js.map +1 -1
  148. package/dist/components/data-table-virtual.d.ts +1 -1
  149. package/dist/components/data-table-virtual.js +18 -6
  150. package/dist/components/data-table-virtual.js.map +1 -1
  151. package/dist/components/data-table.d.ts +13 -4
  152. package/dist/components/data-table.js +139 -36
  153. package/dist/components/data-table.js.map +1 -1
  154. package/dist/components/date-field.d.ts +25 -3
  155. package/dist/components/date-field.js +33 -8
  156. package/dist/components/date-field.js.map +1 -1
  157. package/dist/components/date-picker.d.ts +16 -3
  158. package/dist/components/date-picker.js +55 -26
  159. package/dist/components/date-picker.js.map +1 -1
  160. package/dist/components/date-range-picker.d.ts +38 -24
  161. package/dist/components/date-range-picker.js +23 -7
  162. package/dist/components/date-range-picker.js.map +1 -1
  163. package/dist/components/error-boundary.js +37 -30
  164. package/dist/components/error-boundary.js.map +1 -1
  165. package/dist/components/{ui-labels.d.ts → field-base-labels.d.ts} +0 -2
  166. package/dist/components/{ui-labels.js → field-base-labels.js} +2 -9
  167. package/dist/components/field-base-labels.js.map +1 -0
  168. package/dist/components/field-base.d.ts +4 -3
  169. package/dist/components/field-base.js +3 -3
  170. package/dist/components/field-base.js.map +1 -1
  171. package/dist/components/form-actions.js +11 -5
  172. package/dist/components/form-actions.js.map +1 -1
  173. package/dist/components/hover-menu.d.ts +3 -0
  174. package/dist/components/hover-menu.js +5 -3
  175. package/dist/components/hover-menu.js.map +1 -1
  176. package/dist/components/icon-button.js +14 -9
  177. package/dist/components/icon-button.js.map +1 -1
  178. package/dist/components/inline-confirm.d.ts +43 -0
  179. package/dist/components/inline-confirm.js +67 -0
  180. package/dist/components/inline-confirm.js.map +1 -0
  181. package/dist/components/inline-entity-combobox.d.ts +4 -3
  182. package/dist/components/inline-entity-combobox.js +148 -140
  183. package/dist/components/inline-entity-combobox.js.map +1 -1
  184. package/dist/components/input.js +1 -1
  185. package/dist/components/input.js.map +1 -1
  186. package/dist/components/jump-calendar.js +28 -112
  187. package/dist/components/jump-calendar.js.map +1 -1
  188. package/dist/components/legal-page.d.ts +3 -1
  189. package/dist/components/legal-page.js +3 -2
  190. package/dist/components/legal-page.js.map +1 -1
  191. package/dist/components/legal.d.ts +4 -2
  192. package/dist/components/legal.js.map +1 -1
  193. package/dist/components/lightbox.d.ts +2 -2
  194. package/dist/components/lightbox.js +17 -6
  195. package/dist/components/lightbox.js.map +1 -1
  196. package/dist/components/list.d.ts +6 -2
  197. package/dist/components/list.js +3 -2
  198. package/dist/components/list.js.map +1 -1
  199. package/dist/components/measured-grid.d.ts +1 -1
  200. package/dist/components/measured-grid.js +2 -1
  201. package/dist/components/measured-grid.js.map +1 -1
  202. package/dist/components/mini-calendar.js +3 -5
  203. package/dist/components/mini-calendar.js.map +1 -1
  204. package/dist/components/modal.js +3 -1
  205. package/dist/components/modal.js.map +1 -1
  206. package/dist/components/month-grid.d.ts +55 -0
  207. package/dist/components/month-grid.js +148 -0
  208. package/dist/components/month-grid.js.map +1 -0
  209. package/dist/components/month-picker.d.ts +36 -4
  210. package/dist/components/month-picker.js +123 -146
  211. package/dist/components/month-picker.js.map +1 -1
  212. package/dist/components/multi-select.d.ts +2 -2
  213. package/dist/components/multi-select.js +11 -6
  214. package/dist/components/multi-select.js.map +1 -1
  215. package/dist/components/nav-pills.d.ts +1 -1
  216. package/dist/components/nav-pills.js.map +1 -1
  217. package/dist/components/picker-sheet.js +2 -1
  218. package/dist/components/picker-sheet.js.map +1 -1
  219. package/dist/components/pie-chart-labels.d.ts +7 -0
  220. package/dist/components/pie-chart-labels.js +1 -0
  221. package/dist/components/pie-chart-labels.js.map +1 -1
  222. package/dist/components/pie-chart.d.ts +5 -3
  223. package/dist/components/pie-chart.js +7 -22
  224. package/dist/components/pie-chart.js.map +1 -1
  225. package/dist/components/popover.d.ts +3 -0
  226. package/dist/components/popover.js +7 -1
  227. package/dist/components/popover.js.map +1 -1
  228. package/dist/components/row-actions.js +2 -1
  229. package/dist/components/row-actions.js.map +1 -1
  230. package/dist/components/series-chart-axes.d.ts +4 -4
  231. package/dist/components/series-chart-axes.js.map +1 -1
  232. package/dist/components/series-chart-labels.d.ts +7 -4
  233. package/dist/components/series-chart-labels.js.map +1 -1
  234. package/dist/components/series-chart.d.ts +92 -23
  235. package/dist/components/series-chart.js +74 -30
  236. package/dist/components/series-chart.js.map +1 -1
  237. package/dist/components/share-card.js +3 -3
  238. package/dist/components/share-card.js.map +1 -1
  239. package/dist/components/tabs-labels.d.ts +2 -0
  240. package/dist/components/tabs-labels.js +11 -0
  241. package/dist/components/tabs-labels.js.map +1 -0
  242. package/dist/components/tabs.d.ts +1 -1
  243. package/dist/components/tabs.js +1 -1
  244. package/dist/components/tabs.js.map +1 -1
  245. package/dist/components/toggle-legend.d.ts +2 -2
  246. package/dist/components/toggle-legend.js +6 -6
  247. package/dist/components/toggle-legend.js.map +1 -1
  248. package/dist/components/translation-review-editor.d.ts +4 -2
  249. package/dist/components/translation-review-editor.js +2 -3
  250. package/dist/components/translation-review-editor.js.map +1 -1
  251. package/dist/components/translation-review.d.ts +2 -1
  252. package/dist/components/translation-review.js +2 -2
  253. package/dist/components/translation-review.js.map +1 -1
  254. package/dist/components/treemap-labels.d.ts +31 -0
  255. package/dist/components/treemap-labels.js +10 -0
  256. package/dist/components/treemap-labels.js.map +1 -0
  257. package/dist/components/treemap-layout.d.ts +62 -0
  258. package/dist/components/treemap-layout.js +126 -0
  259. package/dist/components/treemap-layout.js.map +1 -0
  260. package/dist/components/treemap.d.ts +167 -19
  261. package/dist/components/treemap.js +340 -100
  262. package/dist/components/treemap.js.map +1 -1
  263. package/dist/components/use-table-state.d.ts +5 -5
  264. package/dist/components/use-table-state.js +4 -1
  265. package/dist/components/use-table-state.js.map +1 -1
  266. package/dist/data-table.d.ts +1 -2
  267. package/dist/data-table.js +1 -4
  268. package/dist/data-table.js.map +1 -1
  269. package/dist/demo/demo-countdown.d.ts +0 -5
  270. package/dist/demo/demo-countdown.js +0 -4
  271. package/dist/demo/demo-countdown.js.map +1 -1
  272. package/dist/eslint/index.js +1 -1
  273. package/dist/eslint/role-utilities.d.ts +36 -7
  274. package/dist/eslint/role-utilities.js +42 -9
  275. package/dist/eslint/role-utilities.js.map +1 -1
  276. package/dist/feedback/feedback-dialog.d.ts +25 -16
  277. package/dist/feedback/feedback-dialog.js +6 -2
  278. package/dist/feedback/feedback-dialog.js.map +1 -1
  279. package/dist/feedback/feedback-mutation.d.ts +18 -0
  280. package/dist/feedback/feedback-mutation.js +8 -0
  281. package/dist/feedback/feedback-mutation.js.map +1 -0
  282. package/dist/feedback/feedback-page-link.d.ts +53 -0
  283. package/dist/feedback/feedback-page-link.js +37 -0
  284. package/dist/feedback/feedback-page-link.js.map +1 -0
  285. package/dist/feedback/feedback-public-list.d.ts +8 -4
  286. package/dist/feedback/feedback-public-list.js +2 -2
  287. package/dist/feedback/feedback-public-list.js.map +1 -1
  288. package/dist/feedback/feedback-replies.d.ts +3 -1
  289. package/dist/feedback/feedback-replies.js +5 -4
  290. package/dist/feedback/feedback-replies.js.map +1 -1
  291. package/dist/feedback/feedback-row-detail-labels.js +0 -3
  292. package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
  293. package/dist/feedback/feedback-row-detail.d.ts +33 -44
  294. package/dist/feedback/feedback-row-detail.js +28 -34
  295. package/dist/feedback/feedback-row-detail.js.map +1 -1
  296. package/dist/feedback/feedback-status-undo.d.ts +17 -14
  297. package/dist/feedback/feedback-status-undo.js +10 -15
  298. package/dist/feedback/feedback-status-undo.js.map +1 -1
  299. package/dist/feedback/feedback-submit-dialog.d.ts +1 -1
  300. package/dist/feedback/feedback-submit-dialog.js +1 -1
  301. package/dist/feedback/feedback-submit-dialog.js.map +1 -1
  302. package/dist/feedback/feedback-submit.js +9 -3
  303. package/dist/feedback/feedback-submit.js.map +1 -1
  304. package/dist/feedback/feedback-swipe.d.ts +1 -1
  305. package/dist/feedback/feedback-swipe.js.map +1 -1
  306. package/dist/feedback/feedback-table.d.ts +21 -9
  307. package/dist/feedback/feedback-table.js +45 -25
  308. package/dist/feedback/feedback-table.js.map +1 -1
  309. package/dist/feedback/feedback-thread-labels.d.ts +2 -2
  310. package/dist/feedback/feedback-thread-labels.js.map +1 -1
  311. package/dist/feedback/feedback-thread.d.ts +15 -23
  312. package/dist/feedback/feedback-thread.js +2 -1
  313. package/dist/feedback/feedback-thread.js.map +1 -1
  314. package/dist/feedback.d.ts +6 -6
  315. package/dist/feedback.js +90 -6
  316. package/dist/feedback.js.map +1 -1
  317. package/dist/hooks/use-focus-trap.d.ts +35 -0
  318. package/dist/hooks/use-focus-trap.js +6 -4
  319. package/dist/hooks/use-focus-trap.js.map +1 -1
  320. package/dist/hooks/use-hotkey.d.ts +5 -2
  321. package/dist/hooks/use-hotkey.js +5 -4
  322. package/dist/hooks/use-hotkey.js.map +1 -1
  323. package/dist/hooks/use-long-press.d.ts +7 -4
  324. package/dist/hooks/use-long-press.js +3 -0
  325. package/dist/hooks/use-long-press.js.map +1 -1
  326. package/dist/hooks/use-media-query.js +12 -4
  327. package/dist/hooks/use-media-query.js.map +1 -1
  328. package/dist/hooks/use-mounted.d.ts +22 -0
  329. package/dist/hooks/use-mounted.js +24 -0
  330. package/dist/hooks/use-mounted.js.map +1 -0
  331. package/dist/hooks/use-overlay-history.js +19 -3
  332. package/dist/hooks/use-overlay-history.js.map +1 -1
  333. package/dist/i18n/defaults.js +4 -9
  334. package/dist/i18n/defaults.js.map +1 -1
  335. package/dist/i18n/german.js +7 -18
  336. package/dist/i18n/german.js.map +1 -1
  337. package/dist/i18n/kit-labels.d.ts +9 -11
  338. package/dist/i18n/kit-labels.js.map +1 -1
  339. package/dist/i18n/locales/en.js +15 -64
  340. package/dist/i18n/locales/en.js.map +3 -3
  341. package/dist/i18n/locales/es.js +7 -18
  342. package/dist/i18n/locales/es.js.map +1 -1
  343. package/dist/i18n/locales/fr.js +20 -27
  344. package/dist/i18n/locales/fr.js.map +1 -1
  345. package/dist/i18n/locales/hu.js +9 -18
  346. package/dist/i18n/locales/hu.js.map +1 -1
  347. package/dist/i18n/locales/it.js +7 -19
  348. package/dist/i18n/locales/it.js.map +1 -1
  349. package/dist/i18n/locales/zh.js +8 -18
  350. package/dist/i18n/locales/zh.js.map +1 -1
  351. package/dist/i18n/review.js +12 -2
  352. package/dist/i18n/review.js.map +1 -1
  353. package/dist/i18n/shared-labels.js +0 -1
  354. package/dist/i18n/shared-labels.js.map +1 -1
  355. package/dist/index.d.ts +21 -23
  356. package/dist/index.js +50 -65
  357. package/dist/index.js.map +1 -1
  358. package/dist/landing/routing.d.ts +3 -1
  359. package/dist/landing/routing.js +4 -7
  360. package/dist/landing/routing.js.map +1 -1
  361. package/dist/lib/broadcast.d.ts +17 -0
  362. package/dist/lib/broadcast.js +30 -0
  363. package/dist/lib/broadcast.js.map +1 -0
  364. package/dist/lib/date-format.d.ts +37 -2
  365. package/dist/lib/date-format.js +34 -8
  366. package/dist/lib/date-format.js.map +1 -1
  367. package/dist/lib/day-format.d.ts +46 -0
  368. package/dist/lib/day-format.js +46 -0
  369. package/dist/lib/day-format.js.map +1 -0
  370. package/dist/lib/describe-failure.d.ts +14 -0
  371. package/dist/lib/describe-failure.js +9 -0
  372. package/dist/lib/describe-failure.js.map +1 -0
  373. package/dist/lib/direction.d.ts +16 -0
  374. package/dist/lib/direction.js +9 -1
  375. package/dist/lib/direction.js.map +1 -1
  376. package/dist/lib/focus-left.d.ts +63 -0
  377. package/dist/lib/focus-left.js +67 -0
  378. package/dist/lib/focus-left.js.map +1 -0
  379. package/dist/lib/format.js +5 -14
  380. package/dist/lib/format.js.map +1 -1
  381. package/dist/lib/http-status.d.ts +24 -0
  382. package/dist/lib/http-status.js +14 -0
  383. package/dist/lib/http-status.js.map +1 -0
  384. package/dist/lib/intl-cache.d.ts +14 -0
  385. package/dist/lib/intl-cache.js +15 -0
  386. package/dist/lib/intl-cache.js.map +1 -0
  387. package/dist/lib/iso-day.d.ts +11 -0
  388. package/dist/lib/iso-day.js +8 -0
  389. package/dist/lib/iso-day.js.map +1 -0
  390. package/dist/lib/lazy-with-fallback.d.ts +31 -0
  391. package/dist/lib/lazy-with-fallback.js +40 -0
  392. package/dist/lib/lazy-with-fallback.js.map +1 -0
  393. package/dist/lib/path-pattern.d.ts +34 -0
  394. package/dist/lib/path-pattern.js +24 -0
  395. package/dist/lib/path-pattern.js.map +1 -0
  396. package/dist/lib/reduced-motion.d.ts +2 -1
  397. package/dist/lib/reduced-motion.js.map +1 -1
  398. package/dist/lib/round.d.ts +2 -1
  399. package/dist/lib/round.js +2 -1
  400. package/dist/lib/round.js.map +1 -1
  401. package/dist/lib/safe-storage.d.ts +24 -5
  402. package/dist/lib/safe-storage.js +27 -2
  403. package/dist/lib/safe-storage.js.map +1 -1
  404. package/dist/lib/session-refresh.js +1 -1
  405. package/dist/lib/session-refresh.js.map +1 -1
  406. package/dist/lib/share-format.d.ts +13 -0
  407. package/dist/lib/share-format.js +10 -0
  408. package/dist/lib/share-format.js.map +1 -0
  409. package/dist/lib/text-case.d.ts +17 -0
  410. package/dist/lib/text-case.js +8 -0
  411. package/dist/lib/text-case.js.map +1 -0
  412. package/dist/lib/text-match.d.ts +29 -0
  413. package/dist/lib/text-match.js +20 -2
  414. package/dist/lib/text-match.js.map +1 -1
  415. package/dist/lib/thenable.d.ts +13 -0
  416. package/dist/lib/thenable.js +8 -0
  417. package/dist/lib/thenable.js.map +1 -0
  418. package/dist/lib/translation-review.d.ts +7 -2
  419. package/dist/lib/translation-review.js +4 -4
  420. package/dist/lib/translation-review.js.map +1 -1
  421. package/dist/notifications/device-zone-report.js +1 -1
  422. package/dist/notifications/device-zone-report.js.map +1 -1
  423. package/dist/notifications/notification-bell.d.ts +2 -1
  424. package/dist/notifications/notification-bell.js +3 -3
  425. package/dist/notifications/notification-bell.js.map +1 -1
  426. package/dist/notifications/notification-inbox.d.ts +10 -6
  427. package/dist/notifications/notification-inbox.js +26 -48
  428. package/dist/notifications/notification-inbox.js.map +1 -1
  429. package/dist/notifications/notification-parts.d.ts +3 -2
  430. package/dist/notifications/notification-parts.js +1 -3
  431. package/dist/notifications/notification-parts.js.map +1 -1
  432. package/dist/notifications/notification-types-setting.js +1 -1
  433. package/dist/notifications/notification-types-setting.js.map +1 -1
  434. package/dist/notifications/notifications-labels.d.ts +6 -0
  435. package/dist/notifications/notifications-labels.js +1 -0
  436. package/dist/notifications/notifications-labels.js.map +1 -1
  437. package/dist/notifications/push-device-setting.js +1 -1
  438. package/dist/notifications/push-device-setting.js.map +1 -1
  439. package/dist/notifications/push-devices-setting.d.ts +2 -1
  440. package/dist/notifications/push-devices-setting.js +27 -49
  441. package/dist/notifications/push-devices-setting.js.map +1 -1
  442. package/dist/notifications/quiet-hours-setting.d.ts +10 -3
  443. package/dist/notifications/quiet-hours-setting.js +65 -26
  444. package/dist/notifications/quiet-hours-setting.js.map +1 -1
  445. package/dist/notifications/unsubscribe-panel.js +4 -7
  446. package/dist/notifications/unsubscribe-panel.js.map +1 -1
  447. package/dist/notifications/use-push-device.d.ts +39 -1
  448. package/dist/notifications/use-push-device.js +28 -7
  449. package/dist/notifications/use-push-device.js.map +1 -1
  450. package/dist/offline/query-persistence.d.ts +3 -1
  451. package/dist/offline/query-persistence.js +19 -2
  452. package/dist/offline/query-persistence.js.map +1 -1
  453. package/dist/rhf/fields.d.ts +11 -4
  454. package/dist/rhf/fields.js.map +1 -1
  455. package/dist/search/command-palette.js +1 -1
  456. package/dist/search/command-palette.js.map +1 -1
  457. package/dist/search/global-search.js +19 -9
  458. package/dist/search/global-search.js.map +1 -1
  459. package/dist/search/search-index.d.ts +0 -6
  460. package/dist/search/search-index.js +0 -4
  461. package/dist/search/search-index.js.map +1 -1
  462. package/dist/search/use-command-key.d.ts +12 -1
  463. package/dist/search/use-command-key.js +3 -12
  464. package/dist/search/use-command-key.js.map +1 -1
  465. package/dist/search.d.ts +1 -1
  466. package/dist/search.js +6 -1
  467. package/dist/search.js.map +1 -1
  468. package/dist/settings/use-settings-focus.js +1 -0
  469. package/dist/settings/use-settings-focus.js.map +1 -1
  470. package/dist/shell/account-menu-language.d.ts +5 -3
  471. package/dist/shell/account-menu-language.js +2 -2
  472. package/dist/shell/account-menu-language.js.map +1 -1
  473. package/dist/shell/account-menu-text-size.d.ts +9 -6
  474. package/dist/shell/account-menu-text-size.js +2 -2
  475. package/dist/shell/account-menu-text-size.js.map +1 -1
  476. package/dist/shell/app-shell.d.ts +0 -10
  477. package/dist/shell/app-shell.js +1 -7
  478. package/dist/shell/app-shell.js.map +1 -1
  479. package/dist/shell/company-switcher.d.ts +1 -1
  480. package/dist/shell/company-switcher.js.map +1 -1
  481. package/dist/shell/dev-menu.d.ts +1 -1
  482. package/dist/shell/dev-menu.js +5 -18
  483. package/dist/shell/dev-menu.js.map +1 -1
  484. package/dist/shell/top-bar-actions.d.ts +1 -1
  485. package/dist/shell/top-bar-actions.js.map +1 -1
  486. package/dist/shell/top-bar-parts-labels.d.ts +1 -3
  487. package/dist/shell/top-bar-parts-labels.js +0 -1
  488. package/dist/shell/top-bar-parts-labels.js.map +1 -1
  489. package/dist/shell.d.ts +7 -10
  490. package/dist/shell.js +14 -23
  491. package/dist/shell.js.map +1 -1
  492. package/dist/sync/engine.d.ts +30 -4
  493. package/dist/sync/engine.js +172 -52
  494. package/dist/sync/engine.js.map +1 -1
  495. package/dist/sync.d.ts +2 -2
  496. package/dist/sync.js +2 -10
  497. package/dist/sync.js.map +1 -1
  498. package/dist/testing/css-variables.js +3 -8
  499. package/dist/testing/css-variables.js.map +1 -1
  500. package/dist/theme/chart-palette.d.ts +0 -5
  501. package/dist/theme/chart-palette.js +0 -2
  502. package/dist/theme/chart-palette.js.map +1 -1
  503. package/dist/theme/contrast.d.ts +2 -1
  504. package/dist/theme/contrast.js +3 -8
  505. package/dist/theme/contrast.js.map +1 -1
  506. package/dist/theme/palette-store.js +6 -6
  507. package/dist/theme/palette-store.js.map +1 -1
  508. package/dist/theme/text-size.js +3 -8
  509. package/dist/theme/text-size.js.map +1 -1
  510. package/dist/theme/theme-store.js +3 -6
  511. package/dist/theme/theme-store.js.map +1 -1
  512. package/dist/tour/tour-catalogue.d.ts +7 -4
  513. package/dist/tour/tour-catalogue.js +12 -10
  514. package/dist/tour/tour-catalogue.js.map +1 -1
  515. package/dist/tour/tour-dock.d.ts +0 -2
  516. package/dist/tour/tour-dock.js +0 -2
  517. package/dist/tour/tour-dock.js.map +1 -1
  518. package/dist/tour/tour-gates.d.ts +4 -0
  519. package/dist/tour/tour-gates.js +14 -0
  520. package/dist/tour/tour-gates.js.map +1 -1
  521. package/dist/tour/tour-overlay.d.ts +27 -0
  522. package/dist/tour/tour-overlay.js +741 -0
  523. package/dist/tour/tour-overlay.js.map +1 -0
  524. package/dist/tour/tour.d.ts +13 -3
  525. package/dist/tour/tour.js +59 -781
  526. package/dist/tour/tour.js.map +1 -1
  527. package/dist/tour/use-tour-progress.js +1 -1
  528. package/dist/tour/use-tour-progress.js.map +1 -1
  529. package/dist/wizard/stepper-nav.d.ts +5 -1
  530. package/dist/wizard/stepper-nav.js.map +1 -1
  531. package/dist/wizard/types.d.ts +6 -2
  532. package/dist/wizard/types.js.map +1 -1
  533. package/dist/wizard/use-wizard.d.ts +3 -1
  534. package/dist/wizard/use-wizard.js.map +1 -1
  535. package/dist/wizard/validation.d.ts +3 -1
  536. package/dist/wizard/validation.js.map +1 -1
  537. package/package.json +10 -6
  538. package/src/account/account-parts.ts +5 -25
  539. package/src/account/data-export-setting-labels.ts +1 -1
  540. package/src/account/delete-account-setting-labels.ts +1 -1
  541. package/src/account/email-change-setting-labels.ts +1 -1
  542. package/src/account/passkeys-setting.tsx +60 -38
  543. package/src/account/sessions-setting.tsx +18 -12
  544. package/src/admin/admin-action-confirm.tsx +1 -5
  545. package/src/admin/admin-parts.ts +11 -25
  546. package/src/admin/mail-test-card-labels.ts +1 -1
  547. package/src/admin/plan-change-labels.ts +5 -23
  548. package/src/admin/plan-change.tsx +7 -44
  549. package/src/admin/reviewer-scope-editor.tsx +3 -3
  550. package/src/auth/auth-errors-labels.ts +23 -0
  551. package/src/auth/auth-errors.ts +11 -38
  552. package/src/auth/complete-name-dialog.tsx +4 -1
  553. package/src/auth/forgot-password-form-labels.ts +1 -1
  554. package/src/auth/forgot-password-form.tsx +11 -6
  555. package/src/auth/register-form-labels.ts +1 -1
  556. package/src/auth/reset-password-form-labels.ts +1 -1
  557. package/src/auth/reset-password-form.tsx +8 -4
  558. package/src/auth/sign-in-form-labels.ts +1 -1
  559. package/src/auth/status-parts.tsx +0 -6
  560. package/src/auth/verify-email-labels.ts +1 -1
  561. package/src/auth/verify-email.tsx +11 -6
  562. package/src/billing/cancel-subscription.tsx +15 -12
  563. package/src/billing/plan-card.tsx +18 -10
  564. package/src/billing/plan-change-labels.ts +66 -0
  565. package/src/billing/plan-switch.tsx +15 -12
  566. package/src/chart.ts +73 -6
  567. package/src/components/account-chips.tsx +20 -23
  568. package/src/components/alert-banner.tsx +82 -5
  569. package/src/components/amount-input.tsx +18 -7
  570. package/src/components/autocomplete.tsx +64 -70
  571. package/src/components/bulk-action-bar.tsx +70 -2
  572. package/src/components/button-look.ts +5 -5
  573. package/src/components/button.tsx +2 -2
  574. package/src/components/chart-edge.ts +34 -1
  575. package/src/components/chart-figure.tsx +35 -0
  576. package/src/components/chart-zoom-core.tsx +6 -5
  577. package/src/components/chart.tsx +26 -5
  578. package/src/components/combobox-core.tsx +293 -62
  579. package/src/components/combobox.tsx +183 -153
  580. package/src/components/copy-button.tsx +34 -23
  581. package/src/components/country-select.tsx +16 -11
  582. package/src/components/data-table-column-settings.tsx +52 -10
  583. package/src/components/data-table-filters.ts +6 -4
  584. package/src/components/data-table-fit.ts +19 -5
  585. package/src/components/data-table-groups.tsx +1 -1
  586. package/src/components/data-table-head.tsx +32 -22
  587. package/src/components/data-table-mobile.tsx +61 -26
  588. package/src/components/data-table-pagination.tsx +21 -6
  589. package/src/components/data-table-resize.ts +1 -1
  590. package/src/components/data-table-row-actions.tsx +3 -3
  591. package/src/components/data-table-row.tsx +25 -14
  592. package/src/components/data-table-selection.ts +1 -1
  593. package/src/components/data-table-shared.tsx +33 -1
  594. package/src/components/data-table-sort.ts +57 -2
  595. package/src/components/data-table-virtual.ts +32 -8
  596. package/src/components/data-table.tsx +239 -68
  597. package/src/components/date-field.tsx +66 -9
  598. package/src/components/date-picker.tsx +125 -49
  599. package/src/components/date-range-picker.tsx +71 -36
  600. package/src/components/error-boundary.tsx +53 -39
  601. package/src/components/field-base-labels.ts +20 -0
  602. package/src/components/field-base.tsx +13 -7
  603. package/src/components/form-actions.tsx +20 -5
  604. package/src/components/hover-menu.tsx +18 -8
  605. package/src/components/icon-button.tsx +14 -9
  606. package/src/components/inline-confirm.tsx +116 -0
  607. package/src/components/inline-entity-combobox.tsx +197 -153
  608. package/src/components/input.tsx +1 -1
  609. package/src/components/jump-calendar.tsx +30 -146
  610. package/src/components/legal-page.tsx +10 -7
  611. package/src/components/legal.tsx +4 -2
  612. package/src/components/lightbox.tsx +21 -8
  613. package/src/components/list.tsx +10 -4
  614. package/src/components/measured-grid.tsx +8 -4
  615. package/src/components/mini-calendar.tsx +6 -5
  616. package/src/components/modal.tsx +6 -1
  617. package/src/components/month-grid.tsx +229 -0
  618. package/src/components/month-picker.tsx +192 -200
  619. package/src/components/multi-select.tsx +21 -9
  620. package/src/components/nav-pills.tsx +1 -1
  621. package/src/components/picker-sheet.tsx +12 -9
  622. package/src/components/pie-chart-labels.ts +8 -0
  623. package/src/components/pie-chart.tsx +22 -28
  624. package/src/components/popover.tsx +18 -1
  625. package/src/components/row-actions.tsx +2 -1
  626. package/src/components/series-chart-axes.ts +9 -4
  627. package/src/components/series-chart-labels.ts +7 -4
  628. package/src/components/series-chart.tsx +253 -87
  629. package/src/components/share-card.tsx +6 -6
  630. package/src/components/tabs-labels.ts +13 -0
  631. package/src/components/tabs.tsx +2 -2
  632. package/src/components/toggle-legend.tsx +16 -11
  633. package/src/components/translation-review-editor.tsx +6 -5
  634. package/src/components/translation-review.tsx +4 -3
  635. package/src/components/treemap-labels.ts +47 -0
  636. package/src/components/treemap-layout.ts +210 -0
  637. package/src/components/treemap.tsx +780 -138
  638. package/src/components/use-table-state.ts +12 -9
  639. package/src/data-table.ts +1 -4
  640. package/src/demo/demo-countdown.ts +3 -8
  641. package/src/eslint/role-utilities.ts +97 -19
  642. package/src/feedback/feedback-dialog.tsx +30 -19
  643. package/src/feedback/feedback-mutation.ts +20 -0
  644. package/src/feedback/feedback-page-link.ts +90 -0
  645. package/src/feedback/feedback-public-list.tsx +13 -7
  646. package/src/feedback/feedback-replies.tsx +13 -6
  647. package/src/feedback/feedback-row-detail-labels.ts +0 -3
  648. package/src/feedback/feedback-row-detail.tsx +70 -84
  649. package/src/feedback/feedback-status-undo.ts +28 -28
  650. package/src/feedback/feedback-submit-dialog.tsx +2 -2
  651. package/src/feedback/feedback-submit.tsx +21 -7
  652. package/src/feedback/feedback-swipe.tsx +1 -1
  653. package/src/feedback/feedback-table.tsx +86 -38
  654. package/src/feedback/feedback-thread-labels.ts +2 -2
  655. package/src/feedback/feedback-thread.tsx +18 -26
  656. package/src/feedback.ts +89 -6
  657. package/src/hooks/use-focus-trap.ts +8 -24
  658. package/src/hooks/use-hotkey.ts +24 -7
  659. package/src/hooks/use-long-press.ts +16 -2
  660. package/src/hooks/use-media-query.ts +20 -4
  661. package/src/hooks/use-mounted.ts +39 -0
  662. package/src/hooks/use-overlay-history.ts +32 -3
  663. package/src/i18n/defaults.ts +4 -9
  664. package/src/i18n/german.ts +8 -19
  665. package/src/i18n/kit-labels.tsx +9 -11
  666. package/src/i18n/locales/es.ts +8 -19
  667. package/src/i18n/locales/fr.ts +21 -30
  668. package/src/i18n/locales/hu.ts +10 -19
  669. package/src/i18n/locales/it.ts +8 -20
  670. package/src/i18n/locales/zh.ts +9 -19
  671. package/src/i18n/review.ts +12 -2
  672. package/src/i18n/shared-labels.ts +0 -1
  673. package/src/index.ts +57 -49
  674. package/src/landing/routing.tsx +14 -10
  675. package/src/lib/broadcast.ts +52 -0
  676. package/src/lib/date-format.ts +89 -13
  677. package/src/lib/day-format.ts +96 -0
  678. package/src/lib/describe-failure.ts +18 -0
  679. package/src/lib/direction.ts +39 -0
  680. package/src/lib/focus-left.ts +156 -0
  681. package/src/lib/format.ts +5 -17
  682. package/src/lib/http-status.ts +38 -0
  683. package/src/lib/intl-cache.ts +25 -0
  684. package/src/lib/iso-day.ts +13 -0
  685. package/src/lib/lazy-with-fallback.ts +82 -0
  686. package/src/lib/path-pattern.ts +55 -0
  687. package/src/lib/reduced-motion.ts +2 -1
  688. package/src/lib/round.ts +7 -2
  689. package/src/lib/safe-storage.ts +48 -6
  690. package/src/lib/session-refresh.ts +1 -1
  691. package/src/lib/share-format.ts +18 -0
  692. package/src/lib/text-case.ts +19 -0
  693. package/src/lib/text-match.ts +57 -2
  694. package/src/lib/thenable.ts +19 -0
  695. package/src/lib/translation-review.ts +13 -5
  696. package/src/notifications/device-zone-report.ts +1 -1
  697. package/src/notifications/notification-bell.tsx +7 -4
  698. package/src/notifications/notification-inbox.tsx +40 -58
  699. package/src/notifications/notification-parts.tsx +3 -4
  700. package/src/notifications/notification-types-setting.tsx +1 -1
  701. package/src/notifications/notifications-labels.ts +7 -0
  702. package/src/notifications/push-device-setting.tsx +1 -1
  703. package/src/notifications/push-devices-setting.tsx +30 -55
  704. package/src/notifications/quiet-hours-setting.tsx +93 -31
  705. package/src/notifications/unsubscribe-panel.tsx +4 -7
  706. package/src/notifications/use-push-device.ts +76 -10
  707. package/src/offline/query-persistence.ts +35 -3
  708. package/src/rhf/fields.tsx +11 -4
  709. package/src/search/command-palette.tsx +2 -4
  710. package/src/search/global-search.tsx +36 -15
  711. package/src/search/search-index.ts +0 -15
  712. package/src/search/use-command-key.ts +13 -12
  713. package/src/search.ts +10 -1
  714. package/src/settings/use-settings-focus.ts +3 -0
  715. package/src/shell/account-menu-language.tsx +6 -4
  716. package/src/shell/account-menu-text-size.tsx +10 -7
  717. package/src/shell/app-shell.tsx +0 -13
  718. package/src/shell/company-switcher.tsx +1 -1
  719. package/src/shell/dev-menu.tsx +8 -22
  720. package/src/shell/top-bar-actions.tsx +1 -1
  721. package/src/shell/top-bar-parts-labels.ts +1 -4
  722. package/src/shell.ts +21 -17
  723. package/src/sync/engine.ts +339 -61
  724. package/src/sync.ts +2 -8
  725. package/src/testing/css-variables.ts +8 -9
  726. package/src/theme/chart-palette.ts +0 -2
  727. package/src/theme/contrast.ts +8 -9
  728. package/src/theme/palette-store.ts +9 -9
  729. package/src/theme/text-size.ts +6 -8
  730. package/src/theme/theme-store.ts +7 -9
  731. package/src/tour/tour-catalogue.tsx +26 -15
  732. package/src/tour/tour-dock.ts +0 -2
  733. package/src/tour/tour-gates.ts +23 -0
  734. package/src/tour/tour-overlay.tsx +1170 -0
  735. package/src/tour/tour.tsx +108 -1202
  736. package/src/tour/use-tour-progress.ts +1 -1
  737. package/src/wizard/stepper-nav.tsx +3 -1
  738. package/src/wizard/types.ts +11 -2
  739. package/src/wizard/use-wizard.ts +2 -1
  740. package/src/wizard/validation.ts +8 -2
  741. package/tokens.css +6 -5
  742. package/dist/components/field-sync-labels.d.ts +0 -2
  743. package/dist/components/field-sync-labels.js +0 -12
  744. package/dist/components/field-sync-labels.js.map +0 -1
  745. package/dist/components/field-sync.d.ts +0 -164
  746. package/dist/components/field-sync.js +0 -269
  747. package/dist/components/field-sync.js.map +0 -1
  748. package/dist/components/section-nav.d.ts +0 -37
  749. package/dist/components/section-nav.js +0 -33
  750. package/dist/components/section-nav.js.map +0 -1
  751. package/dist/components/sign-chip-labels.d.ts +0 -2
  752. package/dist/components/sign-chip-labels.js +0 -11
  753. package/dist/components/sign-chip-labels.js.map +0 -1
  754. package/dist/components/sign-chip.d.ts +0 -68
  755. package/dist/components/sign-chip.js +0 -47
  756. package/dist/components/sign-chip.js.map +0 -1
  757. package/dist/components/ui-labels.js.map +0 -1
  758. package/dist/lib/isin.d.ts +0 -28
  759. package/dist/lib/isin.js +0 -36
  760. package/dist/lib/isin.js.map +0 -1
  761. package/dist/shell/dev-switchers.d.ts +0 -6
  762. package/dist/shell/dev-switchers.js +0 -12
  763. package/dist/shell/dev-switchers.js.map +0 -1
  764. package/dist/shell/role-switcher.d.ts +0 -21
  765. package/dist/shell/role-switcher.js +0 -31
  766. package/dist/shell/role-switcher.js.map +0 -1
  767. package/src/components/field-sync-labels.ts +0 -13
  768. package/src/components/field-sync.tsx +0 -482
  769. package/src/components/section-nav.tsx +0 -65
  770. package/src/components/sign-chip-labels.ts +0 -12
  771. package/src/components/sign-chip.tsx +0 -124
  772. package/src/components/ui-labels.ts +0 -24
  773. package/src/lib/isin.ts +0 -62
  774. package/src/shell/dev-switchers.tsx +0 -18
  775. package/src/shell/role-switcher.tsx +0 -46
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/toggle-legend.tsx"],"sourcesContent":["// A legend whose entries are SWITCHES — and the stroke table it shares with\n// `SeriesChart`. Lifted out of lenkbank's measurement plots, where ten legends over\n// thirteen charts took channels on and off the picture.\n//\n// No recharts in this module: a legend of switches is plain buttons, and it is as\n// useful under a chart the consumer drew by hand as under `SeriesChart`.\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { FOCUS_RING } from \"./focus-ring\";\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport { DEFAULT_SERIES_CHART_LABELS, type SeriesChartLabels } from \"./series-chart-labels\";\n\n/**\n * The strokes a reader can tell apart, in the order they are handed out.\n *\n * For the charts where the colour is already spoken for: a comparison draws each\n * measurement in its own colour, so a chart carrying several CHANNELS of each has\n * nothing left but the stroke — and \"solid versus dashed\" only gets you two.\n *\n * **Five, and the fifth is the last one.** They are the draughtsman's line types, in\n * the draughtsman's order — continuous, dashed, dotted, dash-dot, dash-dot-dot — the\n * one family of five a century of drawings has already proved legible at a hair's\n * width. A sixth is not distinguishable from one of these at two pixels wide, so a\n * chart needing one is a chart that should be split.\n *\n * Kept here, beside the legend's own mark, and read by `SeriesChart` from here — so\n * the key under a chart and the lines on it cannot disagree.\n */\nexport const STROKE_PATTERNS: readonly (string | undefined)[] = [undefined, \"5 4\", \"1 3\", \"9 3 2 3\", \"9 3 2 3 2 3\"];\n\n/** The dash array for the nth pattern, wrapping past the fifth. */\nexport function strokeDash(order: number): string | undefined {\n const n = STROKE_PATTERNS.length;\n return STROKE_PATTERNS[((Math.trunc(order) % n) + n) % n];\n}\n\n/** Which of the patterns a `step` series is drawn in.\n *\n * A whole-number channel that jumps rather than travels is drawn dashed, and it has\n * to be one of {@link STROKE_PATTERNS} rather than a dash array written out at the\n * call site: a legend entry says its pattern by index, so a step line drawn in\n * anything else would be a legend promising a solid line for a dashed one. */\nexport const STEP_DASH = 1;\n\n/**\n * One key switched, as a new hidden set.\n *\n * Every caller writes `onToggle={(key) => setHidden(toggleHidden(hidden, key))}`. A\n * new set rather than a mutated one, because the state is read during render and a\n * mutation of something a component already holds is invisible to React.\n */\nexport function toggleHidden(hidden: ReadonlySet<string>, key: string): ReadonlySet<string> {\n const next = new Set(hidden);\n if (!next.delete(key)) next.add(key);\n return next;\n}\n\n/**\n * Where a legend that stands BESIDE a chart goes from Large up: UNDER it\n * (docs/text-size-harmonization.md §4). Beside a chart, a legend column takes a third of\n * a row whose plot needs all of it once the text is 25–50 % larger; under it, the row\n * is the plot's and the legend wraps across the width.\n *\n * Pure CSS (`large:`), so a legend moves with the text size alone, whatever drew the\n * row around it:\n * - in a GRID it spans a row of its own (`col-span-full`) after the charts\n * (`order-last`) — Kurvenschmiede's facing pairs;\n * - in a FLEX row it takes a line of its own (`basis-full`), and its row is allowed to\n * wrap for it (`[*:has(>&)]:flex-wrap` sets `flex-wrap` on the PARENT — the one rule\n * here that reaches outside the legend, because a row that may not wrap would squeeze\n * the chart to nothing beside a full-width legend) — Kurvenschmiede's chart rows.\n *\n * A caller that places its legend itself and wants it kept beside the chart at every\n * size overrides with `large:col-auto large:basis-auto`.\n */\nconst UNDER_THE_CHART_AT_LARGE = \"large:order-last large:col-span-full large:basis-full large:[*:has(>&)]:flex-wrap\";\n\n/** A vertical legend's entries from Large up: across, wrapping — it is under the chart\n * now (see {@link UNDER_THE_CHART_AT_LARGE}), where a column of entries would be a\n * screenful of one-word lines. */\nconst VERTICAL_AT_LARGE = \"large:flex-row large:flex-wrap large:items-center large:justify-start\";\n\nexport interface LegendEntry {\n key: string;\n label: ReactNode;\n /** Any CSS colour — `paletteFor(i)` for the kit's own ramp. */\n color: string;\n /**\n * What the entry's mark looks like. `swatch` — a filled square, the default — says\n * WHICH MEASUREMENT. `stroke` says WHICH QUANTITY, for the charts that carry both at\n * once and tell them apart by colour and by dash. `dot` is a round mark, for a\n * MARKER on the chart (a point, an event) rather than a series.\n */\n marker?: \"swatch\" | \"stroke\" | \"dot\";\n /** Which of {@link STROKE_PATTERNS}, when the marker is a stroke — the same index\n * `SeriesChartSeries.dash` takes, so a legend cannot promise a dot-dash the plot\n * draws dashed. Or the same custom `stroke-dasharray` string the series draws with\n * (keksdose's `\"4 3\"`). */\n dash?: number | string;\n /**\n * A mark of the caller's own, drawn in place of {@link marker} — for a key whose\n * mark is not a colour at all (a fading bar for \"open-ended\", a hairline for\n * \"today\"). Decorative: it is wrapped `aria-hidden`, and the label is the name.\n */\n icon?: ReactNode;\n /**\n * `\"end\"`: push this entry to the legend's inline END — the far right in LTR, the far\n * left in RTL — with everything before it kept at the start. kastlan's lease gantt\n * (feedback #49) keys its bars at the start and its \"today\" hairline at the end, over\n * the time axis whose end \"today\" is nearest, and wrote it as\n * `className=\"[&>li:last-child]:ms-auto\"` — a selector that breaks the moment the\n * entries are reordered and that reaches into the legend's markup.\n *\n * An auto inline-start margin, so it is logical (RTL mirrors it) and it takes only the\n * row's LEFTOVER space: on a narrow screen where the row wraps, the entry wraps with it\n * and sits at the end of its own line rather than overflowing. Give it to the LAST\n * entry — auto margins push everything after them too, so an end entry in the middle\n * drags the rest over with it (which is also how to end-align a GROUP: mark its first).\n * In a vertical legend it sits at the column's inline end. Honoured by both\n * {@link StaticLegend} and {@link ToggleLegend}, which share this entry type.\n */\n align?: \"start\" | \"end\";\n}\n\nexport interface ToggleLegendProps {\n entries: LegendEntry[];\n /** The keys currently off the chart. */\n hidden: ReadonlySet<string>;\n onToggle: (key: string) => void;\n /** `vertical` is for a legend standing BESIDE the charts rather than under them —\n * what a row of charts sharing one legend wants, since under two charts there is\n * no \"under\", and putting it under one says it belongs to that one.\n *\n * From Large up a vertical legend moves under the charts and lays its entries\n * across (0.32, docs/text-size-harmonization.md §4) — see {@link LegendColumn}. */\n orientation?: \"horizontal\" | \"vertical\";\n /**\n * Draw a legend of ONE entry rather than nothing.\n *\n * A single line's legend is usually noise. It is not where the legend is the only\n * SWITCH that line has — a series that starts hidden and has no entry to bring it\n * back is gone for good.\n */\n showSingle?: boolean;\n className?: string;\n /** Per-instance strings over `<UiKitProvider labels={{ seriesChart }}>`. */\n labels?: LabelOverride<SeriesChartLabels>;\n}\n\n/**\n * A legend whose entries are switches.\n *\n * `ChartLegendContent` with `onItemClick`/`activeKey` is the other kind: clicking an\n * entry HIGHLIGHTS it and dims the rest, which is right when every series belongs on\n * the chart and one of them is momentarily interesting. This is for the opposite —\n * three coordinates on one axis where y runs to 800 and z barely moves, and the only\n * way to see z is to take y off the chart entirely. Any number may be off at once.\n *\n * A hidden entry keeps its mark and its place. Removing it would reflow the legend on\n * every click and lose the one thing it is for: saying what COULD be shown.\n *\n * Each entry is a real `<button>` with `aria-pressed` — pressed means \"on the chart\" —\n * so Tab reaches every switch and Space/Enter flips it.\n */\nexport function ToggleLegend({\n entries,\n hidden,\n onToggle,\n orientation = \"horizontal\",\n showSingle = false,\n className,\n labels: labelsProp,\n}: ToggleLegendProps) {\n const labels = useKitLabels(\"seriesChart\", DEFAULT_SERIES_CHART_LABELS, labelsProp);\n if (entries.length < (showSingle ? 1 : 2)) return null;\n return (\n <div\n role=\"group\"\n aria-label={labels.legend}\n className={cn(\n \"flex gap-x-3 gap-y-1\",\n orientation === \"vertical\"\n ? cn(\"flex-col items-start justify-center\", VERTICAL_AT_LARGE, UNDER_THE_CHART_AT_LARGE)\n : \"mt-2 flex-wrap items-center\",\n className,\n )}\n >\n {entries.map((entry) => {\n const off = hidden.has(entry.key);\n return (\n <button\n key={entry.key}\n type=\"button\"\n // Pressed rather than a checkbox: it is a control over what the chart\n // draws, and \"not pressed\" is the same sentence the dimmed mark says.\n aria-pressed={!off}\n onClick={() => onToggle(entry.key)}\n className={cn(\n \"flex items-center gap-1.5 rounded text-start text-caption text-secondary transition-opacity hover:opacity-80\",\n FOCUS_RING,\n off && \"opacity-35\",\n entry.align === \"end\" && \"ms-auto\",\n )}\n >\n <LegendMark entry={entry} off={off} />\n {entry.label}\n </button>\n );\n })}\n </div>\n );\n}\n\nexport interface StaticLegendProps {\n entries: LegendEntry[];\n /** See {@link ToggleLegendProps.orientation}. */\n orientation?: \"horizontal\" | \"vertical\";\n /** The list's accessible name. Default: the `seriesChart.legend` label. */\n \"aria-label\"?: string;\n /**\n * `ul` (default): a list — \"list, 4 items\", walked with the list keys. Its entries are\n * `listitem`s, so a host test that counts `getAllByRole(\"listitem\")` on a page with\n * the legend on it counts the legend too: scope it (`within(...)`) or use `div`.\n *\n * `div`: a named `role=\"group\"` of plain entries, for a legend set inside something\n * that is already a list or a table row, where a list in a list reads as nesting that\n * is not there, or where the entries are too few for \"list, 2 items\" to help. The\n * entries are read in order either way; only the list framing goes.\n */\n as?: \"ul\" | \"div\";\n className?: string;\n /** Per-instance strings over `<UiKitProvider labels={{ seriesChart }}>`. */\n labels?: LabelOverride<SeriesChartLabels>;\n}\n\n/**\n * A legend that is a KEY, not a control: the same entries and marks as\n * {@link ToggleLegend} — swatch, stroke (with the chart's own dash), dot, or a mark of\n * the caller's own — but nothing to press.\n *\n * A separate component rather than `ToggleLegend` without `onToggle`, because the two\n * are different things to a screen reader: a group of switches announces a pressed\n * state on every entry, a key is a LIST, read as \"list, 4 items\" and walked with the\n * list keys, and costs no tab stops. The apps drew this by hand under the charts that\n * say what a colour or a dash means without letting you switch it (kastlan's lease\n * timeline, keksdose's cash buffer).\n *\n * Unlike `ToggleLegend` it draws a single entry: a key with one line still says what\n * the one mark on the chart means.\n */\nexport function StaticLegend({\n entries,\n orientation = \"horizontal\",\n \"aria-label\": ariaLabel,\n as = \"ul\",\n className,\n labels: labelsProp,\n}: StaticLegendProps) {\n const labels = useKitLabels(\"seriesChart\", DEFAULT_SERIES_CHART_LABELS, labelsProp);\n if (entries.length === 0) return null;\n const List = as;\n const Item = as === \"ul\" ? \"li\" : \"div\";\n return (\n <List\n // A `div` takes `group`: a name on a role-less div is one ARIA forbids and\n // screen readers drop, and the name is what says these marks are a key.\n role={as === \"div\" ? \"group\" : undefined}\n aria-label={ariaLabel ?? labels.legend}\n className={cn(\n \"m-0 flex list-none gap-x-3 gap-y-1 p-0\",\n orientation === \"vertical\"\n ? cn(\"flex-col items-start justify-center\", VERTICAL_AT_LARGE, UNDER_THE_CHART_AT_LARGE)\n : \"mt-2 flex-wrap items-center\",\n className,\n )}\n >\n {entries.map((entry) => (\n <Item\n key={entry.key}\n className={cn(\"flex items-center gap-1.5 text-caption text-secondary\", entry.align === \"end\" && \"ms-auto\")}\n >\n <LegendMark entry={entry} off={false} />\n {entry.label}\n </Item>\n ))}\n </List>\n );\n}\n\n/**\n * The column a vertical legend stands in: beside the charts, on their horizontal\n * centre line. `h-full` + `justify-center`, so it centres in a grid cell or a flex row\n * that stretches it — and stacks whatever it is given, which is what a panel showing\n * two legends at once needs. The width is the caller's: how wide a legend column is\n * is a fact about that screen's layout.\n *\n * **From Large up it goes UNDER the charts** (0.32, docs/text-size-harmonization.md\n * §4): a row of its own in a grid, a line of its own in a flex row (see\n * `UNDER_THE_CHART_AT_LARGE`), with what it holds laid across and wrapping. A\n * caller's own `max-sm:flex-row …` for the phone keeps working; the caller's width\n * (`sm:w-40`) gives way to the full row, because a size wins over a breakpoint.\n */\nexport function LegendColumn({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div\n className={cn(\n \"flex h-full flex-col justify-center gap-3\",\n UNDER_THE_CHART_AT_LARGE,\n \"large:h-auto large:flex-row large:flex-wrap large:items-start large:justify-start large:gap-x-6\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\n/**\n * A legend and what it is a legend OF, as a headed column.\n *\n * For legends that are several vocabularies at once — which measurement and which\n * channel — so the entries are read down a group and the groups across. Wrapped rows\n * would put the second half of one group on a line under the first half of the next.\n */\nexport function LegendGroup({ title, children }: { title: ReactNode; children: ReactNode }) {\n return (\n <div className=\"flex min-w-0 flex-col items-start gap-1\">\n <span className=\"text-micro font-semibold uppercase tracking-wide text-muted\">{title}</span>\n <div className=\"flex flex-col items-start gap-y-0.5\">{children}</div>\n </div>\n );\n}\n\n/** The entry's mark: a square for a measurement, a stroke for a quantity. A hidden one\n * keeps its outline — a mark that vanished would leave a line of text with nothing in\n * front of it.\n *\n * A stroke is an SVG line with the chart's own `strokeDasharray`, not a CSS gradient\n * approximating it: a gradient can say \"solid or dashed\" and nothing more.\n *\n * A hidden stroke is NOT faded here: the button around it already is (`opacity-35`),\n * and the line used to take its own 0.35 on top — 0.35 × 0.35, about 12 %, a mark\n * gone rather than dimmed, while the square beside it read at the full 35 %. */\nfunction LegendMark({ entry, off }: { entry: LegendEntry; off: boolean }) {\n if (entry.icon != null) {\n return (\n <span aria-hidden className=\"inline-flex shrink-0 items-center\">\n {entry.icon}\n </span>\n );\n }\n if (entry.marker === \"dot\") {\n return (\n <span\n aria-hidden\n className=\"size-2 shrink-0 rounded-full\"\n style={{\n backgroundColor: off ? \"transparent\" : entry.color,\n boxShadow: `inset 0 0 0 1.5px ${entry.color}`,\n }}\n />\n );\n }\n if (entry.marker === \"stroke\") {\n return (\n <svg aria-hidden viewBox=\"0 0 20 2\" className=\"h-0.5 w-5 shrink-0 overflow-visible\">\n <line\n x1={0}\n y1={1}\n x2={20}\n y2={1}\n stroke={entry.color}\n strokeWidth={2}\n strokeDasharray={typeof entry.dash === \"string\" ? entry.dash : strokeDash(entry.dash ?? 0)}\n />\n </svg>\n );\n }\n return (\n <span\n aria-hidden\n className=\"h-2.5 w-2.5 shrink-0 rounded-[3px]\"\n style={{\n backgroundColor: off ? \"transparent\" : entry.color,\n boxShadow: `inset 0 0 0 1.5px ${entry.color}`,\n }}\n />\n );\n}\n"],"mappings":";AA8LU,SAcE,KAdF;AAvLV,SAAS,UAAU;AACnB,SAAS,kBAAkB;AAC3B,SAAS,oBAAwC;AACjD,SAAS,mCAA2D;AAkB7D,MAAM,kBAAmD,CAAC,QAAW,OAAO,OAAO,WAAW,aAAa;AAG3G,SAAS,WAAW,OAAmC;AAC5D,QAAM,IAAI,gBAAgB;AAC1B,SAAO,iBAAkB,KAAK,MAAM,KAAK,IAAI,IAAK,KAAK,CAAC;AAC1D;AAQO,MAAM,YAAY;AASlB,SAAS,aAAa,QAA6B,KAAkC;AAC1F,QAAM,OAAO,IAAI,IAAI,MAAM;AAC3B,MAAI,CAAC,KAAK,OAAO,GAAG,EAAG,MAAK,IAAI,GAAG;AACnC,SAAO;AACT;AAoBA,MAAM,2BAA2B;AAKjC,MAAM,oBAAoB;AAoFnB,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,aAAa;AAAA,EACb;AAAA,EACA,QAAQ;AACV,GAAsB;AACpB,QAAM,SAAS,aAAa,eAAe,6BAA6B,UAAU;AAClF,MAAI,QAAQ,UAAU,aAAa,IAAI,GAAI,QAAO;AAClD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY,OAAO;AAAA,MACnB,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,aACZ,GAAG,uCAAuC,mBAAmB,wBAAwB,IACrF;AAAA,QACJ;AAAA,MACF;AAAA,MAEC,kBAAQ,IAAI,CAAC,UAAU;AACtB,cAAM,MAAM,OAAO,IAAI,MAAM,GAAG;AAChC,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YAGL,gBAAc,CAAC;AAAA,YACf,SAAS,MAAM,SAAS,MAAM,GAAG;AAAA,YACjC,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA,OAAO;AAAA,cACP,MAAM,UAAU,SAAS;AAAA,YAC3B;AAAA,YAEA;AAAA,kCAAC,cAAW,OAAc,KAAU;AAAA,cACnC,MAAM;AAAA;AAAA;AAAA,UAdF,MAAM;AAAA,QAeb;AAAA,MAEJ,CAAC;AAAA;AAAA,EACH;AAEJ;AAuCO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,cAAc;AAAA,EACd,cAAc;AAAA,EACd,KAAK;AAAA,EACL;AAAA,EACA,QAAQ;AACV,GAAsB;AACpB,QAAM,SAAS,aAAa,eAAe,6BAA6B,UAAU;AAClF,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,OAAO;AACb,QAAM,OAAO,OAAO,OAAO,OAAO;AAClC,SACE;AAAA,IAAC;AAAA;AAAA,MAGC,MAAM,OAAO,QAAQ,UAAU;AAAA,MAC/B,cAAY,aAAa,OAAO;AAAA,MAChC,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,aACZ,GAAG,uCAAuC,mBAAmB,wBAAwB,IACrF;AAAA,QACJ;AAAA,MACF;AAAA,MAEC,kBAAQ,IAAI,CAAC,UACZ;AAAA,QAAC;AAAA;AAAA,UAEC,WAAW,GAAG,yDAAyD,MAAM,UAAU,SAAS,SAAS;AAAA,UAEzG;AAAA,gCAAC,cAAW,OAAc,KAAK,OAAO;AAAA,YACrC,MAAM;AAAA;AAAA;AAAA,QAJF,MAAM;AAAA,MAKb,CACD;AAAA;AAAA,EACH;AAEJ;AAeO,SAAS,aAAa,EAAE,UAAU,UAAU,GAAgD;AACjG,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AASO,SAAS,YAAY,EAAE,OAAO,SAAS,GAA8C;AAC1F,SACE,qBAAC,SAAI,WAAU,2CACb;AAAA,wBAAC,UAAK,WAAU,+DAA+D,iBAAM;AAAA,IACrF,oBAAC,SAAI,WAAU,uCAAuC,UAAS;AAAA,KACjE;AAEJ;AAYA,SAAS,WAAW,EAAE,OAAO,IAAI,GAAyC;AACxE,MAAI,MAAM,QAAQ,MAAM;AACtB,WACE,oBAAC,UAAK,eAAW,MAAC,WAAU,qCACzB,gBAAM,MACT;AAAA,EAEJ;AACA,MAAI,MAAM,WAAW,OAAO;AAC1B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,eAAW;AAAA,QACX,WAAU;AAAA,QACV,OAAO;AAAA,UACL,iBAAiB,MAAM,gBAAgB,MAAM;AAAA,UAC7C,WAAW,qBAAqB,MAAM,KAAK;AAAA,QAC7C;AAAA;AAAA,IACF;AAAA,EAEJ;AACA,MAAI,MAAM,WAAW,UAAU;AAC7B,WACE,oBAAC,SAAI,eAAW,MAAC,SAAQ,YAAW,WAAU,uCAC5C;AAAA,MAAC;AAAA;AAAA,QACC,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,QAAQ,MAAM;AAAA,QACd,aAAa;AAAA,QACb,iBAAiB,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,WAAW,MAAM,QAAQ,CAAC;AAAA;AAAA,IAC3F,GACF;AAAA,EAEJ;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,MACX,WAAU;AAAA,MACV,OAAO;AAAA,QACL,iBAAiB,MAAM,gBAAgB,MAAM;AAAA,QAC7C,WAAW,qBAAqB,MAAM,KAAK;AAAA,MAC7C;AAAA;AAAA,EACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/toggle-legend.tsx"],"sourcesContent":["// A legend whose entries are SWITCHES — and the stroke table it shares with\n// `SeriesChart`. Lifted out of lenkbank's measurement plots, where ten legends over\n// thirteen charts took channels on and off the picture.\n//\n// No recharts in this module: a legend of switches is plain buttons, and it is as\n// useful under a chart the consumer drew by hand as under `SeriesChart`.\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { FOCUS_RING } from \"./focus-ring\";\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport { DEFAULT_SERIES_CHART_LABELS, type SeriesChartLabels } from \"./series-chart-labels\";\n\n/**\n * The strokes a reader can tell apart, in the order they are handed out.\n *\n * For the charts where the colour is already spoken for: a comparison draws each\n * measurement in its own colour, so a chart carrying several CHANNELS of each has\n * nothing left but the stroke — and \"solid versus dashed\" only gets you two.\n *\n * **Five, and the fifth is the last one.** They are the draughtsman's line types, in\n * the draughtsman's order — continuous, dashed, dotted, dash-dot, dash-dot-dot — the\n * one family of five a century of drawings has already proved legible at a hair's\n * width. A sixth is not distinguishable from one of these at two pixels wide, so a\n * chart needing one is a chart that should be split.\n *\n * Kept here, beside the legend's own mark, and read by `SeriesChart` from here — so\n * the key under a chart and the lines on it cannot disagree.\n */\nexport const STROKE_PATTERNS: readonly (string | undefined)[] = [undefined, \"5 4\", \"1 3\", \"9 3 2 3\", \"9 3 2 3 2 3\"];\n\n/** The dash array for the nth pattern, wrapping past the fifth. */\nexport function strokeDash(order: number): string | undefined {\n const n = STROKE_PATTERNS.length;\n return STROKE_PATTERNS[((Math.trunc(order) % n) + n) % n];\n}\n\n/** Which of the patterns a `step` series is drawn in.\n *\n * A whole-number channel that jumps rather than travels is drawn dashed, and it has\n * to be one of {@link STROKE_PATTERNS} rather than a dash array written out at the\n * call site: a legend entry says its pattern by index, so a step line drawn in\n * anything else would be a legend promising a solid line for a dashed one. */\nexport const STEP_DASH = 1;\n\n/**\n * One key switched, as a new hidden set.\n *\n * Every caller writes `onToggle={(key) => setHidden(toggleHidden(hidden, key))}`. A\n * new set rather than a mutated one, because the state is read during render and a\n * mutation of something a component already holds is invisible to React.\n */\nexport function toggleHidden(hidden: ReadonlySet<string>, key: string): ReadonlySet<string> {\n const next = new Set(hidden);\n if (!next.delete(key)) next.add(key);\n return next;\n}\n\n/**\n * Where a legend that stands BESIDE a chart goes from Large up: UNDER it\n * (docs/text-size-harmonization.md §4). Beside a chart, a legend column takes a third of\n * a row whose plot needs all of it once the text is 25–50 % larger; under it, the row\n * is the plot's and the legend wraps across the width.\n *\n * Pure CSS (`large:`), so a legend moves with the text size alone, whatever drew the\n * row around it:\n * - in a GRID it spans a row of its own (`col-span-full`) after the charts\n * (`order-last`) — Kurvenschmiede's facing pairs;\n * - in a FLEX row it takes a line of its own (`basis-full`), and its row is allowed to\n * wrap for it (`[*:has(>&)]:flex-wrap` sets `flex-wrap` on the PARENT — the one rule\n * here that reaches outside the legend, because a row that may not wrap would squeeze\n * the chart to nothing beside a full-width legend) — Kurvenschmiede's chart rows.\n *\n * A caller that places its legend itself and wants it kept beside the chart at every\n * size overrides with `large:col-auto large:basis-auto`.\n */\nconst UNDER_THE_CHART_AT_LARGE = \"large:order-last large:col-span-full large:basis-full large:[*:has(>&)]:flex-wrap\";\n\n/** A vertical legend's entries from Large up: across, wrapping — it is under the chart\n * now (see {@link UNDER_THE_CHART_AT_LARGE}), where a column of entries would be a\n * screenful of one-word lines. */\nconst VERTICAL_AT_LARGE = \"large:flex-row large:flex-wrap large:items-center large:justify-start\";\n\nexport interface LegendEntry {\n key: string;\n label: ReactNode;\n /** Any CSS colour — `paletteFor(i)` for the kit's own ramp. */\n color: string;\n /**\n * What the entry's mark looks like. `swatch` — a filled square, the default — says\n * WHICH MEASUREMENT. `stroke` says WHICH QUANTITY, for the charts that carry both at\n * once and tell them apart by colour and by dash. `dot` is a round mark, for a\n * MARKER on the chart (a point, an event) rather than a series.\n */\n marker?: \"swatch\" | \"stroke\" | \"dot\";\n /** Which of {@link STROKE_PATTERNS}, when the marker is a stroke — the same index\n * `SeriesChartSeries.dash` takes, so a legend cannot promise a dot-dash the plot\n * draws dashed. Or the same custom `stroke-dasharray` string the series draws with\n * (keksdose's `\"4 3\"`). */\n dash?: number | string;\n /**\n * A mark of the caller's own, drawn in place of {@link marker} — for a key whose\n * mark is not a colour at all (a fading bar for \"open-ended\", a hairline for\n * \"today\"). Decorative: it is wrapped `aria-hidden`, and the label is the name.\n */\n icon?: ReactNode;\n /**\n * `\"end\"`: push this entry to the legend's inline END — the far right in LTR, the far\n * left in RTL — with everything before it kept at the start. kastlan's lease gantt\n * (feedback #49) keys its bars at the start and its \"today\" hairline at the end, over\n * the time axis whose end \"today\" is nearest, and wrote it as\n * `className=\"[&>li:last-child]:ms-auto\"` — a selector that breaks the moment the\n * entries are reordered and that reaches into the legend's markup.\n *\n * An auto inline-start margin, so it is logical (RTL mirrors it) and it takes only the\n * row's LEFTOVER space: on a narrow screen where the row wraps, the entry wraps with it\n * and sits at the end of its own line rather than overflowing. Give it to the LAST\n * entry — auto margins push everything after them too, so an end entry in the middle\n * drags the rest over with it (which is also how to end-align a GROUP: mark its first).\n * In a vertical legend it sits at the column's inline end. Honoured by both\n * {@link StaticLegend} and {@link ToggleLegend}, which share this entry type.\n */\n align?: \"start\" | \"end\";\n}\n\nexport interface ToggleLegendProps {\n entries: readonly LegendEntry[];\n /** The keys currently off the chart. */\n hidden: ReadonlySet<string>;\n onToggle: (key: string) => void;\n /** `vertical` is for a legend standing BESIDE the charts rather than under them —\n * what a row of charts sharing one legend wants, since under two charts there is\n * no \"under\", and putting it under one says it belongs to that one.\n *\n * From Large up a vertical legend moves under the charts and lays its entries\n * across (0.32, docs/text-size-harmonization.md §4) — see {@link LegendColumn}. */\n orientation?: \"horizontal\" | \"vertical\";\n /**\n * Draw a legend of ONE entry rather than nothing.\n *\n * A single line's legend is usually noise. It is not where the legend is the only\n * SWITCH that line has — a series that starts hidden and has no entry to bring it\n * back is gone for good.\n */\n showSingle?: boolean;\n className?: string;\n /** Per-instance strings over `<UiKitProvider labels={{ seriesChart }}>`. */\n labels?: LabelOverride<SeriesChartLabels>;\n}\n\n/**\n * A legend whose entries are switches.\n *\n * `ChartLegendContent` with `onItemClick`/`activeKey` is the other kind: clicking an\n * entry HIGHLIGHTS it and dims the rest, which is right when every series belongs on\n * the chart and one of them is momentarily interesting. This is for the opposite —\n * three coordinates on one axis where y runs to 800 and z barely moves, and the only\n * way to see z is to take y off the chart entirely. Any number may be off at once.\n *\n * A hidden entry keeps its mark and its place. Removing it would reflow the legend on\n * every click and lose the one thing it is for: saying what COULD be shown.\n *\n * Each entry is a real `<button>` with `aria-pressed` — pressed means \"on the chart\" —\n * so Tab reaches every switch and Space/Enter flips it.\n */\nexport function ToggleLegend({\n entries,\n hidden,\n onToggle,\n orientation = \"horizontal\",\n showSingle = false,\n className,\n labels: labelsProp,\n}: ToggleLegendProps) {\n const labels = useKitLabels(\"seriesChart\", DEFAULT_SERIES_CHART_LABELS, labelsProp);\n if (entries.length < (showSingle ? 1 : 2)) return null;\n return (\n <div\n role=\"group\"\n aria-label={labels.legend}\n className={cn(\n \"flex gap-x-3 gap-y-1\",\n orientation === \"vertical\"\n ? cn(\"flex-col items-start justify-center\", VERTICAL_AT_LARGE, UNDER_THE_CHART_AT_LARGE)\n : \"mt-2 flex-wrap items-center\",\n className,\n )}\n >\n {entries.map((entry) => {\n const off = hidden.has(entry.key);\n return (\n <button\n key={entry.key}\n type=\"button\"\n // Pressed rather than a checkbox: it is a control over what the chart\n // draws, and \"not pressed\" is the same sentence the dimmed mark says.\n aria-pressed={!off}\n onClick={() => onToggle(entry.key)}\n className={cn(\n \"flex items-center gap-1.5 rounded text-start text-caption text-secondary transition-opacity hover:opacity-80\",\n FOCUS_RING,\n entry.align === \"end\" && \"ms-auto\",\n )}\n >\n <LegendMark entry={entry} off={off} />\n {/* Off, the label keeps its ink and is struck through; only the mark fades\n (0.52, refactor audit 2026-10-30, DSP-9). The whole entry at 35 % put the\n label at 1.8:1 — on a button that brings the series back, whose label is\n the only thing saying which one. `aria-pressed` carries the state. */}\n <span className={cn(off && \"line-through\")}>{entry.label}</span>\n </button>\n );\n })}\n </div>\n );\n}\n\nexport interface StaticLegendProps {\n entries: readonly LegendEntry[];\n /** See {@link ToggleLegendProps.orientation}. */\n orientation?: \"horizontal\" | \"vertical\";\n /** The list's accessible name. Default: the `seriesChart.legend` label. */\n \"aria-label\"?: string;\n /**\n * `ul` (default): a list — \"list, 4 items\", walked with the list keys. Its entries are\n * `listitem`s, so a host test that counts `getAllByRole(\"listitem\")` on a page with\n * the legend on it counts the legend too: scope it (`within(...)`) or use `div`.\n *\n * `div`: a named `role=\"group\"` of plain entries, for a legend set inside something\n * that is already a list or a table row, where a list in a list reads as nesting that\n * is not there, or where the entries are too few for \"list, 2 items\" to help. The\n * entries are read in order either way; only the list framing goes.\n */\n as?: \"ul\" | \"div\";\n className?: string;\n /** Per-instance strings over `<UiKitProvider labels={{ seriesChart }}>`. */\n labels?: LabelOverride<SeriesChartLabels>;\n}\n\n/**\n * A legend that is a KEY, not a control: the same entries and marks as\n * {@link ToggleLegend} — swatch, stroke (with the chart's own dash), dot, or a mark of\n * the caller's own — but nothing to press.\n *\n * A separate component rather than `ToggleLegend` without `onToggle`, because the two\n * are different things to a screen reader: a group of switches announces a pressed\n * state on every entry, a key is a LIST, read as \"list, 4 items\" and walked with the\n * list keys, and costs no tab stops. The apps drew this by hand under the charts that\n * say what a colour or a dash means without letting you switch it (kastlan's lease\n * timeline, keksdose's cash buffer).\n *\n * Unlike `ToggleLegend` it draws a single entry: a key with one line still says what\n * the one mark on the chart means.\n */\nexport function StaticLegend({\n entries,\n orientation = \"horizontal\",\n \"aria-label\": ariaLabel,\n as = \"ul\",\n className,\n labels: labelsProp,\n}: StaticLegendProps) {\n const labels = useKitLabels(\"seriesChart\", DEFAULT_SERIES_CHART_LABELS, labelsProp);\n if (entries.length === 0) return null;\n const List = as;\n const Item = as === \"ul\" ? \"li\" : \"div\";\n return (\n <List\n // A `div` takes `group`: a name on a role-less div is one ARIA forbids and\n // screen readers drop, and the name is what says these marks are a key.\n role={as === \"div\" ? \"group\" : undefined}\n aria-label={ariaLabel ?? labels.legend}\n className={cn(\n \"m-0 flex list-none gap-x-3 gap-y-1 p-0\",\n orientation === \"vertical\"\n ? cn(\"flex-col items-start justify-center\", VERTICAL_AT_LARGE, UNDER_THE_CHART_AT_LARGE)\n : \"mt-2 flex-wrap items-center\",\n className,\n )}\n >\n {entries.map((entry) => (\n <Item\n key={entry.key}\n className={cn(\"flex items-center gap-1.5 text-caption text-secondary\", entry.align === \"end\" && \"ms-auto\")}\n >\n <LegendMark entry={entry} off={false} />\n {entry.label}\n </Item>\n ))}\n </List>\n );\n}\n\n/**\n * The column a vertical legend stands in: beside the charts, on their horizontal\n * centre line. `h-full` + `justify-center`, so it centres in a grid cell or a flex row\n * that stretches it — and stacks whatever it is given, which is what a panel showing\n * two legends at once needs. The width is the caller's: how wide a legend column is\n * is a fact about that screen's layout.\n *\n * **From Large up it goes UNDER the charts** (0.32, docs/text-size-harmonization.md\n * §4): a row of its own in a grid, a line of its own in a flex row (see\n * `UNDER_THE_CHART_AT_LARGE`), with what it holds laid across and wrapping. A\n * caller's own `max-sm:flex-row …` for the phone keeps working; the caller's width\n * (`sm:w-40`) gives way to the full row, because a size wins over a breakpoint.\n */\nexport function LegendColumn({ children, className }: { children: ReactNode; className?: string }) {\n return (\n <div\n className={cn(\n \"flex h-full flex-col justify-center gap-3\",\n UNDER_THE_CHART_AT_LARGE,\n \"large:h-auto large:flex-row large:flex-wrap large:items-start large:justify-start large:gap-x-6\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\n/**\n * A legend and what it is a legend OF, as a headed column.\n *\n * For legends that are several vocabularies at once — which measurement and which\n * channel — so the entries are read down a group and the groups across. Wrapped rows\n * would put the second half of one group on a line under the first half of the next.\n */\nexport function LegendGroup({ title, children }: { title: ReactNode; children: ReactNode }) {\n return (\n <div className=\"flex min-w-0 flex-col items-start gap-1\">\n <span className=\"text-micro font-semibold uppercase tracking-wide text-muted\">{title}</span>\n <div className=\"flex flex-col items-start gap-y-0.5\">{children}</div>\n </div>\n );\n}\n\n/** The entry's mark: a square for a measurement, a stroke for a quantity. A hidden one\n * keeps its outline — a mark that vanished would leave a line of text with nothing in\n * front of it.\n *\n * A stroke is an SVG line with the chart's own `strokeDasharray`, not a CSS gradient\n * approximating it: a gradient can say \"solid or dashed\" and nothing more.\n *\n * A hidden mark fades ONCE, on its own box (`opacity-35`; since 0.52 the label beside\n * it keeps its ink, DSP-9). The line used to take its own 0.35 inside a faded button —\n * 0.35 × 0.35, about 12 %, a mark gone rather than dimmed, while the square beside it\n * read at the full 35 %. */\nfunction LegendMark({ entry, off }: { entry: LegendEntry; off: boolean }) {\n const fade = off && \"opacity-35\";\n if (entry.icon != null) {\n return (\n <span aria-hidden className={cn(\"inline-flex shrink-0 items-center\", fade)}>\n {entry.icon}\n </span>\n );\n }\n if (entry.marker === \"dot\") {\n return (\n <span\n aria-hidden\n className={cn(\"size-2 shrink-0 rounded-full\", fade)}\n style={{\n backgroundColor: off ? \"transparent\" : entry.color,\n boxShadow: `inset 0 0 0 1.5px ${entry.color}`,\n }}\n />\n );\n }\n if (entry.marker === \"stroke\") {\n return (\n <svg aria-hidden viewBox=\"0 0 20 2\" className={cn(\"h-0.5 w-5 shrink-0 overflow-visible\", fade)}>\n <line\n x1={0}\n y1={1}\n x2={20}\n y2={1}\n stroke={entry.color}\n strokeWidth={2}\n strokeDasharray={typeof entry.dash === \"string\" ? entry.dash : strokeDash(entry.dash ?? 0)}\n />\n </svg>\n );\n }\n return (\n <span\n aria-hidden\n className={cn(\"h-2.5 w-2.5 shrink-0 rounded-[3px]\", fade)}\n style={{\n backgroundColor: off ? \"transparent\" : entry.color,\n boxShadow: `inset 0 0 0 1.5px ${entry.color}`,\n }}\n />\n );\n}\n"],"mappings":";AA8LU,SAaE,KAbF;AAvLV,SAAS,UAAU;AACnB,SAAS,kBAAkB;AAC3B,SAAS,oBAAwC;AACjD,SAAS,mCAA2D;AAkB7D,MAAM,kBAAmD,CAAC,QAAW,OAAO,OAAO,WAAW,aAAa;AAG3G,SAAS,WAAW,OAAmC;AAC5D,QAAM,IAAI,gBAAgB;AAC1B,SAAO,iBAAkB,KAAK,MAAM,KAAK,IAAI,IAAK,KAAK,CAAC;AAC1D;AAQO,MAAM,YAAY;AASlB,SAAS,aAAa,QAA6B,KAAkC;AAC1F,QAAM,OAAO,IAAI,IAAI,MAAM;AAC3B,MAAI,CAAC,KAAK,OAAO,GAAG,EAAG,MAAK,IAAI,GAAG;AACnC,SAAO;AACT;AAoBA,MAAM,2BAA2B;AAKjC,MAAM,oBAAoB;AAoFnB,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,aAAa;AAAA,EACb;AAAA,EACA,QAAQ;AACV,GAAsB;AACpB,QAAM,SAAS,aAAa,eAAe,6BAA6B,UAAU;AAClF,MAAI,QAAQ,UAAU,aAAa,IAAI,GAAI,QAAO;AAClD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY,OAAO;AAAA,MACnB,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,aACZ,GAAG,uCAAuC,mBAAmB,wBAAwB,IACrF;AAAA,QACJ;AAAA,MACF;AAAA,MAEC,kBAAQ,IAAI,CAAC,UAAU;AACtB,cAAM,MAAM,OAAO,IAAI,MAAM,GAAG;AAChC,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YAGL,gBAAc,CAAC;AAAA,YACf,SAAS,MAAM,SAAS,MAAM,GAAG;AAAA,YACjC,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA,MAAM,UAAU,SAAS;AAAA,YAC3B;AAAA,YAEA;AAAA,kCAAC,cAAW,OAAc,KAAU;AAAA,cAKpC,oBAAC,UAAK,WAAW,GAAG,OAAO,cAAc,GAAI,gBAAM,OAAM;AAAA;AAAA;AAAA,UAjBpD,MAAM;AAAA,QAkBb;AAAA,MAEJ,CAAC;AAAA;AAAA,EACH;AAEJ;AAuCO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,cAAc;AAAA,EACd,cAAc;AAAA,EACd,KAAK;AAAA,EACL;AAAA,EACA,QAAQ;AACV,GAAsB;AACpB,QAAM,SAAS,aAAa,eAAe,6BAA6B,UAAU;AAClF,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,QAAM,OAAO;AACb,QAAM,OAAO,OAAO,OAAO,OAAO;AAClC,SACE;AAAA,IAAC;AAAA;AAAA,MAGC,MAAM,OAAO,QAAQ,UAAU;AAAA,MAC/B,cAAY,aAAa,OAAO;AAAA,MAChC,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,aACZ,GAAG,uCAAuC,mBAAmB,wBAAwB,IACrF;AAAA,QACJ;AAAA,MACF;AAAA,MAEC,kBAAQ,IAAI,CAAC,UACZ;AAAA,QAAC;AAAA;AAAA,UAEC,WAAW,GAAG,yDAAyD,MAAM,UAAU,SAAS,SAAS;AAAA,UAEzG;AAAA,gCAAC,cAAW,OAAc,KAAK,OAAO;AAAA,YACrC,MAAM;AAAA;AAAA;AAAA,QAJF,MAAM;AAAA,MAKb,CACD;AAAA;AAAA,EACH;AAEJ;AAeO,SAAS,aAAa,EAAE,UAAU,UAAU,GAAgD;AACjG,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AASO,SAAS,YAAY,EAAE,OAAO,SAAS,GAA8C;AAC1F,SACE,qBAAC,SAAI,WAAU,2CACb;AAAA,wBAAC,UAAK,WAAU,+DAA+D,iBAAM;AAAA,IACrF,oBAAC,SAAI,WAAU,uCAAuC,UAAS;AAAA,KACjE;AAEJ;AAaA,SAAS,WAAW,EAAE,OAAO,IAAI,GAAyC;AACxE,QAAM,OAAO,OAAO;AACpB,MAAI,MAAM,QAAQ,MAAM;AACtB,WACE,oBAAC,UAAK,eAAW,MAAC,WAAW,GAAG,qCAAqC,IAAI,GACtE,gBAAM,MACT;AAAA,EAEJ;AACA,MAAI,MAAM,WAAW,OAAO;AAC1B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,eAAW;AAAA,QACX,WAAW,GAAG,gCAAgC,IAAI;AAAA,QAClD,OAAO;AAAA,UACL,iBAAiB,MAAM,gBAAgB,MAAM;AAAA,UAC7C,WAAW,qBAAqB,MAAM,KAAK;AAAA,QAC7C;AAAA;AAAA,IACF;AAAA,EAEJ;AACA,MAAI,MAAM,WAAW,UAAU;AAC7B,WACE,oBAAC,SAAI,eAAW,MAAC,SAAQ,YAAW,WAAW,GAAG,uCAAuC,IAAI,GAC3F;AAAA,MAAC;AAAA;AAAA,QACC,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,QAAQ,MAAM;AAAA,QACd,aAAa;AAAA,QACb,iBAAiB,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,WAAW,MAAM,QAAQ,CAAC;AAAA;AAAA,IAC3F,GACF;AAAA,EAEJ;AACA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,MACX,WAAW,GAAG,sCAAsC,IAAI;AAAA,MACxD,OAAO;AAAA,QACL,iBAAiB,MAAM,gBAAgB,MAAM;AAAA,QAC7C,WAAW,qBAAqB,MAAM,KAAK;AAAA,MAC7C;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -30,8 +30,10 @@ export interface TranslationReviewEditorProps {
30
30
  * opened by a tap — that one may only be opened to read.
31
31
  */
32
32
  focusWording?: boolean;
33
- /** How the verdict's date reads. Default: the kit's `formatDate`, medium, in the
34
- * provider's locale. */
33
+ /** How the verdict's date reads. Default (0.52): the `<UiKitProvider formatDate>` when
34
+ * the app set one (its date preference; the day sits in a sentence, so no weekday), else
35
+ * the kit's `formatDate`, medium, in the provider's locale and `timeZone`. An empty
36
+ * answer falls back to the kit's. */
35
37
  formatDate?: (iso: string) => string;
36
38
  /** Turns a rejection into words. Default {@link TranslationReviewLabels.failed}. */
37
39
  formatError?: (error: unknown) => ReactNode;
@@ -3,7 +3,7 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from "react";
4
4
  import { useKitLabels } from "../i18n/kit-labels.js";
5
5
  import { cn } from "../lib/cn.js";
6
- import { useKitFormat } from "../lib/format.js";
6
+ import { useDayFormatter } from "../lib/day-format.js";
7
7
  import { placeholderTokens, reviewWrite } from "../lib/translation-review.js";
8
8
  import { AlertBanner } from "./alert-banner.js";
9
9
  import { Caption, SectionLabel } from "./text.js";
@@ -32,7 +32,7 @@ function TranslationReviewEditor({
32
32
  labels: labelsProp
33
33
  }) {
34
34
  const labels = useKitLabels("translationReview", DEFAULT_TRANSLATION_REVIEW_LABELS, labelsProp);
35
- const { formatDate } = useKitFormat();
35
+ const date = useDayFormatter(formatDateProp);
36
36
  const review = row.review;
37
37
  const missing = row.text === "";
38
38
  const editable = !readOnly && onSave !== void 0;
@@ -50,7 +50,6 @@ function TranslationReviewEditor({
50
50
  }, []);
51
51
  const canFlag = note.trim() !== "" || suggestion.trim() !== row.text.trim();
52
52
  const changed = row.status === "changed" ? review : null;
53
- const date = (iso) => formatDateProp ? formatDateProp(iso) : formatDate(iso, "medium");
54
53
  const run = async (kind, action) => {
55
54
  setBusy(kind);
56
55
  setFailure(null);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/translation-review-editor.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport { cn } from \"../lib/cn\";\nimport { useKitFormat } from \"../lib/format\";\nimport { placeholderTokens, reviewWrite } from \"../lib/translation-review\";\nimport type {\n TranslationReviewKey,\n TranslationReviewWrite,\n TranslationRow,\n TranslationVerdict,\n} from \"../lib/translation-review\";\nimport { AlertBanner } from \"./alert-banner\";\nimport { Caption, SectionLabel } from \"./text\";\nimport { DEFAULT_TRANSLATION_REVIEW_LABELS } from \"./translation-review-labels\";\nimport type { TranslationReviewLabels } from \"./translation-review-labels\";\nimport { Button } from \"./button\";\nimport { Textarea } from \"./textarea\";\nimport { COMMIT_EXCEPT_BILLING } from \"./write-lock\";\n\n/** A callback may return a promise: resolve and the editor closes, reject and it stays\n * open with the error. */\ntype MaybePromise = void | Promise<unknown>;\n\nexport interface TranslationReviewEditorProps {\n row: TranslationRow;\n /** The reference language's own name (\"English\", \"Deutsch\") — the app's choice of\n * reference, so the app's word for it. */\n referenceLabel: ReactNode;\n /** The reviewed language's own name (\"Français\"). */\n localeLabel: ReactNode;\n /** Store a verdict. Left out (or `readOnly`): the editor shows the row and its last\n * verdict, with no fields and no actions. */\n onSave?: (write: TranslationReviewWrite) => MaybePromise;\n /** Clear the verdict — back to unreviewed. Left out: no reset button. */\n onClear?: (key: TranslationReviewKey) => MaybePromise;\n /** The Cancel button — and called after a save or a reset resolves, so the panel\n * that opened the editor closes it. */\n onClose?: () => void;\n /** Look, do not touch: a locale the viewer may read but not review. */\n readOnly?: boolean;\n /**\n * 0.25: put the cursor in the wording field when the editor opens, at the end of the\n * text. The panel sets it when a phone card is swiped toward \"Needs a change\" (keksdose\n * live #377): that swipe opens the editor to be written in, and a reviewer who has just\n * said \"this is wrong\" should not have to find the field first. Not for an editor\n * opened by a tap — that one may only be opened to read.\n */\n focusWording?: boolean;\n /** How the verdict's date reads. Default: the kit's `formatDate`, medium, in the\n * provider's locale. */\n formatDate?: (iso: string) => string;\n /** Turns a rejection into words. Default {@link TranslationReviewLabels.failed}. */\n formatError?: (error: unknown) => ReactNode;\n className?: string;\n labels?: LabelOverride<TranslationReviewLabels>;\n}\n\n/** The wording a verdict was given on, under the line that says it changed. */\nfunction Then({ label, text }: { label: string; text: string }) {\n return (\n <div className=\"mt-1.5 min-w-0\">\n <span className=\"text-xs font-medium\">{label}</span>\n <p className=\"whitespace-pre-line break-words text-secondary\">{text}</p>\n </div>\n );\n}\n\n/**\n * One string, opened: the reference beside the translation, what changed since the last\n * verdict, what a machine can see is wrong (the placeholders), and the two things a\n * reviewer can say about it — keksdose's and kastlan's `translation-review-editor.tsx`.\n *\n * \"Better wording\" opens on the CURRENT text rather than empty, because the usual\n * correction is a word or an ending, and retyping a sentence to change one word is how\n * new mistakes get in. It is sent only with \"Needs a change\" — approving says the text is\n * fine as it stands. A \"Needs a change\" with neither a note nor a different wording tells\n * whoever fixes it nothing, so it takes one of the two.\n *\n * A MISSING string (kastlan: the locale lacks the key) cannot be approved — there is\n * nothing to approve. Its field starts empty and is the translation to add; the verdict\n * is NEEDS_CHANGE on the empty text, which the export hands a developer as the string to\n * add.\n *\n * Every action waits for its callback: the buttons hold while the promise is out, a\n * resolution closes the editor through `onClose`, and a rejection keeps it open with what\n * was typed and the error under the buttons.\n *\n * Each verdict is a `commit` exempt from billing (`COMMIT_EXCEPT_BILLING`, 0.33): a review\n * is an admin route, which a lapsed plan never locks (docs/billing-harmonization.md\n * §12.13, §12.36); a demo's lock still holds it.\n */\nexport function TranslationReviewEditor({\n row,\n referenceLabel,\n localeLabel,\n onSave,\n onClear,\n onClose,\n readOnly = false,\n focusWording = false,\n formatDate: formatDateProp,\n formatError,\n className,\n labels: labelsProp,\n}: TranslationReviewEditorProps) {\n const labels = useKitLabels(\"translationReview\", DEFAULT_TRANSLATION_REVIEW_LABELS, labelsProp);\n const { formatDate } = useKitFormat();\n const review = row.review;\n const missing = row.text === \"\";\n const editable = !readOnly && onSave !== undefined;\n const [suggestion, setSuggestion] = useState(review?.suggestion ?? row.text);\n const [note, setNote] = useState(review?.note ?? \"\");\n const [busy, setBusy] = useState<\"approve\" | \"flag\" | \"clear\" | null>(null);\n const [failure, setFailure] = useState<ReactNode>(null);\n const wordingRef = useRef<HTMLTextAreaElement>(null);\n // On mount only: the prop says how the editor was OPENED, not where focus belongs\n // for the rest of its life.\n const focusOnOpen = useRef(focusWording && editable);\n useEffect(() => {\n const field = wordingRef.current;\n if (!focusOnOpen.current || !field) return;\n field.focus();\n field.setSelectionRange(field.value.length, field.value.length);\n }, []);\n\n const canFlag = note.trim() !== \"\" || suggestion.trim() !== row.text.trim();\n const changed = row.status === \"changed\" ? review : null;\n const date = (iso: string) => (formatDateProp ? formatDateProp(iso) : formatDate(iso, \"medium\"));\n\n const run = async (kind: \"approve\" | \"flag\" | \"clear\", action: () => MaybePromise) => {\n setBusy(kind);\n setFailure(null);\n try {\n await action();\n onClose?.();\n } catch (caught) {\n setFailure(formatError ? formatError(caught) : labels.failed);\n } finally {\n setBusy(null);\n }\n };\n\n const save = (verdict: TranslationVerdict) => {\n if (!onSave) return;\n const trimmedNote = note.trim() || null;\n // Approving says the text is right as it stands: there is no wording to suggest.\n const sent = verdict === \"APPROVED\" ? null : suggestion.trim() || null;\n void run(verdict === \"APPROVED\" ? \"approve\" : \"flag\", () => onSave(reviewWrite(row, verdict, trimmedNote, sent)));\n };\n\n return (\n <div className={cn(\"min-w-0 space-y-3 text-sm\", className)}>\n <code className=\"block break-all text-xs text-muted\">{row.key}</code>\n\n <div className=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\n <div className=\"min-w-0\">\n <SectionLabel as=\"span\" size=\"xs\">\n {referenceLabel}\n </SectionLabel>\n <p className=\"mt-1 whitespace-pre-line break-words text-secondary\">{row.reference}</p>\n </div>\n <div className=\"min-w-0\">\n <SectionLabel as=\"span\" size=\"xs\">\n {localeLabel}\n </SectionLabel>\n {missing ? (\n <p className=\"mt-1 italic text-muted\">{labels.missingText}</p>\n ) : (\n <p lang={row.locale} className=\"mt-1 whitespace-pre-line break-words\">\n {row.text}\n </p>\n )}\n </div>\n </div>\n\n {changed && (\n <AlertBanner tone=\"warning\" variant=\"inline\" size=\"sm\" block live={false}>\n <div className=\"min-w-0\">\n <p>{labels.changedSince}</p>\n {changed.text !== row.text && <Then label={labels.reviewedWording} text={changed.text || \"—\"} />}\n {changed.referenceText != null && changed.referenceText !== row.reference && (\n <Then label={labels.reviewedReference} text={changed.referenceText} />\n )}\n </div>\n </AlertBanner>\n )}\n\n {row.placeholderMismatch && (\n <AlertBanner tone=\"danger\" variant=\"inline\" size=\"sm\" block live={false}>\n <span className=\"min-w-0 break-words\">\n {labels.placeholderMismatch(\n placeholderTokens(row.reference).join(\" \") || \"—\",\n placeholderTokens(row.text).join(\" \") || \"—\",\n )}\n </span>\n </AlertBanner>\n )}\n\n {review && (\n <Caption>\n {(review.verdict === \"APPROVED\" ? labels.lastApproved : labels.lastFlagged)(\n review.reviewerName ?? labels.erasedReviewer,\n date(review.reviewedAt),\n )}\n </Caption>\n )}\n\n {/* Read-only: what the reviewer said, as text — the fields are for saying it. */}\n {!editable && review?.verdict === \"NEEDS_CHANGE\" && (review.suggestion || review.note) && (\n <div className=\"space-y-2\">\n {review.suggestion && (\n <Then label={missing ? labels.translation : labels.suggestion} text={review.suggestion} />\n )}\n {review.note && <Then label={labels.note} text={review.note} />}\n </div>\n )}\n\n {editable && (\n <>\n <Textarea\n ref={wordingRef}\n // The panel's \"Needs a change\" swipe on an already open row finds it here (#416).\n data-review-wording=\"\"\n label={missing ? labels.translation : labels.suggestion}\n lang={row.locale}\n rows={Math.min(8, Math.max(2, Math.ceil(Math.max(row.text.length, row.reference.length) / 80)))}\n value={suggestion}\n disabled={busy !== null}\n onChange={(e) => setSuggestion(e.target.value)}\n />\n <Textarea\n label={labels.note}\n rows={2}\n value={note}\n placeholder={labels.notePlaceholder}\n disabled={busy !== null}\n onChange={(e) => setNote(e.target.value)}\n />\n <div className=\"flex flex-wrap gap-2\">\n {!missing && (\n <Button\n variant=\"default\"\n commit={COMMIT_EXCEPT_BILLING}\n pending={busy === \"approve\"}\n disabled={busy !== null}\n onClick={() => save(\"APPROVED\")}\n >\n {labels.approve}\n </Button>\n )}\n <Button\n variant={missing ? \"default\" : \"danger\"}\n commit={COMMIT_EXCEPT_BILLING}\n pending={busy === \"flag\"}\n disabled={busy !== null || !canFlag}\n onClick={() => save(\"NEEDS_CHANGE\")}\n >\n {missing ? labels.suggest : labels.flag}\n </Button>\n {review && onClear && (\n <Button\n variant=\"ghost\"\n commit={COMMIT_EXCEPT_BILLING}\n pending={busy === \"clear\"}\n disabled={busy !== null}\n onClick={() => void run(\"clear\", () => onClear({ locale: row.locale, key: row.key }))}\n >\n {labels.reset}\n </Button>\n )}\n {onClose && (\n <Button variant=\"ghost\" disabled={busy !== null} onClick={onClose}>\n {labels.cancel}\n </Button>\n )}\n </div>\n </>\n )}\n\n {failure && (\n <AlertBanner tone=\"danger\" size=\"sm\" role=\"alert\">\n {failure}\n </AlertBanner>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/translation-review-editor.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\n\nimport { useKitLabels, type LabelOverride } from \"../i18n/kit-labels\";\nimport { cn } from \"../lib/cn\";\nimport { useDayFormatter } from \"../lib/day-format\";\nimport { placeholderTokens, reviewWrite } from \"../lib/translation-review\";\nimport type {\n TranslationReviewKey,\n TranslationReviewWrite,\n TranslationRow,\n TranslationVerdict,\n} from \"../lib/translation-review\";\nimport { AlertBanner } from \"./alert-banner\";\nimport { Caption, SectionLabel } from \"./text\";\nimport { DEFAULT_TRANSLATION_REVIEW_LABELS } from \"./translation-review-labels\";\nimport type { TranslationReviewLabels } from \"./translation-review-labels\";\nimport { Button } from \"./button\";\nimport { Textarea } from \"./textarea\";\nimport { COMMIT_EXCEPT_BILLING } from \"./write-lock\";\n\n/** A callback may return a promise: resolve and the editor closes, reject and it stays\n * open with the error. */\ntype MaybePromise = void | Promise<unknown>;\n\nexport interface TranslationReviewEditorProps {\n row: TranslationRow;\n /** The reference language's own name (\"English\", \"Deutsch\") — the app's choice of\n * reference, so the app's word for it. */\n referenceLabel: ReactNode;\n /** The reviewed language's own name (\"Français\"). */\n localeLabel: ReactNode;\n /** Store a verdict. Left out (or `readOnly`): the editor shows the row and its last\n * verdict, with no fields and no actions. */\n onSave?: (write: TranslationReviewWrite) => MaybePromise;\n /** Clear the verdict — back to unreviewed. Left out: no reset button. */\n onClear?: (key: TranslationReviewKey) => MaybePromise;\n /** The Cancel button — and called after a save or a reset resolves, so the panel\n * that opened the editor closes it. */\n onClose?: () => void;\n /** Look, do not touch: a locale the viewer may read but not review. */\n readOnly?: boolean;\n /**\n * 0.25: put the cursor in the wording field when the editor opens, at the end of the\n * text. The panel sets it when a phone card is swiped toward \"Needs a change\" (keksdose\n * live #377): that swipe opens the editor to be written in, and a reviewer who has just\n * said \"this is wrong\" should not have to find the field first. Not for an editor\n * opened by a tap — that one may only be opened to read.\n */\n focusWording?: boolean;\n /** How the verdict's date reads. Default (0.52): the `<UiKitProvider formatDate>` when\n * the app set one (its date preference; the day sits in a sentence, so no weekday), else\n * the kit's `formatDate`, medium, in the provider's locale and `timeZone`. An empty\n * answer falls back to the kit's. */\n formatDate?: (iso: string) => string;\n /** Turns a rejection into words. Default {@link TranslationReviewLabels.failed}. */\n formatError?: (error: unknown) => ReactNode;\n className?: string;\n labels?: LabelOverride<TranslationReviewLabels>;\n}\n\n/** The wording a verdict was given on, under the line that says it changed. */\nfunction Then({ label, text }: { label: string; text: string }) {\n return (\n <div className=\"mt-1.5 min-w-0\">\n <span className=\"text-xs font-medium\">{label}</span>\n <p className=\"whitespace-pre-line break-words text-secondary\">{text}</p>\n </div>\n );\n}\n\n/**\n * One string, opened: the reference beside the translation, what changed since the last\n * verdict, what a machine can see is wrong (the placeholders), and the two things a\n * reviewer can say about it — keksdose's and kastlan's `translation-review-editor.tsx`.\n *\n * \"Better wording\" opens on the CURRENT text rather than empty, because the usual\n * correction is a word or an ending, and retyping a sentence to change one word is how\n * new mistakes get in. It is sent only with \"Needs a change\" — approving says the text is\n * fine as it stands. A \"Needs a change\" with neither a note nor a different wording tells\n * whoever fixes it nothing, so it takes one of the two.\n *\n * A MISSING string (kastlan: the locale lacks the key) cannot be approved — there is\n * nothing to approve. Its field starts empty and is the translation to add; the verdict\n * is NEEDS_CHANGE on the empty text, which the export hands a developer as the string to\n * add.\n *\n * Every action waits for its callback: the buttons hold while the promise is out, a\n * resolution closes the editor through `onClose`, and a rejection keeps it open with what\n * was typed and the error under the buttons.\n *\n * Each verdict is a `commit` exempt from billing (`COMMIT_EXCEPT_BILLING`, 0.33): a review\n * is an admin route, which a lapsed plan never locks (docs/billing-harmonization.md\n * §12.13, §12.36); a demo's lock still holds it.\n */\nexport function TranslationReviewEditor({\n row,\n referenceLabel,\n localeLabel,\n onSave,\n onClear,\n onClose,\n readOnly = false,\n focusWording = false,\n formatDate: formatDateProp,\n formatError,\n className,\n labels: labelsProp,\n}: TranslationReviewEditorProps) {\n const labels = useKitLabels(\"translationReview\", DEFAULT_TRANSLATION_REVIEW_LABELS, labelsProp);\n const date = useDayFormatter(formatDateProp);\n const review = row.review;\n const missing = row.text === \"\";\n const editable = !readOnly && onSave !== undefined;\n const [suggestion, setSuggestion] = useState(review?.suggestion ?? row.text);\n const [note, setNote] = useState(review?.note ?? \"\");\n const [busy, setBusy] = useState<\"approve\" | \"flag\" | \"clear\" | null>(null);\n const [failure, setFailure] = useState<ReactNode>(null);\n const wordingRef = useRef<HTMLTextAreaElement>(null);\n // On mount only: the prop says how the editor was OPENED, not where focus belongs\n // for the rest of its life.\n const focusOnOpen = useRef(focusWording && editable);\n useEffect(() => {\n const field = wordingRef.current;\n if (!focusOnOpen.current || !field) return;\n field.focus();\n field.setSelectionRange(field.value.length, field.value.length);\n }, []);\n\n const canFlag = note.trim() !== \"\" || suggestion.trim() !== row.text.trim();\n const changed = row.status === \"changed\" ? review : null;\n\n const run = async (kind: \"approve\" | \"flag\" | \"clear\", action: () => MaybePromise) => {\n setBusy(kind);\n setFailure(null);\n try {\n await action();\n onClose?.();\n } catch (caught) {\n setFailure(formatError ? formatError(caught) : labels.failed);\n } finally {\n setBusy(null);\n }\n };\n\n const save = (verdict: TranslationVerdict) => {\n if (!onSave) return;\n const trimmedNote = note.trim() || null;\n // Approving says the text is right as it stands: there is no wording to suggest.\n const sent = verdict === \"APPROVED\" ? null : suggestion.trim() || null;\n void run(verdict === \"APPROVED\" ? \"approve\" : \"flag\", () => onSave(reviewWrite(row, verdict, trimmedNote, sent)));\n };\n\n return (\n <div className={cn(\"min-w-0 space-y-3 text-sm\", className)}>\n <code className=\"block break-all text-xs text-muted\">{row.key}</code>\n\n <div className=\"grid grid-cols-1 gap-3 md:grid-cols-2\">\n <div className=\"min-w-0\">\n <SectionLabel as=\"span\" size=\"xs\">\n {referenceLabel}\n </SectionLabel>\n <p className=\"mt-1 whitespace-pre-line break-words text-secondary\">{row.reference}</p>\n </div>\n <div className=\"min-w-0\">\n <SectionLabel as=\"span\" size=\"xs\">\n {localeLabel}\n </SectionLabel>\n {missing ? (\n <p className=\"mt-1 italic text-muted\">{labels.missingText}</p>\n ) : (\n <p lang={row.locale} className=\"mt-1 whitespace-pre-line break-words\">\n {row.text}\n </p>\n )}\n </div>\n </div>\n\n {changed && (\n <AlertBanner tone=\"warning\" variant=\"inline\" size=\"sm\" block live={false}>\n <div className=\"min-w-0\">\n <p>{labels.changedSince}</p>\n {changed.text !== row.text && <Then label={labels.reviewedWording} text={changed.text || \"—\"} />}\n {changed.referenceText != null && changed.referenceText !== row.reference && (\n <Then label={labels.reviewedReference} text={changed.referenceText} />\n )}\n </div>\n </AlertBanner>\n )}\n\n {row.placeholderMismatch && (\n <AlertBanner tone=\"danger\" variant=\"inline\" size=\"sm\" block live={false}>\n <span className=\"min-w-0 break-words\">\n {labels.placeholderMismatch(\n placeholderTokens(row.reference).join(\" \") || \"—\",\n placeholderTokens(row.text).join(\" \") || \"—\",\n )}\n </span>\n </AlertBanner>\n )}\n\n {review && (\n <Caption>\n {(review.verdict === \"APPROVED\" ? labels.lastApproved : labels.lastFlagged)(\n review.reviewerName ?? labels.erasedReviewer,\n date(review.reviewedAt),\n )}\n </Caption>\n )}\n\n {/* Read-only: what the reviewer said, as text — the fields are for saying it. */}\n {!editable && review?.verdict === \"NEEDS_CHANGE\" && (review.suggestion || review.note) && (\n <div className=\"space-y-2\">\n {review.suggestion && (\n <Then label={missing ? labels.translation : labels.suggestion} text={review.suggestion} />\n )}\n {review.note && <Then label={labels.note} text={review.note} />}\n </div>\n )}\n\n {editable && (\n <>\n <Textarea\n ref={wordingRef}\n // The panel's \"Needs a change\" swipe on an already open row finds it here (#416).\n data-review-wording=\"\"\n label={missing ? labels.translation : labels.suggestion}\n lang={row.locale}\n rows={Math.min(8, Math.max(2, Math.ceil(Math.max(row.text.length, row.reference.length) / 80)))}\n value={suggestion}\n disabled={busy !== null}\n onChange={(e) => setSuggestion(e.target.value)}\n />\n <Textarea\n label={labels.note}\n rows={2}\n value={note}\n placeholder={labels.notePlaceholder}\n disabled={busy !== null}\n onChange={(e) => setNote(e.target.value)}\n />\n <div className=\"flex flex-wrap gap-2\">\n {!missing && (\n <Button\n variant=\"default\"\n commit={COMMIT_EXCEPT_BILLING}\n pending={busy === \"approve\"}\n disabled={busy !== null}\n onClick={() => save(\"APPROVED\")}\n >\n {labels.approve}\n </Button>\n )}\n <Button\n variant={missing ? \"default\" : \"danger\"}\n commit={COMMIT_EXCEPT_BILLING}\n pending={busy === \"flag\"}\n disabled={busy !== null || !canFlag}\n onClick={() => save(\"NEEDS_CHANGE\")}\n >\n {missing ? labels.suggest : labels.flag}\n </Button>\n {review && onClear && (\n <Button\n variant=\"ghost\"\n commit={COMMIT_EXCEPT_BILLING}\n pending={busy === \"clear\"}\n disabled={busy !== null}\n onClick={() => void run(\"clear\", () => onClear({ locale: row.locale, key: row.key }))}\n >\n {labels.reset}\n </Button>\n )}\n {onClose && (\n <Button variant=\"ghost\" disabled={busy !== null} onClick={onClose}>\n {labels.cancel}\n </Button>\n )}\n </div>\n </>\n )}\n\n {failure && (\n <AlertBanner tone=\"danger\" size=\"sm\" role=\"alert\">\n {failure}\n </AlertBanner>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -115,7 +115,8 @@ export interface TranslationReviewPanelProps {
115
115
  * kit's, which an app listing what it stores had to list as its own (KS U4).
116
116
  */
117
117
  storageKeyPrefix?: string;
118
- /** How a verdict's date reads in the editor. */
118
+ /** How a verdict's date reads in the editor. Default: the editor's — the
119
+ * `<UiKitProvider formatDate>`, else the kit's medium date (0.52). */
119
120
  formatDate?: (iso: string) => string;
120
121
  /** Turns a callback's rejection into words. Default {@link TranslationReviewLabels.failed}. */
121
122
  formatError?: (error: unknown) => ReactNode;
@@ -184,8 +184,8 @@ function TranslationReviewPanel({
184
184
  const mismatches = useMemo(() => rows.filter((r) => r.placeholderMismatch).length, [rows]);
185
185
  const { status, query, placeholdersOnly } = filter;
186
186
  const visible = useMemo(
187
- () => filterTranslationRows(rows, { status, source, namespace, query, placeholdersOnly }),
188
- [rows, status, source, namespace, query, placeholdersOnly]
187
+ () => filterTranslationRows(rows, { status, source, namespace, query, placeholdersOnly }, locale),
188
+ [rows, status, source, namespace, query, placeholdersOnly, locale]
189
189
  );
190
190
  const groups = useMemo(() => {
191
191
  if (groupBy === void 0) return null;