@eifi1/ui-kit 0.48.2 → 0.50.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 (377) hide show
  1. package/README.md +34 -16
  2. package/dist/admin/admin-action-confirm.js +1 -1
  3. package/dist/admin/admin-action-confirm.js.map +1 -1
  4. package/dist/admin/invitations-panel.js +5 -1
  5. package/dist/admin/invitations-panel.js.map +1 -1
  6. package/dist/admin/user-roster.d.ts +24 -12
  7. package/dist/admin/user-roster.js +21 -6
  8. package/dist/admin/user-roster.js.map +1 -1
  9. package/dist/auth/accept-invitation.js +2 -2
  10. package/dist/auth/accept-invitation.js.map +1 -1
  11. package/dist/auth/not-found-page.js +1 -1
  12. package/dist/auth/not-found-page.js.map +1 -1
  13. package/dist/auth/reset-password-form.js +2 -2
  14. package/dist/auth/reset-password-form.js.map +1 -1
  15. package/dist/auth/verify-email.d.ts +9 -1
  16. package/dist/auth/verify-email.js +5 -3
  17. package/dist/auth/verify-email.js.map +1 -1
  18. package/dist/billing/plan-card.js +2 -2
  19. package/dist/billing/plan-card.js.map +1 -1
  20. package/dist/components/amount-input.d.ts +2 -1
  21. package/dist/components/amount-input.js +3 -2
  22. package/dist/components/amount-input.js.map +1 -1
  23. package/dist/components/autocomplete.js +1 -0
  24. package/dist/components/autocomplete.js.map +1 -1
  25. package/dist/components/breadcrumbs.d.ts +19 -1
  26. package/dist/components/breadcrumbs.js +11 -4
  27. package/dist/components/breadcrumbs.js.map +1 -1
  28. package/dist/components/button-look.d.ts +37 -7
  29. package/dist/components/button-look.js +33 -6
  30. package/dist/components/button-look.js.map +1 -1
  31. package/dist/components/button.d.ts +4 -1
  32. package/dist/components/button.js +2 -2
  33. package/dist/components/button.js.map +1 -1
  34. package/dist/components/calendar-heatmap.js +2 -1
  35. package/dist/components/calendar-heatmap.js.map +1 -1
  36. package/dist/components/chart-edge.d.ts +10 -0
  37. package/dist/components/chart-edge.js +57 -0
  38. package/dist/components/chart-edge.js.map +1 -0
  39. package/dist/components/chart.js +2 -1
  40. package/dist/components/chart.js.map +1 -1
  41. package/dist/components/chip.d.ts +8 -1
  42. package/dist/components/chip.js +4 -1
  43. package/dist/components/chip.js.map +1 -1
  44. package/dist/components/combobox-core.js +1 -0
  45. package/dist/components/combobox-core.js.map +1 -1
  46. package/dist/components/combobox-suggestions.js +1 -0
  47. package/dist/components/combobox-suggestions.js.map +1 -1
  48. package/dist/components/confirm-context.d.ts +5 -0
  49. package/dist/components/confirm-context.js +7 -0
  50. package/dist/components/confirm-context.js.map +1 -0
  51. package/dist/components/confirm-dialog.js +3 -3
  52. package/dist/components/confirm-dialog.js.map +1 -1
  53. package/dist/components/currency-select.d.ts +28 -2
  54. package/dist/components/currency-select.js +56 -10
  55. package/dist/components/currency-select.js.map +1 -1
  56. package/dist/components/danger-confirm.d.ts +2 -2
  57. package/dist/components/danger-confirm.js +1 -1
  58. package/dist/components/danger-confirm.js.map +1 -1
  59. package/dist/components/data-table-fit.js +21 -3
  60. package/dist/components/data-table-fit.js.map +1 -1
  61. package/dist/components/data-table-mobile.d.ts +58 -3
  62. package/dist/components/data-table-mobile.js +147 -16
  63. package/dist/components/data-table-mobile.js.map +1 -1
  64. package/dist/components/data-table-row-actions.d.ts +2 -1
  65. package/dist/components/data-table-row-actions.js +7 -0
  66. package/dist/components/data-table-row-actions.js.map +1 -1
  67. package/dist/components/data-table-row.d.ts +1 -1
  68. package/dist/components/data-table-row.js.map +1 -1
  69. package/dist/components/data-table.d.ts +34 -1
  70. package/dist/components/data-table.js +37 -10
  71. package/dist/components/data-table.js.map +1 -1
  72. package/dist/components/dropdown.js +1 -0
  73. package/dist/components/dropdown.js.map +1 -1
  74. package/dist/components/error-boundary.js +2 -2
  75. package/dist/components/error-boundary.js.map +1 -1
  76. package/dist/components/floating-panel.d.ts +36 -13
  77. package/dist/components/floating-panel.js +27 -7
  78. package/dist/components/floating-panel.js.map +1 -1
  79. package/dist/components/form-actions.d.ts +1 -1
  80. package/dist/components/form-actions.js +1 -1
  81. package/dist/components/form-actions.js.map +1 -1
  82. package/dist/components/full-bleed-dialog.js +1 -0
  83. package/dist/components/full-bleed-dialog.js.map +1 -1
  84. package/dist/components/icon-button.d.ts +11 -0
  85. package/dist/components/icon-button.js +7 -5
  86. package/dist/components/icon-button.js.map +1 -1
  87. package/dist/components/modal.js +1 -0
  88. package/dist/components/modal.js.map +1 -1
  89. package/dist/components/nav-pills.d.ts +8 -1
  90. package/dist/components/nav-pills.js +61 -38
  91. package/dist/components/nav-pills.js.map +1 -1
  92. package/dist/components/number-input.d.ts +2 -1
  93. package/dist/components/number-input.js +6 -2
  94. package/dist/components/number-input.js.map +1 -1
  95. package/dist/components/numpad-sheet.js +2 -0
  96. package/dist/components/numpad-sheet.js.map +1 -1
  97. package/dist/components/page-header-context.d.ts +8 -0
  98. package/dist/components/page-header-context.js +7 -0
  99. package/dist/components/page-header-context.js.map +1 -0
  100. package/dist/components/page-header.d.ts +14 -2
  101. package/dist/components/page-header.js +10 -1
  102. package/dist/components/page-header.js.map +1 -1
  103. package/dist/components/picker-sheet.js +2 -0
  104. package/dist/components/picker-sheet.js.map +1 -1
  105. package/dist/components/pie-chart.js +2 -1
  106. package/dist/components/pie-chart.js.map +1 -1
  107. package/dist/components/popover.js +1 -0
  108. package/dist/components/popover.js.map +1 -1
  109. package/dist/components/progress-bar.js +5 -4
  110. package/dist/components/progress-bar.js.map +1 -1
  111. package/dist/components/reauth-dialog.js +1 -1
  112. package/dist/components/reauth-dialog.js.map +1 -1
  113. package/dist/components/row-actions-labels.js +3 -1
  114. package/dist/components/row-actions-labels.js.map +1 -1
  115. package/dist/components/row-actions.d.ts +43 -9
  116. package/dist/components/row-actions.js +78 -15
  117. package/dist/components/row-actions.js.map +1 -1
  118. package/dist/components/section-nav.d.ts +37 -0
  119. package/dist/components/section-nav.js +33 -0
  120. package/dist/components/section-nav.js.map +1 -0
  121. package/dist/components/series-chart.js +8 -3
  122. package/dist/components/series-chart.js.map +1 -1
  123. package/dist/components/share-card.js +1 -1
  124. package/dist/components/share-card.js.map +1 -1
  125. package/dist/components/signature-pad.js +1 -1
  126. package/dist/components/signature-pad.js.map +1 -1
  127. package/dist/components/sparkline.js +2 -1
  128. package/dist/components/sparkline.js.map +1 -1
  129. package/dist/components/stat-tile.js +14 -7
  130. package/dist/components/stat-tile.js.map +1 -1
  131. package/dist/components/status-dot.d.ts +5 -1
  132. package/dist/components/status-dot.js +4 -1
  133. package/dist/components/status-dot.js.map +1 -1
  134. package/dist/components/tooltip.js +1 -1
  135. package/dist/components/tooltip.js.map +1 -1
  136. package/dist/components/translation-review-editor.js +2 -2
  137. package/dist/components/translation-review-editor.js.map +1 -1
  138. package/dist/components/translation-review.js +2 -2
  139. package/dist/components/translation-review.js.map +1 -1
  140. package/dist/components/treemap.d.ts +5 -2
  141. package/dist/components/treemap.js +26 -6
  142. package/dist/components/treemap.js.map +1 -1
  143. package/dist/components/use-table-state.js +37 -6
  144. package/dist/components/use-table-state.js.map +1 -1
  145. package/dist/data-table.js.map +1 -1
  146. package/dist/demo/demo-banner.js +1 -1
  147. package/dist/demo/demo-banner.js.map +1 -1
  148. package/dist/demo/demo-ended.js +1 -1
  149. package/dist/demo/demo-ended.js.map +1 -1
  150. package/dist/demo/demo-start.js +1 -1
  151. package/dist/demo/demo-start.js.map +1 -1
  152. package/dist/eslint/index.js +1 -1
  153. package/dist/feedback/feedback-inbox.js +1 -1
  154. package/dist/feedback/feedback-inbox.js.map +1 -1
  155. package/dist/hooks/use-dismiss.d.ts +9 -1
  156. package/dist/hooks/use-dismiss.js +25 -5
  157. package/dist/hooks/use-dismiss.js.map +1 -1
  158. package/dist/hooks/use-document-title.d.ts +14 -0
  159. package/dist/hooks/use-document-title.js +22 -0
  160. package/dist/hooks/use-document-title.js.map +1 -0
  161. package/dist/i18n/defaults.js +2 -0
  162. package/dist/i18n/defaults.js.map +1 -1
  163. package/dist/i18n/german.js +20 -3
  164. package/dist/i18n/german.js.map +1 -1
  165. package/dist/i18n/kit-labels.d.ts +34 -1
  166. package/dist/i18n/kit-labels.js +43 -2
  167. package/dist/i18n/kit-labels.js.map +1 -1
  168. package/dist/i18n/locales/en.js +22 -3
  169. package/dist/i18n/locales/en.js.map +3 -3
  170. package/dist/i18n/locales/es.js +20 -3
  171. package/dist/i18n/locales/es.js.map +1 -1
  172. package/dist/i18n/locales/fr.js +20 -3
  173. package/dist/i18n/locales/fr.js.map +1 -1
  174. package/dist/i18n/locales/hu.js +20 -3
  175. package/dist/i18n/locales/hu.js.map +1 -1
  176. package/dist/i18n/locales/it.js +20 -3
  177. package/dist/i18n/locales/it.js.map +1 -1
  178. package/dist/i18n/locales/zh.js +20 -3
  179. package/dist/i18n/locales/zh.js.map +1 -1
  180. package/dist/i18n/review.js +3 -0
  181. package/dist/i18n/review.js.map +1 -1
  182. package/dist/i18n/shared-labels.js +1 -0
  183. package/dist/i18n/shared-labels.js.map +1 -1
  184. package/dist/index.d.ts +8 -1
  185. package/dist/index.js +11 -0
  186. package/dist/index.js.map +1 -1
  187. package/dist/landing/landing-actions.js +2 -2
  188. package/dist/landing/landing-actions.js.map +1 -1
  189. package/dist/landing/public-header.js +2 -2
  190. package/dist/landing/public-header.js.map +1 -1
  191. package/dist/lib/clipboard-minus.d.ts +27 -0
  192. package/dist/lib/clipboard-minus.js +63 -0
  193. package/dist/lib/clipboard-minus.js.map +1 -0
  194. package/dist/lib/data-attributes.d.ts +11 -0
  195. package/dist/lib/data-attributes.js +10 -0
  196. package/dist/lib/data-attributes.js.map +1 -0
  197. package/dist/lib/document-title.d.ts +17 -0
  198. package/dist/lib/document-title.js +29 -0
  199. package/dist/lib/document-title.js.map +1 -0
  200. package/dist/lib/float-clearance.js +8 -2
  201. package/dist/lib/float-clearance.js.map +1 -1
  202. package/dist/lib/format.d.ts +36 -10
  203. package/dist/lib/format.js +57 -11
  204. package/dist/lib/format.js.map +1 -1
  205. package/dist/lib/minus.d.ts +32 -0
  206. package/dist/lib/minus.js +14 -0
  207. package/dist/lib/minus.js.map +1 -0
  208. package/dist/lib/status-tone.d.ts +30 -0
  209. package/dist/lib/status-tone.js +17 -0
  210. package/dist/lib/status-tone.js.map +1 -0
  211. package/dist/settings/settings-layout.js +3 -2
  212. package/dist/settings/settings-layout.js.map +1 -1
  213. package/dist/shell/account-menu-language.d.ts +1 -1
  214. package/dist/shell/account-menu-language.js +9 -1
  215. package/dist/shell/account-menu-language.js.map +1 -1
  216. package/dist/shell/app-shell-labels.js +2 -1
  217. package/dist/shell/app-shell-labels.js.map +1 -1
  218. package/dist/shell/app-shell.d.ts +58 -16
  219. package/dist/shell/app-shell.js +151 -43
  220. package/dist/shell/app-shell.js.map +1 -1
  221. package/dist/shell/language-code.d.ts +3 -0
  222. package/dist/shell/language-code.js +8 -0
  223. package/dist/shell/language-code.js.map +1 -0
  224. package/dist/shell/topbar-controls.d.ts +14 -6
  225. package/dist/shell/topbar-controls.js +14 -17
  226. package/dist/shell/topbar-controls.js.map +1 -1
  227. package/dist/testing/css-variables.generated.d.ts +1 -1
  228. package/dist/testing/css-variables.generated.js +1 -1
  229. package/dist/testing/css-variables.generated.js.map +1 -1
  230. package/dist/testing/react.d.ts +25 -0
  231. package/dist/testing/react.js +19 -0
  232. package/dist/testing/react.js.map +1 -0
  233. package/dist/theme/chart-palette.d.ts +82 -15
  234. package/dist/theme/chart-palette.js +51 -24
  235. package/dist/theme/chart-palette.js.map +1 -1
  236. package/dist/theme/palette-presets.d.ts +9 -0
  237. package/dist/theme/palette-presets.js +77 -62
  238. package/dist/theme/palette-presets.js.map +1 -1
  239. package/dist/tour/tour-dock.d.ts +42 -0
  240. package/dist/tour/tour-dock.js +80 -0
  241. package/dist/tour/tour-dock.js.map +1 -0
  242. package/dist/tour/tour-gates.d.ts +71 -0
  243. package/dist/tour/tour-gates.js +114 -0
  244. package/dist/tour/tour-gates.js.map +1 -0
  245. package/dist/tour/tour-guide-button.d.ts +27 -0
  246. package/dist/tour/tour-guide-button.js +45 -0
  247. package/dist/tour/tour-guide-button.js.map +1 -0
  248. package/dist/tour/tour-guide-labels.d.ts +8 -0
  249. package/dist/tour/tour-guide-labels.js +9 -0
  250. package/dist/tour/tour-guide-labels.js.map +1 -0
  251. package/dist/tour/tour-labels.js +9 -1
  252. package/dist/tour/tour-labels.js.map +1 -1
  253. package/dist/tour/tour.d.ts +133 -9
  254. package/dist/tour/tour.js +594 -70
  255. package/dist/tour/tour.js.map +1 -1
  256. package/dist/tour/use-tour-url-sync.d.ts +5 -2
  257. package/dist/tour/use-tour-url-sync.js +7 -1
  258. package/dist/tour/use-tour-url-sync.js.map +1 -1
  259. package/dist/tour.d.ts +2 -0
  260. package/dist/tour.js +3 -0
  261. package/dist/tour.js.map +1 -1
  262. package/dist/wizard/stepper-nav.d.ts +1 -1
  263. package/dist/wizard/stepper-nav.js +18 -5
  264. package/dist/wizard/stepper-nav.js.map +1 -1
  265. package/dist/wizard/types.d.ts +11 -0
  266. package/dist/wizard/types.js.map +1 -1
  267. package/dist/wizard/use-wizard.js +11 -5
  268. package/dist/wizard/use-wizard.js.map +1 -1
  269. package/package.json +9 -1
  270. package/src/admin/admin-action-confirm.tsx +1 -1
  271. package/src/admin/invitations-panel.tsx +7 -3
  272. package/src/admin/user-roster.tsx +41 -14
  273. package/src/auth/accept-invitation.tsx +3 -3
  274. package/src/auth/not-found-page.tsx +1 -1
  275. package/src/auth/reset-password-form.tsx +3 -3
  276. package/src/auth/verify-email.tsx +13 -3
  277. package/src/billing/plan-card.tsx +2 -2
  278. package/src/components/amount-input.tsx +5 -4
  279. package/src/components/autocomplete.tsx +2 -0
  280. package/src/components/breadcrumbs.tsx +32 -4
  281. package/src/components/button-look.ts +86 -15
  282. package/src/components/button.tsx +6 -3
  283. package/src/components/calendar-heatmap.tsx +2 -1
  284. package/src/components/chart-edge.ts +73 -0
  285. package/src/components/chart.tsx +3 -1
  286. package/src/components/chip.tsx +10 -1
  287. package/src/components/combobox-core.tsx +2 -0
  288. package/src/components/combobox-suggestions.tsx +2 -0
  289. package/src/components/confirm-context.ts +7 -0
  290. package/src/components/confirm-dialog.tsx +3 -3
  291. package/src/components/currency-select.tsx +95 -6
  292. package/src/components/danger-confirm.tsx +3 -3
  293. package/src/components/data-table-fit.ts +41 -6
  294. package/src/components/data-table-mobile.tsx +259 -17
  295. package/src/components/data-table-row-actions.tsx +9 -1
  296. package/src/components/data-table-row.tsx +3 -0
  297. package/src/components/data-table.tsx +91 -19
  298. package/src/components/dropdown.tsx +2 -0
  299. package/src/components/error-boundary.tsx +2 -2
  300. package/src/components/floating-panel.tsx +67 -16
  301. package/src/components/form-actions.tsx +2 -2
  302. package/src/components/full-bleed-dialog.tsx +3 -0
  303. package/src/components/icon-button.tsx +21 -8
  304. package/src/components/modal.tsx +3 -0
  305. package/src/components/nav-pills.tsx +29 -2
  306. package/src/components/number-input.tsx +8 -3
  307. package/src/components/numpad-sheet.tsx +4 -0
  308. package/src/components/page-header-context.ts +8 -0
  309. package/src/components/page-header.tsx +34 -2
  310. package/src/components/picker-sheet.tsx +4 -0
  311. package/src/components/pie-chart.tsx +2 -1
  312. package/src/components/popover.tsx +2 -0
  313. package/src/components/progress-bar.tsx +6 -3
  314. package/src/components/reauth-dialog.tsx +1 -1
  315. package/src/components/row-actions-labels.ts +2 -0
  316. package/src/components/row-actions.tsx +154 -33
  317. package/src/components/section-nav.tsx +65 -0
  318. package/src/components/series-chart.tsx +12 -3
  319. package/src/components/share-card.tsx +1 -1
  320. package/src/components/signature-pad.tsx +1 -1
  321. package/src/components/sparkline.tsx +2 -1
  322. package/src/components/stat-tile.tsx +19 -7
  323. package/src/components/status-dot.tsx +7 -1
  324. package/src/components/tooltip.tsx +3 -1
  325. package/src/components/translation-review-editor.tsx +2 -2
  326. package/src/components/translation-review.tsx +2 -2
  327. package/src/components/treemap.tsx +34 -8
  328. package/src/components/use-table-state.ts +63 -8
  329. package/src/data-table.ts +3 -3
  330. package/src/demo/demo-banner.tsx +1 -1
  331. package/src/demo/demo-ended.tsx +1 -1
  332. package/src/demo/demo-start.tsx +1 -1
  333. package/src/feedback/feedback-inbox.tsx +1 -1
  334. package/src/hooks/use-dismiss.ts +43 -4
  335. package/src/hooks/use-document-title.ts +31 -0
  336. package/src/i18n/defaults.ts +2 -0
  337. package/src/i18n/german.ts +17 -0
  338. package/src/i18n/kit-labels.tsx +79 -1
  339. package/src/i18n/locales/es.ts +17 -0
  340. package/src/i18n/locales/fr.ts +17 -0
  341. package/src/i18n/locales/hu.ts +17 -0
  342. package/src/i18n/locales/it.ts +17 -0
  343. package/src/i18n/locales/zh.ts +17 -0
  344. package/src/i18n/review.ts +3 -0
  345. package/src/i18n/shared-labels.ts +1 -0
  346. package/src/index.ts +13 -0
  347. package/src/landing/landing-actions.tsx +2 -2
  348. package/src/landing/public-header.tsx +2 -2
  349. package/src/lib/clipboard-minus.ts +94 -0
  350. package/src/lib/data-attributes.ts +14 -0
  351. package/src/lib/document-title.ts +41 -0
  352. package/src/lib/float-clearance.ts +19 -3
  353. package/src/lib/format.ts +117 -30
  354. package/src/lib/minus.ts +50 -0
  355. package/src/lib/status-tone.ts +43 -0
  356. package/src/settings/settings-layout.tsx +3 -2
  357. package/src/shell/account-menu-language.tsx +14 -2
  358. package/src/shell/app-shell-labels.ts +1 -0
  359. package/src/shell/app-shell.tsx +291 -68
  360. package/src/shell/language-code.ts +5 -0
  361. package/src/shell/topbar-controls.tsx +35 -15
  362. package/src/testing/css-variables.generated.ts +6 -6
  363. package/src/testing/react.ts +59 -0
  364. package/src/theme/chart-palette.ts +102 -20
  365. package/src/theme/palette-presets.ts +121 -84
  366. package/src/tour/tour-dock.ts +132 -0
  367. package/src/tour/tour-gates.ts +195 -0
  368. package/src/tour/tour-guide-button.tsx +64 -0
  369. package/src/tour/tour-guide-labels.ts +15 -0
  370. package/src/tour/tour-labels.ts +8 -0
  371. package/src/tour/tour.tsx +949 -86
  372. package/src/tour/use-tour-url-sync.ts +14 -4
  373. package/src/tour.ts +3 -0
  374. package/src/wizard/stepper-nav.tsx +27 -7
  375. package/src/wizard/types.ts +12 -0
  376. package/src/wizard/use-wizard.ts +16 -9
  377. package/tokens.css +28 -18
