@eifi1/ui-kit 0.11.0 → 0.13.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 (397) hide show
  1. package/README.md +59 -34
  2. package/dist/chart.d.ts +3 -1
  3. package/dist/chart.js +2 -0
  4. package/dist/chart.js.map +1 -1
  5. package/dist/components/account-settings-labels.d.ts +105 -0
  6. package/dist/components/account-settings-labels.js +81 -0
  7. package/dist/components/account-settings-labels.js.map +1 -0
  8. package/dist/components/account-settings.d.ts +36 -52
  9. package/dist/components/account-settings.js +44 -8
  10. package/dist/components/account-settings.js.map +1 -1
  11. package/dist/components/amount-input.d.ts +40 -4
  12. package/dist/components/amount-input.js +50 -12
  13. package/dist/components/amount-input.js.map +1 -1
  14. package/dist/components/authed-image.d.ts +68 -0
  15. package/dist/components/authed-image.js +117 -0
  16. package/dist/components/authed-image.js.map +1 -0
  17. package/dist/components/breadcrumbs.d.ts +2 -1
  18. package/dist/components/breadcrumbs.js +5 -2
  19. package/dist/components/breadcrumbs.js.map +1 -1
  20. package/dist/components/bulk-action-bar.d.ts +10 -2
  21. package/dist/components/bulk-action-bar.js.map +1 -1
  22. package/dist/components/button-group.d.ts +51 -2
  23. package/dist/components/button-group.js +5 -1
  24. package/dist/components/button-group.js.map +1 -1
  25. package/dist/components/calculator.d.ts +15 -4
  26. package/dist/components/calendar-heatmap.d.ts +41 -3
  27. package/dist/components/calendar-heatmap.js +48 -7
  28. package/dist/components/calendar-heatmap.js.map +1 -1
  29. package/dist/components/chart.js +14 -8
  30. package/dist/components/chart.js.map +1 -1
  31. package/dist/components/chip.d.ts +2 -1
  32. package/dist/components/chip.js +26 -18
  33. package/dist/components/chip.js.map +1 -1
  34. package/dist/components/choice-card.d.ts +35 -3
  35. package/dist/components/choice-card.js +83 -25
  36. package/dist/components/choice-card.js.map +1 -1
  37. package/dist/components/combobox-core.d.ts +3 -1
  38. package/dist/components/combobox-core.js +18 -5
  39. package/dist/components/combobox-core.js.map +1 -1
  40. package/dist/components/combobox.d.ts +2 -2
  41. package/dist/components/combobox.js +31 -9
  42. package/dist/components/combobox.js.map +1 -1
  43. package/dist/components/copy-button.d.ts +51 -70
  44. package/dist/components/copy-button.js +1 -1
  45. package/dist/components/copy-button.js.map +1 -1
  46. package/dist/components/data-table-cells.d.ts +63 -0
  47. package/dist/components/data-table-cells.js +64 -0
  48. package/dist/components/data-table-cells.js.map +1 -0
  49. package/dist/components/data-table-filter-popover.d.ts +1 -1
  50. package/dist/components/data-table-filter-popover.js +8 -1
  51. package/dist/components/data-table-filter-popover.js.map +1 -1
  52. package/dist/components/data-table-filters.d.ts +1 -1
  53. package/dist/components/data-table-filters.js +31 -1
  54. package/dist/components/data-table-filters.js.map +1 -1
  55. package/dist/components/data-table-labels.d.ts +13 -0
  56. package/dist/components/data-table-labels.js +8 -1
  57. package/dist/components/data-table-labels.js.map +1 -1
  58. package/dist/components/data-table.d.ts +1 -1
  59. package/dist/components/data-table.js +125 -27
  60. package/dist/components/data-table.js.map +1 -1
  61. package/dist/components/date-picker.d.ts +3 -1
  62. package/dist/components/date-picker.js +15 -2
  63. package/dist/components/date-picker.js.map +1 -1
  64. package/dist/components/description-list.d.ts +23 -3
  65. package/dist/components/description-list.js +23 -3
  66. package/dist/components/description-list.js.map +1 -1
  67. package/dist/components/dialog-frame.d.ts +1 -0
  68. package/dist/components/entity-combobox.d.ts +1 -1
  69. package/dist/components/entity-combobox.js +40 -20
  70. package/dist/components/entity-combobox.js.map +1 -1
  71. package/dist/components/error-boundary.d.ts +281 -0
  72. package/dist/components/error-boundary.js +302 -0
  73. package/dist/components/error-boundary.js.map +1 -0
  74. package/dist/components/field-sync.js +90 -83
  75. package/dist/components/field-sync.js.map +1 -1
  76. package/dist/components/field.d.ts +30 -4
  77. package/dist/components/field.js +40 -17
  78. package/dist/components/field.js.map +1 -1
  79. package/dist/components/file-button.d.ts +50 -208
  80. package/dist/components/file-dropzone.d.ts +49 -2
  81. package/dist/components/floating-panel.d.ts +13 -4
  82. package/dist/components/floating-panel.js +7 -3
  83. package/dist/components/floating-panel.js.map +1 -1
  84. package/dist/components/form-actions.d.ts +53 -0
  85. package/dist/components/form-actions.js +98 -0
  86. package/dist/components/form-actions.js.map +1 -0
  87. package/dist/components/full-bleed-dialog.js +4 -0
  88. package/dist/components/full-bleed-dialog.js.map +1 -1
  89. package/dist/components/hover-menu.js +64 -56
  90. package/dist/components/hover-menu.js.map +1 -1
  91. package/dist/components/image-grid.d.ts +67 -0
  92. package/dist/components/image-grid.js +112 -0
  93. package/dist/components/image-grid.js.map +1 -0
  94. package/dist/components/lightbox.d.ts +104 -0
  95. package/dist/components/lightbox.js +229 -0
  96. package/dist/components/lightbox.js.map +1 -0
  97. package/dist/components/line-items.d.ts +114 -0
  98. package/dist/components/line-items.js +238 -0
  99. package/dist/components/line-items.js.map +1 -0
  100. package/dist/components/list.d.ts +37 -4
  101. package/dist/components/list.js +35 -14
  102. package/dist/components/list.js.map +1 -1
  103. package/dist/components/loading-state.d.ts +24 -0
  104. package/dist/components/loading-state.js +32 -0
  105. package/dist/components/loading-state.js.map +1 -0
  106. package/dist/components/menu-item.d.ts +2 -1
  107. package/dist/components/menu-item.js +5 -1
  108. package/dist/components/menu-item.js.map +1 -1
  109. package/dist/components/mini-calendar.d.ts +13 -1
  110. package/dist/components/mini-calendar.js +46 -2
  111. package/dist/components/mini-calendar.js.map +1 -1
  112. package/dist/components/modal.d.ts +20 -1
  113. package/dist/components/modal.js +26 -2
  114. package/dist/components/modal.js.map +1 -1
  115. package/dist/components/month-picker.d.ts +21 -1
  116. package/dist/components/month-picker.js +139 -26
  117. package/dist/components/month-picker.js.map +1 -1
  118. package/dist/components/multi-entity-combobox.d.ts +1 -1
  119. package/dist/components/multi-entity-combobox.js +42 -20
  120. package/dist/components/multi-entity-combobox.js.map +1 -1
  121. package/dist/components/nav-pills.d.ts +2 -1
  122. package/dist/components/nav-pills.js +5 -1
  123. package/dist/components/nav-pills.js.map +1 -1
  124. package/dist/components/number-field.d.ts +24 -5
  125. package/dist/components/number-field.js +5 -7
  126. package/dist/components/number-field.js.map +1 -1
  127. package/dist/components/number-input.d.ts +25 -5
  128. package/dist/components/number-input.js +7 -1
  129. package/dist/components/number-input.js.map +1 -1
  130. package/dist/components/numpad-sheet.d.ts +20 -5
  131. package/dist/components/numpad-sheet.js +3 -2
  132. package/dist/components/numpad-sheet.js.map +1 -1
  133. package/dist/components/page-header.d.ts +35 -6
  134. package/dist/components/page-header.js +7 -1
  135. package/dist/components/page-header.js.map +1 -1
  136. package/dist/components/passkeys-setting.d.ts +67 -0
  137. package/dist/components/passkeys-setting.js +214 -0
  138. package/dist/components/passkeys-setting.js.map +1 -0
  139. package/dist/components/picker-sheet.js +5 -1
  140. package/dist/components/picker-sheet.js.map +1 -1
  141. package/dist/components/pie-chart-labels.d.ts +20 -0
  142. package/dist/components/pie-chart-labels.js +12 -0
  143. package/dist/components/pie-chart-labels.js.map +1 -0
  144. package/dist/components/pie-chart.d.ts +101 -0
  145. package/dist/components/pie-chart.js +283 -0
  146. package/dist/components/pie-chart.js.map +1 -0
  147. package/dist/components/popover.js +3 -0
  148. package/dist/components/popover.js.map +1 -1
  149. package/dist/components/progress-bar.d.ts +44 -3
  150. package/dist/components/progress-bar.js +58 -22
  151. package/dist/components/progress-bar.js.map +1 -1
  152. package/dist/components/qr-code.d.ts +34 -0
  153. package/dist/components/qr-code.js +57 -0
  154. package/dist/components/qr-code.js.map +1 -0
  155. package/dist/components/search-field.d.ts +15 -3
  156. package/dist/components/search-field.js +5 -1
  157. package/dist/components/search-field.js.map +1 -1
  158. package/dist/components/settings-fields.d.ts +49 -1
  159. package/dist/components/signed-amount.d.ts +122 -0
  160. package/dist/components/signed-amount.js +152 -0
  161. package/dist/components/signed-amount.js.map +1 -0
  162. package/dist/components/skeleton.d.ts +10 -3
  163. package/dist/components/skeleton.js +12 -2
  164. package/dist/components/skeleton.js.map +1 -1
  165. package/dist/components/stat-tile.d.ts +2 -1
  166. package/dist/components/stat-tile.js +6 -3
  167. package/dist/components/stat-tile.js.map +1 -1
  168. package/dist/components/swipeable-row.js +4 -3
  169. package/dist/components/swipeable-row.js.map +1 -1
  170. package/dist/components/table.d.ts +39 -5
  171. package/dist/components/table.js +45 -11
  172. package/dist/components/table.js.map +1 -1
  173. package/dist/components/text-link.d.ts +238 -0
  174. package/dist/components/text-link.js +144 -0
  175. package/dist/components/text-link.js.map +1 -0
  176. package/dist/components/text.d.ts +26 -2
  177. package/dist/components/text.js +16 -2
  178. package/dist/components/text.js.map +1 -1
  179. package/dist/components/time-input.d.ts +49 -1
  180. package/dist/components/toggle-group.d.ts +35 -0
  181. package/dist/components/toggle-group.js +43 -14
  182. package/dist/components/toggle-group.js.map +1 -1
  183. package/dist/components/toggle-legend.d.ts +53 -3
  184. package/dist/components/toggle-legend.js +54 -1
  185. package/dist/components/toggle-legend.js.map +1 -1
  186. package/dist/components/tooltip.d.ts +51 -1
  187. package/dist/components/tooltip.js +59 -35
  188. package/dist/components/tooltip.js.map +1 -1
  189. package/dist/components/ui.d.ts +51 -779
  190. package/dist/components/ui.js +364 -34
  191. package/dist/components/ui.js.map +1 -1
  192. package/dist/components/use-table-state.d.ts +3 -52
  193. package/dist/components/use-table-state.js +128 -30
  194. package/dist/components/use-table-state.js.map +1 -1
  195. package/dist/{data-table-filters-Dh9uF_S-.d.ts → data-table-Drun8Fk9.d.ts} +305 -61
  196. package/dist/data-table.d.ts +2 -1
  197. package/dist/data-table.js +5 -1
  198. package/dist/data-table.js.map +1 -1
  199. package/dist/feedback/feedback-attachment.js +4 -6
  200. package/dist/feedback/feedback-attachment.js.map +1 -1
  201. package/dist/feedback/feedback-dialog.d.ts +26 -3
  202. package/dist/feedback/feedback-dialog.js +21 -5
  203. package/dist/feedback/feedback-dialog.js.map +1 -1
  204. package/dist/feedback/feedback-inbox.js +86 -75
  205. package/dist/feedback/feedback-inbox.js.map +1 -1
  206. package/dist/feedback/feedback-thread.d.ts +173 -0
  207. package/dist/feedback/feedback-thread.js +264 -0
  208. package/dist/feedback/feedback-thread.js.map +1 -0
  209. package/dist/feedback.d.ts +2 -1
  210. package/dist/feedback.js +1 -0
  211. package/dist/feedback.js.map +1 -1
  212. package/dist/hooks/use-authed-src.d.ts +71 -0
  213. package/dist/hooks/use-authed-src.js +63 -0
  214. package/dist/hooks/use-authed-src.js.map +1 -0
  215. package/dist/hooks/use-file-drop.d.ts +49 -2
  216. package/dist/hooks/use-hotkey.d.ts +59 -0
  217. package/dist/hooks/use-hotkey.js +105 -0
  218. package/dist/hooks/use-hotkey.js.map +1 -0
  219. package/dist/hooks/use-search-param-state.d.ts +83 -0
  220. package/dist/hooks/use-search-param-state.js +94 -0
  221. package/dist/hooks/use-search-param-state.js.map +1 -0
  222. package/dist/i18n/defaults.d.ts +15 -4
  223. package/dist/i18n/defaults.js +31 -1
  224. package/dist/i18n/defaults.js.map +1 -1
  225. package/dist/i18n/kit-labels.d.ts +46 -312
  226. package/dist/i18n/kit-labels.js +20 -5
  227. package/dist/i18n/kit-labels.js.map +1 -1
  228. package/dist/i18n/locales/de-CH-informal.d.ts +15 -4
  229. package/dist/i18n/locales/de-CH.d.ts +15 -4
  230. package/dist/i18n/locales/de-informal.d.ts +15 -4
  231. package/dist/i18n/locales/de.d.ts +15 -4
  232. package/dist/i18n/locales/de.js +164 -3
  233. package/dist/i18n/locales/de.js.map +1 -1
  234. package/dist/i18n/locales/es.d.ts +15 -4
  235. package/dist/i18n/locales/es.js +161 -3
  236. package/dist/i18n/locales/es.js.map +1 -1
  237. package/dist/i18n/locales/fr.d.ts +15 -4
  238. package/dist/i18n/locales/fr.js +161 -3
  239. package/dist/i18n/locales/fr.js.map +1 -1
  240. package/dist/i18n/locales/hu.d.ts +15 -4
  241. package/dist/i18n/locales/hu.js +161 -3
  242. package/dist/i18n/locales/hu.js.map +1 -1
  243. package/dist/i18n/locales/it.d.ts +15 -4
  244. package/dist/i18n/locales/it.js +161 -3
  245. package/dist/i18n/locales/it.js.map +1 -1
  246. package/dist/i18n/locales/zh.d.ts +15 -4
  247. package/dist/i18n/locales/zh.js +161 -3
  248. package/dist/i18n/locales/zh.js.map +1 -1
  249. package/dist/index.d.ts +39 -19
  250. package/dist/index.js +40 -1
  251. package/dist/index.js.map +1 -1
  252. package/dist/kit-labels-Mz8zZHIF.d.ts +1744 -0
  253. package/dist/lib/format.d.ts +162 -0
  254. package/dist/lib/format.js +151 -0
  255. package/dist/lib/format.js.map +1 -0
  256. package/dist/lib/qr-encode.d.ts +44 -0
  257. package/dist/lib/qr-encode.js +338 -0
  258. package/dist/lib/qr-encode.js.map +1 -0
  259. package/dist/rhf/fields.d.ts +283 -0
  260. package/dist/rhf/fields.js +631 -0
  261. package/dist/rhf/fields.js.map +1 -0
  262. package/dist/rhf/form.d.ts +49 -1
  263. package/dist/rhf/line-items.d.ts +31 -0
  264. package/dist/rhf/line-items.js +59 -0
  265. package/dist/rhf/line-items.js.map +1 -0
  266. package/dist/rhf.d.ts +61 -1
  267. package/dist/rhf.js +2 -0
  268. package/dist/rhf.js.map +1 -1
  269. package/dist/search/command-palette.js +20 -14
  270. package/dist/search/command-palette.js.map +1 -1
  271. package/dist/shell/app-shell.d.ts +99 -2
  272. package/dist/shell/app-shell.js +120 -57
  273. package/dist/shell/app-shell.js.map +1 -1
  274. package/dist/shell/auth-layout.d.ts +74 -0
  275. package/dist/shell/auth-layout.js +97 -0
  276. package/dist/shell/auth-layout.js.map +1 -0
  277. package/dist/shell/top-bar-brand.d.ts +81 -0
  278. package/dist/shell/top-bar-brand.js +47 -0
  279. package/dist/shell/top-bar-brand.js.map +1 -0
  280. package/dist/shell/topbar-action-menu.d.ts +21 -4
  281. package/dist/shell/topbar-action-menu.js +18 -2
  282. package/dist/shell/topbar-action-menu.js.map +1 -1
  283. package/dist/shell.d.ts +56 -1
  284. package/dist/shell.js +2 -0
  285. package/dist/shell.js.map +1 -1
  286. package/dist/tour/tour.js +10 -4
  287. package/dist/tour/tour.js.map +1 -1
  288. package/dist/wizard/stepper-nav.d.ts +48 -1
  289. package/dist/wizard/use-wizard.js +4 -2
  290. package/dist/wizard/use-wizard.js.map +1 -1
  291. package/dist/wizard/wizard-context.d.ts +24 -1
  292. package/dist/wizard/wizard-context.js +19 -2
  293. package/dist/wizard/wizard-context.js.map +1 -1
  294. package/dist/wizard/wizard-step.d.ts +30 -5
  295. package/dist/wizard/wizard-step.js +34 -3
  296. package/dist/wizard/wizard-step.js.map +1 -1
  297. package/dist/wizard.d.ts +50 -3
  298. package/package.json +6 -6
  299. package/src/chart.ts +4 -0
  300. package/src/components/account-settings-labels.ts +190 -0
  301. package/src/components/account-settings.tsx +110 -63
  302. package/src/components/amount-input.tsx +109 -10
  303. package/src/components/authed-image.tsx +190 -0
  304. package/src/components/breadcrumbs.tsx +8 -2
  305. package/src/components/bulk-action-bar.tsx +16 -3
  306. package/src/components/button-group.tsx +7 -2
  307. package/src/components/calendar-heatmap.tsx +105 -10
  308. package/src/components/chart.tsx +19 -8
  309. package/src/components/chip.tsx +14 -4
  310. package/src/components/choice-card.tsx +136 -25
  311. package/src/components/combobox-core.tsx +33 -9
  312. package/src/components/combobox.tsx +33 -9
  313. package/src/components/copy-button.tsx +3 -2
  314. package/src/components/data-table-cells.tsx +119 -0
  315. package/src/components/data-table-filter-popover.tsx +13 -5
  316. package/src/components/data-table-filters.ts +77 -0
  317. package/src/components/data-table-labels.ts +23 -0
  318. package/src/components/data-table.tsx +230 -13
  319. package/src/components/date-picker.tsx +19 -3
  320. package/src/components/description-list.tsx +52 -3
  321. package/src/components/entity-combobox.tsx +25 -7
  322. package/src/components/error-boundary.tsx +640 -0
  323. package/src/components/field-sync.tsx +48 -40
  324. package/src/components/field.tsx +56 -5
  325. package/src/components/floating-panel.tsx +20 -7
  326. package/src/components/form-actions.tsx +191 -0
  327. package/src/components/full-bleed-dialog.tsx +4 -0
  328. package/src/components/hover-menu.tsx +26 -17
  329. package/src/components/image-grid.tsx +181 -0
  330. package/src/components/lightbox.tsx +374 -0
  331. package/src/components/line-items.tsx +385 -0
  332. package/src/components/list.tsx +82 -19
  333. package/src/components/loading-state.tsx +47 -0
  334. package/src/components/menu-item.tsx +7 -2
  335. package/src/components/mini-calendar.tsx +72 -3
  336. package/src/components/modal.tsx +65 -3
  337. package/src/components/month-picker.tsx +181 -26
  338. package/src/components/multi-entity-combobox.tsx +27 -7
  339. package/src/components/nav-pills.tsx +8 -1
  340. package/src/components/number-field.tsx +15 -10
  341. package/src/components/number-input.tsx +17 -1
  342. package/src/components/numpad-sheet.tsx +6 -1
  343. package/src/components/page-header.tsx +42 -5
  344. package/src/components/passkeys-setting.tsx +331 -0
  345. package/src/components/picker-sheet.tsx +10 -1
  346. package/src/components/pie-chart-labels.ts +32 -0
  347. package/src/components/pie-chart.tsx +479 -0
  348. package/src/components/popover.tsx +3 -0
  349. package/src/components/progress-bar.tsx +129 -19
  350. package/src/components/qr-code.tsx +83 -0
  351. package/src/components/search-field.tsx +20 -4
  352. package/src/components/signed-amount.tsx +295 -0
  353. package/src/components/skeleton.tsx +21 -3
  354. package/src/components/stat-tile.tsx +11 -3
  355. package/src/components/swipeable-row.tsx +7 -7
  356. package/src/components/table.tsx +102 -13
  357. package/src/components/text-link.tsx +356 -0
  358. package/src/components/text.tsx +46 -2
  359. package/src/components/toggle-group.tsx +93 -20
  360. package/src/components/toggle-legend.tsx +108 -2
  361. package/src/components/tooltip.tsx +79 -8
  362. package/src/components/ui.tsx +707 -49
  363. package/src/components/use-table-state.ts +265 -33
  364. package/src/data-table.ts +10 -0
  365. package/src/feedback/feedback-attachment.tsx +10 -7
  366. package/src/feedback/feedback-dialog.tsx +49 -7
  367. package/src/feedback/feedback-inbox.tsx +7 -0
  368. package/src/feedback/feedback-thread.tsx +510 -0
  369. package/src/feedback.ts +1 -0
  370. package/src/hooks/use-authed-src.ts +161 -0
  371. package/src/hooks/use-hotkey.ts +172 -0
  372. package/src/hooks/use-search-param-state.ts +187 -0
  373. package/src/i18n/defaults.ts +30 -0
  374. package/src/i18n/kit-labels.tsx +118 -8
  375. package/src/i18n/locales/de.ts +161 -0
  376. package/src/i18n/locales/es.ts +158 -0
  377. package/src/i18n/locales/fr.ts +161 -0
  378. package/src/i18n/locales/hu.ts +159 -0
  379. package/src/i18n/locales/it.ts +159 -0
  380. package/src/i18n/locales/zh.ts +158 -0
  381. package/src/index.ts +59 -0
  382. package/src/lib/format.ts +348 -0
  383. package/src/lib/qr-encode.ts +420 -0
  384. package/src/rhf/fields.tsx +1135 -0
  385. package/src/rhf/line-items.tsx +130 -0
  386. package/src/rhf.ts +6 -0
  387. package/src/search/command-palette.tsx +29 -19
  388. package/src/shell/app-shell.tsx +153 -3
  389. package/src/shell/auth-layout.tsx +190 -0
  390. package/src/shell/top-bar-brand.tsx +76 -0
  391. package/src/shell/topbar-action-menu.tsx +32 -4
  392. package/src/shell.ts +2 -0
  393. package/src/tour/tour.tsx +16 -5
  394. package/src/wizard/use-wizard.ts +8 -4
  395. package/src/wizard/wizard-context.tsx +41 -1
  396. package/src/wizard/wizard-step.tsx +75 -6
  397. package/tokens.css +12 -0