@@ -12,12 +12,14 @@ import { useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
12
12
  import type { ComponentProps, ComponentPropsWithoutRef, KeyboardEvent, ReactNode } from "react";
13
13
  import { Treemap as RechartsTreemap } from "recharts";
14
14
  import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "./chart";
15
- import { PALETTE_HEX, textOn } from "../theme/chart-palette";
15
+ import { PALETTE_HEX, PALETTE_SIZE, textOn } from "../theme/chart-palette";
16
+ import { resolveChartColor, useChartEdge } from "./chart-edge";
16
17
  import { parseHex } from "../theme/color";
17
18
  import { dirOf, type Direction } from "../lib/direction";
18
19
  import { cn } from "../lib/cn";
19
20
  import { useTextSize } from "../theme/text-size";
20
21
  import { chartHeightProps, type ChartHeight } from "./chart-height";
22
+ import { formatWithMinus } from "../lib/minus";
21
23
 
22
24
  /**
23
25
  * One tile.
@@ -39,7 +41,9 @@ export interface TreemapNode {
39
41
  value: number;
40
42
  /** Overrides the by-index palette colour for this tile — e.g. to paint every child
41
43
  * in its parent group's colour, so the groups stay legible in one picture. A
42
- * concrete `#rrggbb` or a bare `var(--chart-N)` keeps the label ink measured;
44
+ * concrete `#rrggbb` or a bare `var(--chart-N)` keeps the label ink measured — so
45
+ * does `CHART_ROLES.other` (`var(--chart-muted)`), the colour for a "rest" bucket,
46
+ * which should not wrap round to a hue the reader matches to a real node;
43
47
  * anything else (another var, a `color-mix()`) gets `currentColor` ink, because
44
48
  * there is nothing to measure — set `labelColor` then. */
45
49
  fill?: string;
@@ -101,7 +105,8 @@ export interface TreemapProps extends Omit<ComponentPropsWithoutRef<"div">, "chi
101
105
  maxTiles?: number;
102
106
  /**
103
107
  * The categorical ramp tiles take by index, as concrete hex. Default: the page's
104
- * live `--chart-1…9` tokens (see `useChartTokenHex`). Pass one when the host
108
+ * live `--chart-1…8` tokens (see `useChartTokenHex`), cycled after eight like
109
+ * `paletteFor` (0.49). Pass one when the host
105
110
  * already holds the active palette — e.g. `createPaletteStore`'s `useChartHex()` —
106
111
  * so a caller colouring a legend beside the map is guaranteed the same list.
107
112
  */
@@ -119,7 +124,7 @@ export interface TreemapProps extends Omit<ComponentPropsWithoutRef<"div">, "chi
119
124
  // re-read whenever <html>'s class or style changes — which is exactly when a theme
120
125
  // toggle or a palette switch lands.
121
126
 
122
- const CHART_TOKENS = 9;
127
+ const CHART_TOKENS = PALETTE_SIZE;
123
128
 
124
129
  /** The resolved ramp, joined into ONE string so `useSyncExternalStore` compares a
125
130
  * primitive: a fresh array per read would never be `Object.is`-equal to the last and
@@ -163,9 +168,12 @@ const CHART_TOKEN_REF = /^var\(\s*--chart-(\d+)\s*\)$/;
163
168
  function measurableFill(fill: string, tokens: readonly string[]): string | null {
164
169
  if (parseHex(fill)) return fill;
165
170
  const m = CHART_TOKEN_REF.exec(fill.trim());
166
- if (!m) return null;
167
- const hex = tokens[Number(m[1]) - 1];
168
- return hex && parseHex(hex) ? hex : null;
171
+ const hex = m ? tokens[Number(m[1]) - 1] : undefined;
172
+ if (hex && parseHex(hex)) return hex;
173
+ // Any other bare token the page declares — `var(--chart-muted)` for a rest bucket,
174
+ // `var(--chart-9)`, a money colour — read off <html> as it resolves.
175
+ if (typeof document === "undefined") return null;
176
+ return resolveChartColor(fill, getComputedStyle(document.documentElement));
169
177
  }
170
178
 
171
179
  // ── Tile ──────────────────────────────────────────────────────────────────
@@ -241,6 +249,7 @@ export function TreemapCell({
241
249
  // Before the early return: a hook has to run on every render of the cell.
242
250
  const tokenHex = useChartTokenHex();
243
251
  const { scale } = useTextSize();
252
+ const edgeFor = useChartEdge();
244
253
  // Depth 0 is recharts' synthetic root, which spans the whole chart.
245
254
  if (!depth) return null;
246
255
  // Whole pixels: a rect on a half pixel renders its edge across two half-lit
@@ -252,6 +261,9 @@ export function TreemapCell({
252
261
  const tileFill = fill ?? "var(--chart-1)";
253
262
  const measured = measurableFill(tileFill, tokenHex);
254
263
  const ink = labelColor ?? (measured ? textOn(measured) : "currentColor");
264
+ // A tile in a hue under 3:1 against the card gets a 1px ink edge inside the gutter
265
+ // (WCAG 1.4.11, anyplot's rule); a hue that clears it stays bare.
266
+ const edge = measured ? edgeFor(measured) : undefined;
255
267
  // The halo only on an OPAQUE fill we could measure. A translucent fill (a
256
268
  // `color-mix(… transparent)`) composited twice — once as the tile, once as the halo
257
269
  // over it — draws a darker ring round every glyph, which read as doubled text on the
@@ -310,6 +322,20 @@ export function TreemapCell({
310
322
  strokeWidth={2}
311
323
  rx={3}
312
324
  />
325
+ {edge && pw > 3 && ph > 3 && (
326
+ <rect
327
+ data-edge=""
328
+ x={px + 1.5}
329
+ y={py + 1.5}
330
+ width={pw - 3}
331
+ height={ph - 3}
332
+ fill="none"
333
+ stroke={edge}
334
+ strokeWidth={1}
335
+ rx={2}
336
+ style={{ pointerEvents: "none" }}
337
+ />
338
+ )}
313
339
  {label != null && (
314
340
  <text
315
341
  // A plain attribute, so the host's `[data-private]` rule reaches an SVG
@@ -437,7 +463,7 @@ export function Treemap({
437
463
  const total = nodes.reduce((acc, n) => acc + n.size, 0);
438
464
  const format = (v: number): ReactNode => {
439
465
  const share = total > 0 ? v / total : undefined;
440
- return valueFormatter ? valueFormatter(v, share) : DEFAULT_NUMBER.format(v);
466
+ return valueFormatter ? valueFormatter(v, share) : formatWithMinus(DEFAULT_NUMBER, v);
441
467
  };
442
468
  const Content = redactNames ? RedactedTooltipContent : ChartTooltipContent;
443
469
  return (
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import type { SetStateAction } from "react";
3
+ import * as ReactRouter from "react-router";
3
4
  import { useLocation, useSearchParams } from "react-router";
4
5
  import { useKitTableStorageKeyPrefix } from "../i18n/kit-labels";
5
6
  import { readStored, writeStored } from "../lib/safe-storage";
@@ -103,6 +104,62 @@ function urlKeys(prefix = ""): UrlKeys {
103
104
  };
104
105
  }
105
106
 
107
+ /** What a table reads and writes the URL through: react-router's, or none. */
108
+ interface TableSearch {
109
+ searchParams: URLSearchParams;
110
+ setSearchParams: ReturnType<typeof useSearchParams>[1];
111
+ locationState: unknown;
112
+ }
113
+
114
+ function useRouterSearch(): TableSearch {
115
+ const [searchParams, setSearchParams] = useSearchParams();
116
+ const location = useLocation();
117
+ return { searchParams, setSearchParams, locationState: location.state };
118
+ }
119
+
120
+ const NO_ROUTER_SEARCH: TableSearch = {
121
+ searchParams: new URLSearchParams(),
122
+ setSearchParams: () => {},
123
+ locationState: null,
124
+ };
125
+ function useNoRouterSearch(): TableSearch {
126
+ return NO_ROUTER_SEARCH;
127
+ }
128
+
129
+ /**
130
+ * `useInRouterContext`, read off the namespace inside a `try` like
131
+ * use-search-param-state's probe: a test's `vi.mock("react-router", () => ({ … }))`
132
+ * without it throws on the read. Missing, a table assumes a Router, as before 0.49.
133
+ */
134
+ function readInRouterContext(): () => boolean {
135
+ try {
136
+ return (ReactRouter as unknown as { useInRouterContext?: () => boolean }).useInRouterContext ?? (() => true);
137
+ } catch {
138
+ return () => true;
139
+ }
140
+ }
141
+ const useInRouterContext = readInRouterContext();
142
+
143
+ /**
144
+ * The URL access a table needs (0.49, KIT-3). Without `urlSync` a table needs no Router:
145
+ * before 0.49 it called `useSearchParams()` unconditionally and threw outside one even
146
+ * with URL sync off. With `urlSync` and no Router, a development build throws a message
147
+ * that names the fix; a production build keeps the view in local state rather than
148
+ * crash the page. Whether a Router is around does not change during a table's life, so
149
+ * which hook runs is stable from render to render.
150
+ */
151
+ function useTableSearch(urlSync: boolean, owner: string): TableSearch {
152
+ const inRouter = useInRouterContext();
153
+ if (!inRouter && urlSync && import.meta.env?.DEV) {
154
+ throw new Error(
155
+ `${owner} urlSync requires a Router: render it inside a react-router Router ` +
156
+ "(BrowserRouter, a data router or a MemoryRouter), or leave urlSync off.",
157
+ );
158
+ }
159
+ const useSearch = inRouter ? useRouterSearch : useNoRouterSearch;
160
+ return useSearch();
161
+ }
162
+
106
163
  /** `urlSync` split into "is it on" and "under which prefix". */
107
164
  function resolveTableUrlSync(urlSync: TableUrlSync | undefined): { enabled: boolean; prefix: string } {
108
165
  if (!urlSync) return { enabled: false, prefix: "" };
@@ -295,10 +352,9 @@ export function useTableState<T>({
295
352
  }) {
296
353
  const { enabled: urlSync, prefix: urlPrefix } = resolveTableUrlSync(urlSyncOption);
297
354
  const keys = useMemo(() => urlKeys(urlPrefix), [urlPrefix]);
298
- // Called unconditionally (a Router is required either way — see src/data-table.ts),
299
- // and the SAME pair is used to read and to write; see `readTableUrlState`.
300
- const [searchParams, setSearchParams] = useSearchParams();
301
- const location = useLocation();
355
+ // The SAME pair is used to read and to write; see `readTableUrlState`. No Router is
356
+ // needed without `urlSync` (0.49, KIT-3; see `useTableSearch`).
357
+ const { searchParams, setSearchParams, locationState } = useTableSearch(urlSync, "DataTable");
302
358
  // Read URL once on mount so URL-encoded views (e.g. shared links, deep links from
303
359
  // other pages) populate initial state. After mount, state is local.
304
360
  const [urlInitial] = useState<TableUrlState>(() =>
@@ -436,7 +492,7 @@ export function useTableState<T>({
436
492
  // `columns` is deliberately not a dependency and does not need to be: the
437
493
  // signature above is computed from it, so a column set that would produce a
438
494
  // different address produces a different signature first.
439
- writeUrlState(setSearchParams, columns, sorts, filters, pageSize, page, defaultPageSize, undefined, location.state, keys);
495
+ writeUrlState(setSearchParams, columns, sorts, filters, pageSize, page, defaultPageSize, undefined, locationState, keys);
440
496
  // eslint-disable-next-line react-hooks/exhaustive-deps
441
497
  }, [urlSync, urlSig]);
442
498
 
@@ -620,8 +676,7 @@ export function useTableUrlState<T>({
620
676
  const urlSync = resolved.enabled;
621
677
  const urlPrefix = urlPrefixOption ?? resolved.prefix;
622
678
  const keys = useMemo(() => urlKeys(urlPrefix), [urlPrefix]);
623
- const [searchParams, setSearchParams] = useSearchParams();
624
- const location = useLocation();
679
+ const { searchParams, setSearchParams, locationState } = useTableSearch(urlSync, "useTableUrlState");
625
680
  const [state, setState] = useState<OwnedState>(() => {
626
681
  const url = urlSync ? readTableUrlState(searchParams, columns, urlPrefix) : {};
627
682
  return {
@@ -653,7 +708,7 @@ export function useTableUrlState<T>({
653
708
  state.page,
654
709
  defaultPageSize,
655
710
  manageSearch ? state.search : undefined,
656
- location.state,
711
+ locationState,
657
712
  keys,
658
713
  );
659
714
  // Keyed on the signature, which is computed from everything the write reads — see
package/src/data-table.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  // `@eifi1/ui-kit/data-table` — the table suite.
2
2
  //
3
- // A re-slicing of the main barrel, not a new API. This entry point requires
4
- // `react-router`: DataTable calls useSearchParams unconditionally, above any `urlSync`
5
- // branch, so a Router is needed even with URL sync switched off.
3
+ // A re-slicing of the main barrel, not a new API. This entry point imports
4
+ // `react-router` (a peer dependency), but since 0.49 (KIT-3) a Router is needed only for
5
+ // a table with `urlSync`: without it DataTable keeps its view in local state alone.
6
6
  export * from "./components/data-table-labels";
7
7
  export * from "./components/data-table-sort";
8
8
  export * from "./components/data-table-filters";
@@ -116,7 +116,7 @@ export function DemoBanner({
116
116
  <>
117
117
  {!collapsed && (
118
118
  <>
119
- <Button href={accessLink.href} variant="brand" size="sm">
119
+ <Button href={accessLink.href} variant="accent" size="sm">
120
120
  {accessLink.label}
121
121
  </Button>
122
122
  <Button href={signInHref} variant="secondary" size="sm">
@@ -75,7 +75,7 @@ export function DemoEnded({
75
75
  </p>
76
76
  {children}
77
77
  <div className="mt-4 flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:flex-wrap sm:justify-center">
78
- <Button href={restartHref} variant="brand">
78
+ <Button href={restartHref} variant="accent">
79
79
  {labels.restart}
80
80
  </Button>
81
81
  <Button href={accessLink.href} variant="secondary">
@@ -139,7 +139,7 @@ export function DemoStart<T>({
139
139
  </div>
140
140
  {refusal !== null && (
141
141
  <div className="flex flex-wrap justify-center gap-3">
142
- <Button href={accessLink.href} variant="brand">
142
+ <Button href={accessLink.href} variant="accent">
143
143
  {accessLink.label}
144
144
  </Button>
145
145
  <Button href={backHref} variant="secondary">
@@ -469,7 +469,7 @@ export function FeedbackNoteEditor({
469
469
  <Button variant="ghost" onClick={onCancel}>
470
470
  {cancelLabel}
471
471
  </Button>
472
- <Button variant="brand" disabled={pending || blank} commit={commit} onClick={submit}>
472
+ <Button variant="accent" disabled={pending || blank} commit={commit} onClick={submit}>
473
473
  {saveLabel}
474
474
  </Button>
475
475
  </div>
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useRef } from "react";
1
+ import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
2
2
  import type { KeyboardEvent as ReactKeyboardEvent, RefObject } from "react";
3
3
  import { isComposing } from "../lib/ime";
4
4
 
@@ -60,6 +60,9 @@ import { isComposing } from "../lib/ime";
60
60
  /** A layer's handler, read through a ref so the stack holds one stable entry per open. */
61
61
  interface Layer {
62
62
  escape: (e: KeyboardEvent) => void;
63
+ /** Whether it covers the page — a dialog, a sheet, a popover, a list — rather than
64
+ * annotating it (a tooltip). Counted by {@link useOverlayOpen}. */
65
+ overlay: boolean;
63
66
  }
64
67
 
65
68
  /** What {@link useDismissLayer} hands back: its layer while open, for
@@ -114,11 +117,13 @@ function push(layer: Layer): void {
114
117
  document.addEventListener("keydown", onBubble);
115
118
  }
116
119
  layers.push(layer);
120
+ if (layer.overlay) notifyOverlays();
117
121
  }
118
122
 
119
123
  function pop(layer: Layer): void {
120
124
  const at = layers.indexOf(layer);
121
125
  if (at >= 0) layers.splice(at, 1);
126
+ if (at >= 0 && layer.overlay) notifyOverlays();
122
127
  if (layers.length > 0) return;
123
128
  document.removeEventListener("keydown", onCapture, true);
124
129
  document.removeEventListener("keydown", onBubble);
@@ -135,7 +140,11 @@ function pop(layer: Layer): void {
135
140
  * read live, so it need not be memoised; the layer is pushed and popped only when
136
141
  * `open` flips. The layer it returns is for the owner's {@link runOwnKeyDown}.
137
142
  */
138
- export function useDismissLayer(open: boolean, onEscape: (e: KeyboardEvent) => void): DismissLayer {
143
+ export function useDismissLayer(
144
+ open: boolean,
145
+ onEscape: (e: KeyboardEvent) => void,
146
+ { overlay = true }: { overlay?: boolean } = {},
147
+ ): DismissLayer {
139
148
  const handlerRef = useRef(onEscape);
140
149
  const layerRef = useRef<Layer | null>(null);
141
150
  // Written in an effect, not during render, so it satisfies react-hooks/refs: a
@@ -148,17 +157,47 @@ export function useDismissLayer(open: boolean, onEscape: (e: KeyboardEvent) => v
148
157
  });
149
158
  useEffect(() => {
150
159
  if (!open || typeof document === "undefined") return;
151
- const layer: Layer = { escape: (e) => handlerRef.current(e) };
160
+ const layer: Layer = { escape: (e) => handlerRef.current(e), overlay };
152
161
  layerRef.current = layer;
153
162
  push(layer);
154
163
  return () => {
155
164
  pop(layer);
156
165
  layerRef.current = null;
157
166
  };
158
- }, [open]);
167
+ }, [open, overlay]);
159
168
  return layerRef;
160
169
  }
161
170
 
171
+ /*
172
+ * Whether an overlay is open (0.49 KIT-11): every layer on the stack but a tooltip's is a
173
+ * dialog, a sheet, a popover, a hover menu or a list covering part of the page, so the
174
+ * stack is also the one place that knows. The floating launchers (`hideWhenOverlay`) step
175
+ * aside while one is open instead of floating over it or beside it.
176
+ */
177
+ const overlayListeners = new Set<() => void>();
178
+
179
+ function notifyOverlays(): void {
180
+ for (const listener of overlayListeners) listener();
181
+ }
182
+
183
+ function subscribeOverlays(listener: () => void): () => void {
184
+ overlayListeners.add(listener);
185
+ return () => {
186
+ overlayListeners.delete(listener);
187
+ };
188
+ }
189
+
190
+ function anyOverlayOpen(): boolean {
191
+ return layers.some((layer) => layer.overlay);
192
+ }
193
+
194
+ /** True while a kit dialog, sheet, popover, hover menu or list is open (any layer of the
195
+ * Escape stack but a tooltip's). Internal. */
196
+ export function useOverlayOpen(enabled = true): boolean {
197
+ const open = useSyncExternalStore(subscribeOverlays, anyOverlayOpen, () => false);
198
+ return enabled && open;
199
+ }
200
+
162
201
  /**
163
202
  * For a layer's own element, in its `onKeyDown`: hands an Escape on its way up to the
164
203
  * topmost layer — this one, unless something opened over it without taking focus (a
@@ -0,0 +1,31 @@
1
+ import { useEffect } from "react";
2
+ import { useKitDocumentTitle } from "../i18n/kit-labels";
3
+ import { DEFAULT_DOCUMENT_TITLE_FORMAT, formatDocumentTitle } from "../lib/document-title";
4
+
5
+ /**
6
+ * Writes the window's title (`document.title`) while the calling component is mounted,
7
+ * and puts back the title it found when it unmounts (0.49 KIT-9).
8
+ *
9
+ * `parts` are positional: `[page, section, app]`. The app falls back to the provider's
10
+ * `appName`, the format to the provider's `documentTitleFormat` (default
11
+ * "{page} · {section} · {app}"); a missing part drops with its separator
12
+ * ({@link formatDocumentTitle}). Parts that are ALL empty write nothing — the provider's
13
+ * app alone is not a page's title.
14
+ *
15
+ * `PageHeader` calls it for its `h1` (`documentTitle`); call it yourself for a page
16
+ * without one.
17
+ */
18
+ export function useDocumentTitle(parts: ReadonlyArray<string | null | undefined | false>): void {
19
+ const { format, appName } = useKitDocumentTitle();
20
+ const [page, section, app] = parts.map((p) => (typeof p === "string" && p.trim() !== "" ? p : undefined));
21
+ const any = parts.some((p) => typeof p === "string" && p.trim() !== "");
22
+ const title = any ? formatDocumentTitle(format ?? DEFAULT_DOCUMENT_TITLE_FORMAT, { page, section, app: app ?? appName }) : null;
23
+ useEffect(() => {
24
+ if (title === null || title === "") return;
25
+ const before = document.title;
26
+ document.title = title;
27
+ return () => {
28
+ document.title = before;
29
+ };
30
+ }, [title]);
31
+ }
@@ -14,6 +14,7 @@ import {
14
14
  import { DEFAULT_WIZARD_LABELS } from "../wizard/wizard-labels";
15
15
  import { DEFAULT_TOUR_LABELS } from "../tour/tour-labels";
16
16
  import { DEFAULT_TOUR_CATALOGUE_LABELS } from "../tour/tour-catalogue-labels";
17
+ import { DEFAULT_TOUR_GUIDE_LABELS } from "../tour/tour-guide-labels";
17
18
  import { DEFAULT_COMMAND_PALETTE_LABELS } from "../search/command-palette-labels";
18
19
  import { DEFAULT_GLOBAL_SEARCH_LABELS } from "../search/global-search-labels";
19
20
  import { DEFAULT_SERIES_CHART_LABELS } from "../components/series-chart-labels";
@@ -173,6 +174,7 @@ export const DEFAULT_UI_KIT_LABELS: UiKitLabels = {
173
174
  wizard: DEFAULT_WIZARD_LABELS,
174
175
  tour: DEFAULT_TOUR_LABELS,
175
176
  tourCatalogue: DEFAULT_TOUR_CATALOGUE_LABELS,
177
+ tourGuide: DEFAULT_TOUR_GUIDE_LABELS,
176
178
  commandPalette: DEFAULT_COMMAND_PALETTE_LABELS,
177
179
  globalSearch: DEFAULT_GLOBAL_SEARCH_LABELS,
178
180
  seriesChart: DEFAULT_SERIES_CHART_LABELS,
@@ -556,6 +556,7 @@ export function germanLabels(numberLocale: string): UiKitLabels {
556
556
  theme: "Theme wechseln",
557
557
  palette: "Darstellungsvorlage",
558
558
  language: "Sprache",
559
+ languageCurrent: (name) => `Sprache wählen, aktuell: ${name}`,
559
560
  switchRole: "Rolle wechseln",
560
561
  role: (value) => `Rolle: ${value}`,
561
562
  },
@@ -633,6 +634,19 @@ export function germanLabels(numberLocale: string): UiKitLabels {
633
634
  awaitClickHint: "Klicken Sie auf das hervorgehobene Element, um fortzufahren",
634
635
  awaitInputHint: "Geben Sie im hervorgehobenen Feld einen Wert ein, um fortzufahren", // 0.46: TourStep.awaitInput (keksdose live #436).
635
636
  step: (current, total) => `${n(current)} / ${n(total)}`,
637
+ // 0.50: guided form tours (docs/tours-harmonization.md §11).
638
+ leaveEmpty: "Leer lassen",
639
+ optional: "Optional",
640
+ gateHint: "Füllen Sie das hervorgehobene Feld aus, um fortzufahren",
641
+ gateReady: "Bereit. Drücken Sie Weiter oder die Eingabetaste, um fortzufahren.",
642
+ awaitGateHint: "Speichern Sie, um fortzufahren",
643
+ awaitGateRefused: "Das ist noch nicht gespeichert. Prüfen Sie das Formular und versuchen Sie es erneut.",
644
+ backToStep: (title) => `Zurück zu „${title}“`,
645
+ },
646
+ // 0.50: the form's "Guide me" (TourGuideButton, docs/tours-harmonization.md §11).
647
+ tourGuide: {
648
+ label: "Schritt für Schritt",
649
+ description: "Führt Sie Feld für Feld durch dieses Formular",
636
650
  },
637
651
  // 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page. keksdose's reviewed words.
638
652
  tourCatalogue: {
@@ -1929,10 +1943,13 @@ export function germanLabels(numberLocale: string): UiKitLabels {
1929
1943
  rowActions: {
1930
1944
  actions: "Aktionen",
1931
1945
  actionsFor: (name) => `Aktionen für ${name}`,
1946
+ confirmTitle: (action, name) => (name ? `${action}: «${name}»?` : `${action}?`),
1947
+ confirmBody: "Das lässt sich nicht rückgängig machen.",
1932
1948
  },
1933
1949
  appShellMore: {
1934
1950
  more: "Mehr",
1935
1951
  moreTitle: "Weitere Seiten",
1952
+ overview: "Übersicht",
1936
1953
  },
1937
1954
  // 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words. "Verwaltung"
1938
1955
  // as settings §5 names the page; "mit Administratorrechten" rather than "für
@@ -26,6 +26,7 @@ import type { TabsLabels } from "../components/tabs";
26
26
  import type { WizardLabels } from "../wizard/types";
27
27
  import type { TourLabels } from "../tour/tour";
28
28
  import type { TourCatalogueLabels } from "../tour/tour-catalogue-labels";
29
+ import type { TourGuideLabels } from "../tour/tour-guide-labels";
29
30
  import type { CommandPaletteLabels } from "../search/command-palette";
30
31
  import type { GlobalSearchLabels } from "../search/global-search";
31
32
  import type { MonthPickerLabels } from "../components/month-picker";
@@ -283,6 +284,9 @@ export interface TopBarLabels {
283
284
  theme: string;
284
285
  palette: string;
285
286
  language: string;
287
+ /** 0.49 KIT-10: the language menu's trigger, given the current language's name —
288
+ * "Choose language, current: Deutsch". */
289
+ languageCurrent: (name: string) => string;
286
290
  switchRole: string;
287
291
  /** The role switcher's tooltip, given the active role's name. */
288
292
  role: (value: string) => string;
@@ -332,6 +336,8 @@ export interface UiKitLabels {
332
336
  tour: TourLabels;
333
337
  /** 0.43.0: the tours page — `TourCatalogue` and its card (docs/tours-harmonization.md §3). */
334
338
  tourCatalogue: TourCatalogueLabels;
339
+ /** 0.50: the form's "Guide me" — `TourGuideButton` (docs/tours-harmonization.md §11). */
340
+ tourGuide: TourGuideLabels;
335
341
  commandPalette: CommandPaletteLabels;
336
342
  globalSearch: GlobalSearchLabels;
337
343
  seriesChart: SeriesChartLabels;
@@ -557,6 +563,8 @@ interface KitI18n {
557
563
  formatDate?: KitDateFormatter;
558
564
  tableStorageKeyPrefix?: string;
559
565
  timeZone?: string;
566
+ documentTitleFormat?: string;
567
+ appName?: string;
560
568
  }
561
569
 
562
570
  /** What a {@link KitDateFormatter} is told about the date it is formatting. */
@@ -699,6 +707,25 @@ export interface UiKitProviderProps {
699
707
  * it names in every zone). Left out, timestamps follow the device, as before.
700
708
  */
701
709
  timeZone?: string;
710
+ /**
711
+ * 0.49 KIT-9: how `useDocumentTitle` and every `PageHeader`'s `h1` write the window's
712
+ * title, from `{page}`, `{section}` and `{app}`. Default
713
+ * `"{page} · {section} · {app}"` (`DEFAULT_DOCUMENT_TITLE_FORMAT`); a missing part
714
+ * drops with its separator.
715
+ */
716
+ documentTitleFormat?: string;
717
+ /** 0.49 KIT-9: the app's name, the `{app}` of the window's title ("Kastlan"). Left
718
+ * out, titles end at the page or section. */
719
+ appName?: string;
720
+ /**
721
+ * 0.49 KIT-1b: while a person copies a selection in the page, a U+2212 minus standing
722
+ * before a number ("−CHF 1,250.50") goes to the clipboard as "-", in `text/plain` and
723
+ * `text/html` (what Excel pastes), so a spreadsheet reads a number; the page keeps "−".
724
+ * A copy out of an input, a textarea or an editable element is left alone. Installed
725
+ * once per document, by the first provider that has it on. Default `true`; `false` on
726
+ * the outermost provider leaves the clipboard to the browser.
727
+ */
728
+ copyAsciiMinus?: boolean;
702
729
  children: ReactNode;
703
730
  }
704
731
 
@@ -719,6 +746,9 @@ export function UiKitProvider({
719
746
  formatDate,
720
747
  tableStorageKeyPrefix,
721
748
  timeZone,
749
+ documentTitleFormat,
750
+ appName,
751
+ copyAsciiMinus = true,
722
752
  children,
723
753
  }: UiKitProviderProps) {
724
754
  const outer = useContext(KitI18nContext);
@@ -732,10 +762,25 @@ export function UiKitProvider({
732
762
  formatDate: formatDate ?? outer.formatDate,
733
763
  tableStorageKeyPrefix: tableStorageKeyPrefix ?? outer.tableStorageKeyPrefix,
734
764
  timeZone: timeZone ?? outer.timeZone,
765
+ documentTitleFormat: documentTitleFormat ?? outer.documentTitleFormat,
766
+ appName: appName ?? outer.appName,
735
767
  }),
736
- [outer, labels, locale, weekStartsOn, linkComponent, chartTooltipPlacement, formatDate, tableStorageKeyPrefix, timeZone],
768
+ [
769
+ outer,
770
+ labels,
771
+ locale,
772
+ weekStartsOn,
773
+ linkComponent,
774
+ chartTooltipPlacement,
775
+ formatDate,
776
+ tableStorageKeyPrefix,
777
+ timeZone,
778
+ documentTitleFormat,
779
+ appName,
780
+ ],
737
781
  );
738
782
  useTableViewsJoin(value.tableStorageKeyPrefix ?? DEFAULT_TABLE_STORAGE_KEY_PREFIX);
783
+ useClipboardMinus(copyAsciiMinus);
739
784
  return <KitI18nContext.Provider value={value}>{children}</KitI18nContext.Provider>;
740
785
  }
741
786
 
@@ -773,6 +818,30 @@ function useTableViewsJoin(prefix: string): void {
773
818
  }, [prefix]);
774
819
  }
775
820
 
821
+ /**
822
+ * The copy handler (lib/clipboard-minus, 0.49 KIT-1b), through a literal `import()` for
823
+ * the reason {@link useTableViewsJoin} gives: a catalogue may reach nothing outside i18n/.
824
+ * It is in place a microtask after the mount — long before a person selects and copies.
825
+ */
826
+ function useClipboardMinus(on: boolean): void {
827
+ useEffect(() => {
828
+ if (!on || typeof document === "undefined") return;
829
+ let uninstall: (() => void) | undefined;
830
+ let gone = false;
831
+ import("../lib/clipboard-minus").then(
832
+ ({ installClipboardMinus }) => {
833
+ if (!gone) uninstall = installClipboardMinus(document);
834
+ },
835
+ // Not loaded: copying works as the browser does it.
836
+ () => undefined,
837
+ );
838
+ return () => {
839
+ gone = true;
840
+ uninstall?.();
841
+ };
842
+ }, [on]);
843
+ }
844
+
776
845
  function isRecord(v: unknown): v is Record<string, unknown> {
777
846
  return typeof v === "object" && v !== null && !Array.isArray(v);
778
847
  }
@@ -892,6 +961,15 @@ export function useKitTimeZone(): string | undefined {
892
961
  return useContext(KitI18nContext).timeZone;
893
962
  }
894
963
 
964
+ /** The nearest provider's `documentTitleFormat` and `appName`, or `undefined` each —
965
+ * what `useDocumentTitle` writes with (the format's default is
966
+ * `DEFAULT_DOCUMENT_TITLE_FORMAT`, applied there: this module reaches nothing outside
967
+ * i18n/). */
968
+ export function useKitDocumentTitle(): { format: string | undefined; appName: string | undefined } {
969
+ const { documentTitleFormat, appName } = useContext(KitI18nContext);
970
+ return { format: documentTitleFormat, appName };
971
+ }
972
+
895
973
  /** {@link DEFAULT_FILE_LABELS}, but formatting in the provider's locale. */
896
974
  export function useKitFileLabels(prop?: Partial<FileLabels>): FileLabels {
897
975
  const locale = useKitLocale();
@@ -543,6 +543,7 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
543
543
  theme: "Cambiar de tema",
544
544
  palette: "Ajuste de apariencia",
545
545
  language: "Idioma",
546
+ languageCurrent: (name) => `Elegir idioma, actual: ${name}`,
546
547
  switchRole: "Cambiar de rol",
547
548
  role: (value) => `Rol: ${value}`,
548
549
  },
@@ -616,6 +617,19 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
616
617
  awaitClickHint: "Haga clic en el elemento resaltado para continuar",
617
618
  awaitInputHint: "Introduzca un valor en el campo resaltado para continuar", // 0.46: TourStep.awaitInput (keksdose live #436).
618
619
  step: (current, total) => `${n(current)} / ${n(total)}`,
620
+ // 0.50: guided form tours (docs/tours-harmonization.md §11).
621
+ leaveEmpty: "Dejar vacío",
622
+ optional: "Opcional",
623
+ gateHint: "Rellene el campo resaltado para continuar",
624
+ gateReady: "Listo. Pulse Siguiente o Intro para continuar.",
625
+ awaitGateHint: "Guarde para continuar",
626
+ awaitGateRefused: "Todavía no se ha guardado. Revise el formulario e inténtelo de nuevo.",
627
+ backToStep: (title) => `Volver a «${title}»`,
628
+ },
629
+ // 0.50: the form's "Guide me" (TourGuideButton, docs/tours-harmonization.md §11).
630
+ tourGuide: {
631
+ label: "Guíeme",
632
+ description: "Le guía por este formulario, campo a campo",
619
633
  },
620
634
  // 0.43.0 — docs/tours-harmonization.md §3, §8: the tours page.
621
635
  tourCatalogue: {
@@ -1869,10 +1883,13 @@ export function uiKitLabelsEs(numberLocale = "es-ES"): UiKitLabels {
1869
1883
  rowActions: {
1870
1884
  actions: "Acciones",
1871
1885
  actionsFor: (name) => `Acciones de ${name}`,
1886
+ confirmTitle: (action, name) => (name ? `¿${action} «${name}»?` : `¿${action}?`),
1887
+ confirmBody: "Esta acción no se puede deshacer.",
1872
1888
  },
1873
1889
  appShellMore: {
1874
1890
  more: "Más",
1875
1891
  moreTitle: "Más páginas",
1892
+ overview: "Resumen",
1876
1893
  },
1877
1894
  // 0.42.0 — docs/admin-harmonization.md §3.1: the admin area's shared words. "Usted",
1878
1895
  // as the rest of the catalogue.