@@ -68,14 +68,38 @@ export interface LegendEntry {
68
68
  /**
69
69
  * What the entry's mark looks like. `swatch` — a filled square, the default — says
70
70
  * WHICH MEASUREMENT. `stroke` says WHICH QUANTITY, for the charts that carry both at
71
- * once and tell them apart by colour and by dash.
71
+ * once and tell them apart by colour and by dash. `dot` is a round mark, for a
72
+ * MARKER on the chart (a point, an event) rather than a series.
72
73
  */
73
- marker?: "swatch" | "stroke";
74
+ marker?: "swatch" | "stroke" | "dot";
74
75
  /** Which of {@link STROKE_PATTERNS}, when the marker is a stroke — the same index
75
76
  * `SeriesChartSeries.dash` takes, so a legend cannot promise a dot-dash the plot
76
77
  * draws dashed. Or the same custom `stroke-dasharray` string the series draws with
77
78
  * (keksdose's `"4 3"`). */
78
79
  dash?: number | string;
80
+ /**
81
+ * A mark of the caller's own, drawn in place of {@link marker} — for a key whose
82
+ * mark is not a colour at all (a fading bar for "open-ended", a hairline for
83
+ * "today"). Decorative: it is wrapped `aria-hidden`, and the label is the name.
84
+ */
85
+ icon?: ReactNode;
86
+ /**
87
+ * `"end"`: push this entry to the legend's inline END — the far right in LTR, the far
88
+ * left in RTL — with everything before it kept at the start. kastlan's lease gantt
89
+ * (feedback #49) keys its bars at the start and its "today" hairline at the end, over
90
+ * the time axis whose end "today" is nearest, and wrote it as
91
+ * `className="[&>li:last-child]:ms-auto"` — a selector that breaks the moment the
92
+ * entries are reordered and that reaches into the legend's markup.
93
+ *
94
+ * An auto inline-start margin, so it is logical (RTL mirrors it) and it takes only the
95
+ * row's LEFTOVER space: on a narrow screen where the row wraps, the entry wraps with it
96
+ * and sits at the end of its own line rather than overflowing. Give it to the LAST
97
+ * entry — auto margins push everything after them too, so an end entry in the middle
98
+ * drags the rest over with it (which is also how to end-align a GROUP: mark its first).
99
+ * In a vertical legend it sits at the column's inline end. Honoured by both
100
+ * {@link StaticLegend} and {@link ToggleLegend}, which share this entry type.
101
+ */
102
+ align?: "start" | "end";
79
103
  }
80
104
 
81
105
  export interface ToggleLegendProps {
@@ -152,6 +176,7 @@ export function ToggleLegend({
152
176
  "flex items-center gap-1.5 rounded text-start text-[11px] text-[var(--text-secondary)] transition-opacity hover:opacity-80",
153
177
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand)]",
154
178
  off && "opacity-35",
179
+ entry.align === "end" && "ms-auto",
155
180
  )}
156
181
  >
157
182
  <LegendMark entry={entry} off={off} />
@@ -163,6 +188,68 @@ export function ToggleLegend({
163
188
  );
164
189
  }
165
190
 
191
+ export interface StaticLegendProps {
192
+ entries: LegendEntry[];
193
+ /** See {@link ToggleLegendProps.orientation}. */
194
+ orientation?: "horizontal" | "vertical";
195
+ /** The list's accessible name. Default: the `seriesChart.legend` label. */
196
+ "aria-label"?: string;
197
+ className?: string;
198
+ /** Per-instance strings over `<UiKitProvider labels={{ seriesChart }}>`. */
199
+ labels?: Partial<SeriesChartLabels>;
200
+ }
201
+
202
+ /**
203
+ * A legend that is a KEY, not a control: the same entries and marks as
204
+ * {@link ToggleLegend} — swatch, stroke (with the chart's own dash), dot, or a mark of
205
+ * the caller's own — but nothing to press.
206
+ *
207
+ * A separate component rather than `ToggleLegend` without `onToggle`, because the two
208
+ * are different things to a screen reader: a group of switches announces a pressed
209
+ * state on every entry, a key is a LIST, read as "list, 4 items" and walked with the
210
+ * list keys, and costs no tab stops. The apps drew this by hand under the charts that
211
+ * say what a colour or a dash means without letting you switch it (kastlan's lease
212
+ * timeline, keksdose's cash buffer).
213
+ *
214
+ * Unlike `ToggleLegend` it draws a single entry: a key with one line still says what
215
+ * the one mark on the chart means.
216
+ */
217
+ export function StaticLegend({
218
+ entries,
219
+ orientation = "horizontal",
220
+ "aria-label": ariaLabel,
221
+ className,
222
+ labels: labelsProp,
223
+ }: StaticLegendProps) {
224
+ const labels = useKitLabels("seriesChart", DEFAULT_SERIES_CHART_LABELS, labelsProp);
225
+ if (entries.length === 0) return null;
226
+ return (
227
+ <ul
228
+ aria-label={ariaLabel ?? labels.legend}
229
+ className={cn(
230
+ "m-0 flex list-none gap-x-3 gap-y-1 p-0",
231
+ orientation === "vertical"
232
+ ? "flex-col items-start justify-center"
233
+ : "mt-2 flex-wrap items-center",
234
+ className,
235
+ )}
236
+ >
237
+ {entries.map((entry) => (
238
+ <li
239
+ key={entry.key}
240
+ className={cn(
241
+ "flex items-center gap-1.5 text-[11px] text-[var(--text-secondary)]",
242
+ entry.align === "end" && "ms-auto",
243
+ )}
244
+ >
245
+ <LegendMark entry={entry} off={false} />
246
+ {entry.label}
247
+ </li>
248
+ ))}
249
+ </ul>
250
+ );
251
+ }
252
+
166
253
  /**
167
254
  * The column a vertical legend stands in: beside the charts, on their horizontal
168
255
  * centre line. `h-full` + `justify-center`, so it centres in a grid cell or a flex row
@@ -205,6 +292,25 @@ export function LegendGroup({ title, children }: { title: ReactNode; children: R
205
292
  * and the line used to take its own 0.35 on top — 0.35 × 0.35, about 12 %, a mark
206
293
  * gone rather than dimmed, while the square beside it read at the full 35 %. */
207
294
  function LegendMark({ entry, off }: { entry: LegendEntry; off: boolean }) {
295
+ if (entry.icon != null) {
296
+ return (
297
+ <span aria-hidden className="inline-flex shrink-0 items-center">
298
+ {entry.icon}
299
+ </span>
300
+ );
301
+ }
302
+ if (entry.marker === "dot") {
303
+ return (
304
+ <span
305
+ aria-hidden
306
+ className="size-2 shrink-0 rounded-full"
307
+ style={{
308
+ backgroundColor: off ? "transparent" : entry.color,
309
+ boxShadow: `inset 0 0 0 1.5px ${entry.color}`,
310
+ }}
311
+ />
312
+ );
313
+ }
208
314
  if (entry.marker === "stroke") {
209
315
  return (
210
316
  <svg aria-hidden viewBox="0 0 20 2" className="h-0.5 w-5 shrink-0 overflow-visible">
@@ -87,6 +87,19 @@ export interface TooltipProps extends ComponentPropsWithoutRef<"span"> {
87
87
  * what they were before the default existed.
88
88
  */
89
89
  portal?: boolean;
90
+ /**
91
+ * Mount the IN-PLACE bubble only while it is up (hovered or focused, not dismissed),
92
+ * instead of always. It still sits next to its trigger and is still placed by CSS
93
+ * alone; what changes is that, while closed, it is not in the DOM — so it is not in
94
+ * `getAllByRole("tooltip")`, not in an ancestor's `textContent` and not in an
95
+ * ancestor's accessible name. The trigger is described (`aria-describedby`) while
96
+ * the bubble is up, exactly as a portalled one is. See "Lazy in place" on
97
+ * {@link Tooltip} for why this exists (keksdose F6) and why it is not the default.
98
+ *
99
+ * Has no effect on a bubble that is portalled (`portal`, or auto-portalled inside a
100
+ * clipping container) — that one is already mounted only while up.
101
+ */
102
+ lazy?: boolean;
90
103
  /** Tag the bubble `data-private`, for a label that repeats the user's own data. */
91
104
  redact?: boolean;
92
105
  children: ReactNode;
@@ -94,7 +107,7 @@ export interface TooltipProps extends ComponentPropsWithoutRef<"span"> {
94
107
 
95
108
  /** What each variant below takes: the resolved `side`, and every span attribute the
96
109
  * caller handed {@link Tooltip}, forwarded to that variant's own wrapper. */
97
- type TooltipVariantProps = Omit<TooltipProps, "side" | "portal"> & { side: TooltipSide };
110
+ type TooltipVariantProps = Omit<TooltipProps, "side" | "portal" | "lazy"> & { side: TooltipSide };
98
111
 
99
112
  /**
100
113
  * Hover/focus label for a control.
@@ -170,12 +183,50 @@ type TooltipVariantProps = Omit<TooltipProps, "side" | "portal"> & { side: Toolt
170
183
  * Why not simply portal everything? Because the in-place bubble is the one existing
171
184
  * app tests rely on (it is in the DOM without a hover), and because it follows its
172
185
  * trigger through a scroll or an animation for free — the portalled one re-measures.
186
+ *
187
+ * ⚠️ **Lazy in place: `lazy` keeps the bubble next to its trigger but out of the DOM
188
+ * until it is up (keksdose F6).** `data-clips` cured the doubled text for tooltips
189
+ * inside a table; it did nothing for the ones outside any clipping container, where the
190
+ * always-mounted bubble is still a real `role="tooltip"` node full of text. About seven
191
+ * keksdose sites — column-header tooltips, a direction toggle, an fx-estimate row, a
192
+ * FlagBadge, a toast inside a `<button>` — still pinned `portal` for that alone: their
193
+ * tests found two tooltips with `getAllByRole("tooltip")`, read the label twice in a
194
+ * `textContent`, or got a button whose accessible name had the bubble's sentence glued
195
+ * on. `portal` fixed the test and cost the browser the measured, re-positioning bubble
196
+ * for no reason. `lazy` is the in-place bubble with the portalled one's lifetime: it is
197
+ * rendered only while hovered or focused (and not dismissed), in the same slot, with the
198
+ * same classes, so it looks and sits exactly as the default one does when it is up.
199
+ *
200
+ * Why unmount rather than hide? Hiding was the other option — keep the bubble mounted
201
+ * with `hidden` (or `aria-hidden` plus `display: none` until `:hover` / `:focus-within`)
202
+ * and let `aria-describedby` go on pointing at it, which the accname algorithm allows:
203
+ * a node referenced directly by `aria-describedby` contributes its text even while
204
+ * hidden. That would take the bubble out of the role queries and out of an ancestor's
205
+ * accessible name — but NOT out of `textContent`, which is plain DOM and counts hidden
206
+ * text too, and `textContent` is one of the three things F6 lists. Only a bubble that
207
+ * is not there solves all three. The cost is the portalled variant's: the trigger is
208
+ * described while the bubble is up rather than always. Focusing the trigger IS what
209
+ * puts it up, and React commits the `aria-describedby` in the same task as that focus
210
+ * event, so a screen reader landing on the control still hears it; what goes is the description
211
+ * of a trigger that is read in browse mode without ever being focused.
212
+ *
213
+ * Escape still dismisses it — here by unmounting it, which takes it off the screen and
214
+ * out of the accessibility tree at once — and the next hover or focus brings it back.
215
+ * There is no fade to lose: neither in-place bubble has ever had a transition, only the
216
+ * `opacity` switch, so appearing on a state change looks the same as appearing on
217
+ * `:hover`.
218
+ *
219
+ * Why opt-in rather than the new default? Because the always-mounted bubble is the one
220
+ * existing app tests find without a hover (see above): flipping it would turn every
221
+ * `getByRole("tooltip")` written against 0.12 into a failure in all three apps at once.
222
+ * Reach for `lazy` wherever a `portal` was pinned only to keep a test's DOM clean.
173
223
  */
174
224
  export function Tooltip({
175
225
  label,
176
226
  side = "top",
177
227
  className,
178
228
  portal,
229
+ lazy = false,
179
230
  redact = false,
180
231
  children,
181
232
  ...rest
@@ -208,6 +259,7 @@ export function Tooltip({
208
259
  className={className}
209
260
  redact={redact}
210
261
  detect={portal === undefined}
262
+ lazy={lazy}
211
263
  {...rest}
212
264
  >
213
265
  {children}
@@ -235,9 +287,10 @@ function InPlaceTooltip({
235
287
  className,
236
288
  redact,
237
289
  detect,
290
+ lazy,
238
291
  children,
239
292
  ...rest
240
- }: TooltipVariantProps & { detect: boolean }) {
293
+ }: TooltipVariantProps & { detect: boolean; lazy: boolean }) {
241
294
  const id = useId();
242
295
  const triggerRef = useRef<HTMLSpanElement | null>(null);
243
296
  const [clipped, setClipped] = useState(false);
@@ -263,6 +316,9 @@ function InPlaceTooltip({
263
316
  setClipped(hasClippingAncestor(el));
264
317
  };
265
318
  return (
319
+ /* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- the handlers track
320
+ whether the bubble is up and activate nothing; the caller's child is the
321
+ interactive element, keeps its own handlers, and its focus shows the bubble too. */
266
322
  <span
267
323
  // `...rest` first: the four handlers below are what decides whether a bubble is
268
324
  // up, and a caller passing an `onFocus` of its own must not replace them.
@@ -272,10 +328,6 @@ function InPlaceTooltip({
272
328
  // These four track WHETHER A BUBBLE IS UP. They activate nothing — the only thing
273
329
  // here that can be activated is the caller's child, which keeps every handler it
274
330
  // arrived with — so this wrapper needs no role and no key handling of its own.
275
- // `jsx-a11y/no-static-element-interactions` warns about it all the same, as it
276
- // already does about the portal variant's identical trigger below; both are left
277
- // visible rather than silenced, because a rule this package ratchets should be
278
- // argued with in the backlog and not in a disable comment.
279
331
  onMouseEnter={(e) => {
280
332
  setHovered(true);
281
333
  arm(e.currentTarget);
@@ -287,12 +339,28 @@ function InPlaceTooltip({
287
339
  }}
288
340
  onBlur={() => setFocused(false)}
289
341
  >
290
- {/* In place the bubble is always there to point at; portalled, only while up. */}
291
- {describedBy(children, clipped ? (open ? id : undefined) : dismissed ? undefined : id)}
342
+ {/* In place the bubble is always there to point at; portalled or lazy, only while up. */}
343
+ {describedBy(children, clipped || lazy ? (open ? id : undefined) : dismissed ? undefined : id)}
292
344
  {clipped ? (
293
345
  open && (
294
346
  <PortalBubble triggerRef={triggerRef} id={id} label={label} side={side} dir={dir} redact={redact} />
295
347
  )
348
+ ) : lazy ? (
349
+ // keksdose F6: the in-place slot and classes, the portalled lifetime. Mounted only
350
+ // while up, so it is visible whenever it exists — `opacity-100` outright rather
351
+ // than the `group-hover` switch, which would also be right but would make the
352
+ // bubble's visibility depend on two sources (the state that mounted it and the
353
+ // CSS that shows it) that can disagree for a frame after Escape.
354
+ open && (
355
+ <span
356
+ id={id}
357
+ role="tooltip"
358
+ data-private={redact ? "" : undefined}
359
+ className={cn(TOOLTIP_SURFACE, "pointer-events-none absolute z-50 opacity-100", sidePositionClass[side])}
360
+ >
361
+ {label}
362
+ </span>
363
+ )
296
364
  ) : (
297
365
  <span
298
366
  id={id}
@@ -506,6 +574,9 @@ function PortalTooltip({
506
574
 
507
575
  return (
508
576
  <>
577
+ {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- as in
578
+ `InPlaceTooltip`: the handlers only show and hide the bubble; the caller's child
579
+ is the interactive element, and focusing it shows the bubble. */}
509
580
  <span
510
581
  // As in `InPlaceTooltip`: the caller's attributes first, the four handlers that
511
582
  // run this component after them. The BUBBLE is deliberately not given them — it