@eifi1/ui-kit 0.21.0 → 0.23.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 (320) hide show
  1. package/README.md +42 -25
  2. package/dist/chart.d.ts +19 -14
  3. package/dist/components/account-chips.d.ts +10 -3
  4. package/dist/components/account-chips.js +5 -2
  5. package/dist/components/account-chips.js.map +1 -1
  6. package/dist/components/account-settings.d.ts +33 -2
  7. package/dist/components/account-settings.js +43 -21
  8. package/dist/components/account-settings.js.map +1 -1
  9. package/dist/components/amount-input.d.ts +53 -14
  10. package/dist/components/amount-input.js +17 -28
  11. package/dist/components/amount-input.js.map +1 -1
  12. package/dist/components/autocomplete.d.ts +29 -0
  13. package/dist/components/autocomplete.js +134 -104
  14. package/dist/components/autocomplete.js.map +1 -1
  15. package/dist/components/button-group.d.ts +19 -14
  16. package/dist/components/calculator.d.ts +19 -14
  17. package/dist/components/checkbox-group.d.ts +104 -0
  18. package/dist/components/checkbox-group.js +111 -0
  19. package/dist/components/checkbox-group.js.map +1 -0
  20. package/dist/components/checkbox.d.ts +19 -0
  21. package/dist/components/checkbox.js +30 -7
  22. package/dist/components/checkbox.js.map +1 -1
  23. package/dist/components/chip.d.ts +18 -0
  24. package/dist/components/chip.js +47 -10
  25. package/dist/components/chip.js.map +1 -1
  26. package/dist/components/column-mapper.d.ts +182 -0
  27. package/dist/components/column-mapper.js +375 -0
  28. package/dist/components/column-mapper.js.map +1 -0
  29. package/dist/components/combobox-core.d.ts +39 -2
  30. package/dist/components/combobox-core.js +29 -6
  31. package/dist/components/combobox-core.js.map +1 -1
  32. package/dist/components/combobox.d.ts +91 -2
  33. package/dist/components/combobox.js +454 -308
  34. package/dist/components/combobox.js.map +1 -1
  35. package/dist/components/confirm-dialog.d.ts +69 -161
  36. package/dist/components/confirm-dialog.js +7 -8
  37. package/dist/components/confirm-dialog.js.map +1 -1
  38. package/dist/components/copy-button.d.ts +16 -11
  39. package/dist/components/country-select.d.ts +167 -0
  40. package/dist/components/country-select.js +293 -0
  41. package/dist/components/country-select.js.map +1 -0
  42. package/dist/components/currency-select.d.ts +17 -1
  43. package/dist/components/currency-select.js +56 -23
  44. package/dist/components/currency-select.js.map +1 -1
  45. package/dist/components/danger-confirm.d.ts +69 -166
  46. package/dist/components/danger-confirm.js +144 -50
  47. package/dist/components/danger-confirm.js.map +1 -1
  48. package/dist/components/data-table-cells.d.ts +1 -2
  49. package/dist/components/data-table-filter-popover.d.ts +1 -2
  50. package/dist/components/data-table-filters.d.ts +1 -2
  51. package/dist/components/data-table-labels.d.ts +4 -97
  52. package/dist/components/data-table-labels.js +9 -1
  53. package/dist/components/data-table-labels.js.map +1 -1
  54. package/dist/components/data-table-pagination.d.ts +3 -1
  55. package/dist/components/data-table.d.ts +1 -2
  56. package/dist/components/data-table.js +98 -3
  57. package/dist/components/data-table.js.map +1 -1
  58. package/dist/components/date-picker.d.ts +66 -5
  59. package/dist/components/date-picker.js +378 -66
  60. package/dist/components/date-picker.js.map +1 -1
  61. package/dist/components/entity-combobox.d.ts +42 -1
  62. package/dist/components/entity-combobox.js +154 -113
  63. package/dist/components/entity-combobox.js.map +1 -1
  64. package/dist/components/facing-pair.d.ts +19 -14
  65. package/dist/components/field-parts.d.ts +224 -0
  66. package/dist/components/field-parts.js +156 -0
  67. package/dist/components/field-parts.js.map +1 -0
  68. package/dist/components/field-strip.d.ts +105 -0
  69. package/dist/components/field-strip.js +46 -0
  70. package/dist/components/field-strip.js.map +1 -0
  71. package/dist/components/file-button.d.ts +19 -14
  72. package/dist/components/file-button.js +13 -3
  73. package/dist/components/file-button.js.map +1 -1
  74. package/dist/components/file-dropzone.d.ts +41 -15
  75. package/dist/components/file-dropzone.js +57 -34
  76. package/dist/components/file-dropzone.js.map +1 -1
  77. package/dist/components/form-actions.d.ts +18 -13
  78. package/dist/components/form-actions.js +69 -5
  79. package/dist/components/form-actions.js.map +1 -1
  80. package/dist/components/iban-input.d.ts +69 -0
  81. package/dist/components/iban-input.js +125 -0
  82. package/dist/components/iban-input.js.map +1 -0
  83. package/dist/components/icon-picker.d.ts +21 -2
  84. package/dist/components/icon-picker.js +19 -4
  85. package/dist/components/icon-picker.js.map +1 -1
  86. package/dist/components/inline-edit-field.d.ts +167 -0
  87. package/dist/components/inline-edit-field.js +293 -0
  88. package/dist/components/inline-edit-field.js.map +1 -0
  89. package/dist/components/language-select.d.ts +130 -0
  90. package/dist/components/language-select.js +18 -0
  91. package/dist/components/language-select.js.map +1 -0
  92. package/dist/components/line-items.d.ts +25 -3
  93. package/dist/components/line-items.js +55 -46
  94. package/dist/components/line-items.js.map +1 -1
  95. package/dist/components/list.d.ts +25 -0
  96. package/dist/components/list.js +16 -5
  97. package/dist/components/list.js.map +1 -1
  98. package/dist/components/money-field.d.ts +32 -14
  99. package/dist/components/money-field.js.map +1 -1
  100. package/dist/components/month-picker.d.ts +52 -4
  101. package/dist/components/month-picker.js +291 -86
  102. package/dist/components/month-picker.js.map +1 -1
  103. package/dist/components/multi-entity-combobox.d.ts +26 -1
  104. package/dist/components/multi-entity-combobox.js +143 -102
  105. package/dist/components/multi-entity-combobox.js.map +1 -1
  106. package/dist/components/multi-select.d.ts +8 -1
  107. package/dist/components/multi-select.js +9 -4
  108. package/dist/components/multi-select.js.map +1 -1
  109. package/dist/components/number-field.d.ts +34 -19
  110. package/dist/components/number-field.js +6 -20
  111. package/dist/components/number-field.js.map +1 -1
  112. package/dist/components/number-input.d.ts +79 -19
  113. package/dist/components/number-input.js +90 -38
  114. package/dist/components/number-input.js.map +1 -1
  115. package/dist/components/numpad-sheet.d.ts +19 -14
  116. package/dist/components/one-time-code-input.d.ts +100 -0
  117. package/dist/components/one-time-code-input.js +137 -0
  118. package/dist/components/one-time-code-input.js.map +1 -0
  119. package/dist/components/phone-input.d.ts +69 -0
  120. package/dist/components/phone-input.js +171 -0
  121. package/dist/components/phone-input.js.map +1 -0
  122. package/dist/components/reauth-dialog.d.ts +2 -2
  123. package/dist/components/reauth-dialog.js +6 -8
  124. package/dist/components/reauth-dialog.js.map +1 -1
  125. package/dist/components/series-chart.d.ts +19 -14
  126. package/dist/components/settings-fields.d.ts +19 -14
  127. package/dist/components/share-card.d.ts +16 -11
  128. package/dist/components/sign-chip.d.ts +66 -0
  129. package/dist/components/sign-chip.js +39 -0
  130. package/dist/components/sign-chip.js.map +1 -0
  131. package/dist/components/swatch-picker.d.ts +43 -2
  132. package/dist/components/swatch-picker.js +80 -32
  133. package/dist/components/swatch-picker.js.map +1 -1
  134. package/dist/components/switch.d.ts +19 -0
  135. package/dist/components/switch.js +24 -6
  136. package/dist/components/switch.js.map +1 -1
  137. package/dist/components/text-link.d.ts +18 -13
  138. package/dist/components/tile-radio.d.ts +88 -13
  139. package/dist/components/tile-radio.js +28 -11
  140. package/dist/components/tile-radio.js.map +1 -1
  141. package/dist/components/time-input.d.ts +24 -15
  142. package/dist/components/time-input.js.map +1 -1
  143. package/dist/components/toggle-group.d.ts +47 -1
  144. package/dist/components/toggle-group.js +110 -69
  145. package/dist/components/toggle-group.js.map +1 -1
  146. package/dist/components/trigger-aria.d.ts +3 -0
  147. package/dist/components/trigger-aria.js +3 -1
  148. package/dist/components/trigger-aria.js.map +1 -1
  149. package/dist/components/ui.d.ts +16 -11
  150. package/dist/components/ui.js +284 -88
  151. package/dist/components/ui.js.map +1 -1
  152. package/dist/components/use-table-state.d.ts +1 -2
  153. package/dist/{data-table-3mi2R3so.d.ts → data-table-labels-B7OdnM0S.d.ts} +140 -3
  154. package/dist/data-table.d.ts +1 -2
  155. package/dist/data-table.js +2 -1
  156. package/dist/data-table.js.map +1 -1
  157. package/dist/feedback/feedback-attachment.d.ts +1 -1
  158. package/dist/feedback/feedback-attachment.js +348 -41
  159. package/dist/feedback/feedback-attachment.js.map +1 -1
  160. package/dist/feedback/feedback-dialog.d.ts +1 -1
  161. package/dist/feedback/feedback-dialog.js.map +1 -1
  162. package/dist/feedback/feedback-inbox.d.ts +3 -2
  163. package/dist/feedback/feedback-inbox.js.map +1 -1
  164. package/dist/feedback/feedback-thread.d.ts +68 -253
  165. package/dist/feedback/feedback-thread.js +46 -10
  166. package/dist/feedback/feedback-thread.js.map +1 -1
  167. package/dist/{kit-labels-D1J2_jyD.d.ts → feedback-BxeQVzwq.d.ts} +1206 -20
  168. package/dist/{feedback-attachment-WFiXJ8We.d.ts → feedback-attachment-fGAzZPf0.d.ts} +178 -11
  169. package/dist/feedback.d.ts +66 -2
  170. package/dist/hooks/use-file-drop.d.ts +19 -14
  171. package/dist/i18n/defaults.d.ts +19 -14
  172. package/dist/i18n/defaults.js +19 -2
  173. package/dist/i18n/defaults.js.map +1 -1
  174. package/dist/i18n/german.d.ts +19 -14
  175. package/dist/i18n/german.js +84 -5
  176. package/dist/i18n/german.js.map +1 -1
  177. package/dist/i18n/kit-labels.d.ts +15 -10
  178. package/dist/i18n/kit-labels.js +8 -2
  179. package/dist/i18n/kit-labels.js.map +1 -1
  180. package/dist/i18n/languages.d.ts +19 -14
  181. package/dist/i18n/locales/de-CH.d.ts +19 -14
  182. package/dist/i18n/locales/en.d.ts +19 -14
  183. package/dist/i18n/locales/en.js +16 -0
  184. package/dist/i18n/locales/en.js.map +1 -1
  185. package/dist/i18n/locales/es.d.ts +19 -14
  186. package/dist/i18n/locales/es.js +83 -5
  187. package/dist/i18n/locales/es.js.map +1 -1
  188. package/dist/i18n/locales/fr.d.ts +19 -14
  189. package/dist/i18n/locales/fr.js +82 -5
  190. package/dist/i18n/locales/fr.js.map +1 -1
  191. package/dist/i18n/locales/hu.d.ts +19 -14
  192. package/dist/i18n/locales/hu.js +85 -5
  193. package/dist/i18n/locales/hu.js.map +1 -1
  194. package/dist/i18n/locales/it.d.ts +19 -14
  195. package/dist/i18n/locales/it.js +84 -5
  196. package/dist/i18n/locales/it.js.map +1 -1
  197. package/dist/i18n/locales/zh.d.ts +19 -14
  198. package/dist/i18n/locales/zh.js +80 -5
  199. package/dist/i18n/locales/zh.js.map +1 -1
  200. package/dist/i18n/review.d.ts +19 -14
  201. package/dist/i18n/review.js +27 -1
  202. package/dist/i18n/review.js.map +1 -1
  203. package/dist/index.d.ts +21 -10
  204. package/dist/index.js +73 -2
  205. package/dist/index.js.map +1 -1
  206. package/dist/lib/column-mapping.d.ts +81 -0
  207. package/dist/lib/column-mapping.js +108 -0
  208. package/dist/lib/column-mapping.js.map +1 -0
  209. package/dist/lib/countries.d.ts +43 -0
  210. package/dist/lib/countries.js +39 -0
  211. package/dist/lib/countries.js.map +1 -0
  212. package/dist/lib/decimal-marks.d.ts +46 -0
  213. package/dist/lib/decimal-marks.js +40 -0
  214. package/dist/lib/decimal-marks.js.map +1 -0
  215. package/dist/lib/iban.d.ts +98 -0
  216. package/dist/lib/iban.js +154 -0
  217. package/dist/lib/iban.js.map +1 -0
  218. package/dist/lib/isin.d.ts +30 -0
  219. package/dist/lib/isin.js +36 -0
  220. package/dist/lib/isin.js.map +1 -0
  221. package/dist/lib/phone.d.ts +69 -0
  222. package/dist/lib/phone.js +121 -0
  223. package/dist/lib/phone.js.map +1 -0
  224. package/dist/lib/table-text.d.ts +109 -1
  225. package/dist/lib/table-text.js +122 -1
  226. package/dist/lib/table-text.js.map +1 -1
  227. package/dist/rhf/fields.d.ts +233 -18
  228. package/dist/rhf/fields.js +456 -3
  229. package/dist/rhf/fields.js.map +1 -1
  230. package/dist/rhf/form.d.ts +19 -14
  231. package/dist/rhf.d.ts +22 -15
  232. package/dist/rhf.js.map +1 -1
  233. package/dist/shell/app-shell.d.ts +18 -13
  234. package/dist/shell/top-bar-brand.d.ts +19 -14
  235. package/dist/shell/topbar-action-menu.d.ts +36 -3
  236. package/dist/shell/topbar-action-menu.js +74 -33
  237. package/dist/shell/topbar-action-menu.js.map +1 -1
  238. package/dist/shell.d.ts +18 -13
  239. package/dist/table-text.d.ts +1 -1
  240. package/dist/tour/tour.js +20 -24
  241. package/dist/tour/tour.js.map +1 -1
  242. package/dist/wizard/stepper-nav.d.ts +49 -16
  243. package/dist/wizard/stepper-nav.js +4 -0
  244. package/dist/wizard/stepper-nav.js.map +1 -1
  245. package/dist/wizard.d.ts +19 -14
  246. package/package.json +1 -1
  247. package/src/components/account-chips.tsx +17 -3
  248. package/src/components/account-settings.tsx +92 -26
  249. package/src/components/amount-input.tsx +68 -56
  250. package/src/components/autocomplete.tsx +198 -114
  251. package/src/components/checkbox-group.tsx +229 -0
  252. package/src/components/checkbox.tsx +72 -8
  253. package/src/components/chip.tsx +80 -10
  254. package/src/components/column-mapper.tsx +666 -0
  255. package/src/components/combobox-core.tsx +82 -9
  256. package/src/components/combobox.tsx +661 -340
  257. package/src/components/confirm-dialog.tsx +12 -8
  258. package/src/components/country-select.tsx +556 -0
  259. package/src/components/currency-select.tsx +109 -33
  260. package/src/components/danger-confirm.tsx +429 -82
  261. package/src/components/data-table-labels.ts +16 -1
  262. package/src/components/data-table.tsx +175 -6
  263. package/src/components/date-picker.tsx +595 -71
  264. package/src/components/entity-combobox.tsx +235 -124
  265. package/src/components/field-parts.tsx +403 -0
  266. package/src/components/field-strip.tsx +149 -0
  267. package/src/components/file-button.tsx +36 -3
  268. package/src/components/file-dropzone.tsx +106 -38
  269. package/src/components/form-actions.tsx +147 -6
  270. package/src/components/iban-input.tsx +231 -0
  271. package/src/components/icon-picker.tsx +47 -6
  272. package/src/components/inline-edit-field.tsx +533 -0
  273. package/src/components/language-select.tsx +78 -0
  274. package/src/components/line-items.tsx +35 -2
  275. package/src/components/list.tsx +55 -6
  276. package/src/components/money-field.tsx +13 -0
  277. package/src/components/month-picker.tsx +400 -63
  278. package/src/components/multi-entity-combobox.tsx +207 -120
  279. package/src/components/multi-select.tsx +80 -58
  280. package/src/components/number-field.tsx +28 -46
  281. package/src/components/number-input.tsx +210 -43
  282. package/src/components/one-time-code-input.tsx +288 -0
  283. package/src/components/phone-input.tsx +299 -0
  284. package/src/components/reauth-dialog.tsx +17 -18
  285. package/src/components/sign-chip.tsx +103 -0
  286. package/src/components/swatch-picker.tsx +143 -31
  287. package/src/components/switch.tsx +58 -7
  288. package/src/components/tile-radio.tsx +145 -29
  289. package/src/components/time-input.tsx +8 -1
  290. package/src/components/toggle-group.tsx +206 -73
  291. package/src/components/trigger-aria.ts +5 -0
  292. package/src/components/ui.tsx +479 -138
  293. package/src/data-table.ts +2 -1
  294. package/src/feedback/feedback-attachment.tsx +597 -46
  295. package/src/feedback/feedback-dialog.tsx +9 -3
  296. package/src/feedback/feedback-inbox.tsx +3 -2
  297. package/src/feedback/feedback-thread.tsx +113 -9
  298. package/src/i18n/defaults.ts +18 -1
  299. package/src/i18n/german.ts +89 -0
  300. package/src/i18n/kit-labels.tsx +82 -2
  301. package/src/i18n/locales/en.ts +36 -4
  302. package/src/i18n/locales/es.ts +87 -0
  303. package/src/i18n/locales/fr.ts +87 -0
  304. package/src/i18n/locales/hu.ts +83 -0
  305. package/src/i18n/locales/it.ts +88 -0
  306. package/src/i18n/locales/zh.ts +76 -0
  307. package/src/i18n/review.ts +26 -0
  308. package/src/index.ts +71 -3
  309. package/src/lib/column-mapping.ts +234 -0
  310. package/src/lib/countries.ts +91 -0
  311. package/src/lib/decimal-marks.ts +99 -0
  312. package/src/lib/iban.ts +171 -0
  313. package/src/lib/isin.ts +62 -0
  314. package/src/lib/phone.ts +211 -0
  315. package/src/lib/table-text.ts +271 -0
  316. package/src/rhf/fields.tsx +860 -9
  317. package/src/rhf.ts +3 -1
  318. package/src/shell/topbar-action-menu.tsx +134 -38
  319. package/src/tour/tour.tsx +66 -28
  320. package/src/wizard/stepper-nav.tsx +35 -1
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useLayoutEffect, useRef, useState } from "react";
2
- import type { ComponentPropsWithoutRef, CSSProperties, KeyboardEvent, Key, ReactNode } from "react";
2
+ import type { ComponentPropsWithoutRef, CSSProperties, HTMLAttributes, KeyboardEvent, Key, ReactNode } from "react";
3
3
  import { Plus, Trash2 } from "lucide-react";
4
4
  import { cn } from "../lib/cn";
5
5
  import { useKitLabels } from "../i18n/kit-labels";
@@ -112,6 +112,15 @@ export interface LineItemsColumn<T> {
112
112
  render: (ctx: LineItemCellContext<T>) => ReactNode;
113
113
  }
114
114
 
115
+ /**
116
+ * What {@link LineItemsProps.rowProps} returns for one row: `data-*` hooks, an `id`, a
117
+ * class, handlers. A `data-*` attribute is typed so `{ "data-tour": "rule-condition" }`
118
+ * needs no cast.
119
+ */
120
+ export type LineItemsRowProps = Omit<HTMLAttributes<HTMLDivElement>, "children" | "dangerouslySetInnerHTML" | "role"> & {
121
+ [key: `data-${string}`]: string | number | boolean | undefined;
122
+ };
123
+
115
124
  export interface LineItemsProps<T> extends Omit<ComponentPropsWithoutRef<"div">, "children"> {
116
125
  items: readonly T[];
117
126
  columns: readonly LineItemsColumn<T>[];
@@ -170,6 +179,20 @@ export interface LineItemsProps<T> extends Omit<ComponentPropsWithoutRef<"div">,
170
179
  removeAlign?: LineItemsRemoveAlign;
171
180
  /** Disables add and remove (the fields are the caller's to disable). */
172
181
  disabled?: boolean;
182
+ /**
183
+ * Extra attributes for one row's element (the `role="group"` that holds its fields) —
184
+ * keksdose K20: its rule editor's tour points at the first condition row with
185
+ * `[data-tour="rule-condition"]` (dev#495), and with nowhere to put the anchor on the
186
+ * row it wrapped a cell's field in a `span` instead, which the spotlight then drew
187
+ * around one field rather than the row. Test ids the same way.
188
+ *
189
+ * MERGED under the list's own, which win: `role`, the group's `aria-label` (that is
190
+ * {@link rowLabel}'s) and `data-line-items-row` stay the list's — the row's name and
191
+ * the hook its focus handling finds rows by. `className` is added after the row's
192
+ * own classes, `style` is passed through, and an `onKeyDown` runs BEFORE the row's
193
+ * Ctrl/⌘+Enter "add a row" (call `preventDefault()` to keep that from happening).
194
+ */
195
+ rowProps?: (item: T, index: number) => LineItemsRowProps | undefined;
173
196
  labels?: Partial<LineItemsLabels>;
174
197
  }
175
198
 
@@ -253,6 +276,7 @@ export function LineItems<T>({
253
276
  removePlacement = "row",
254
277
  removeAlign: removeAlignProp,
255
278
  disabled = false,
279
+ rowProps,
256
280
  labels: labelsProp,
257
281
  className,
258
282
  style,
@@ -405,11 +429,13 @@ export function LineItems<T>({
405
429
  {items.map((item, index) => {
406
430
  const row = index + 1;
407
431
  const isConfirming = confirming === index;
432
+ const extra = rowProps?.(item, index);
408
433
  return (
409
434
  // The row's Ctrl/⌘+Enter is a shortcut for the add button, which is itself
410
435
  // reachable by Tab; the group only listens for it bubbling from its fields.
411
436
  // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
412
437
  <div
438
+ {...extra}
413
439
  key={getKey(item, index)}
414
440
  ref={(el) => {
415
441
  rowRefs.current[index] = el;
@@ -417,7 +443,13 @@ export function LineItems<T>({
417
443
  role="group"
418
444
  aria-label={rowLabel ? rowLabel(index, item) : labels.row(row)}
419
445
  data-line-items-row=""
420
- onKeyDown={onRowKeyDown}
446
+ onKeyDown={(e) => {
447
+ if (extra?.onKeyDown) {
448
+ extra.onKeyDown(e);
449
+ if (e.defaultPrevented) return;
450
+ }
451
+ onRowKeyDown(e);
452
+ }}
421
453
  className={cn(
422
454
  "grid grid-cols-1 gap-2 @lg:grid-cols-[var(--line-items-cols)] @lg:items-start",
423
455
  // Two short fields to a row of the card — from 18rem, where two fields
@@ -425,6 +457,7 @@ export function LineItems<T>({
425
457
  pairs && "@2xs:grid-cols-2",
426
458
  // Stacked, each row is a card of its own so the rows stay apart.
427
459
  "@max-lg:rounded-md @max-lg:border @max-lg:border-[var(--border)] @max-lg:p-3",
460
+ extra?.className,
428
461
  )}
429
462
  >
430
463
  {columns.map((column) => {
@@ -10,6 +10,8 @@ import { toneTextClass } from "./signed-amount";
10
10
  import type { TextTone } from "./signed-amount";
11
11
  import { useKitLabels, useKitLink } from "../i18n/kit-labels";
12
12
  import { pickLinkRenderer } from "./text-link";
13
+ import { mergeDescribedBy } from "./choice-parts";
14
+ import { LockedReason, useLockReason } from "./field-parts";
13
15
 
14
16
  /* ── Labels ───────────────────────────────────────────────────────────────── */
15
17
 
@@ -309,6 +311,25 @@ export type ListItemProps = ListItemBaseProps &
309
311
  | ({
310
312
  /** Makes the row a button — select, open, mark read. */
311
313
  onClick: (event: MouseEvent<HTMLButtonElement>) => void;
314
+ /**
315
+ * Why the row's click is not available — {@link Button}'s `disabledReason`, for
316
+ * a row whose click WRITES (mark read, assign, toggle a grant). keksdose K3: such
317
+ * rows sat behind a `SaveGuard` that forced a native `disabled`, out of the tab
318
+ * order, so the reason never reached a keyboard; `renderRow` with a Tooltip could
319
+ * explain it but not keep the row reachable.
320
+ *
321
+ * With a reason the row's button is `aria-disabled` instead: still focusable, a
322
+ * click does nothing, the reason is in the kit {@link Tooltip} over the row and on
323
+ * its `aria-describedby`, and the row wears the disabled look. It wins over
324
+ * `disabled`. The row's `actions` are untouched — each control there is its own.
325
+ */
326
+ disabledReason?: ReactNode;
327
+ /**
328
+ * The row's click COMMITS. Under a locked {@link WriteLockProvider} it is locked
329
+ * the `disabledReason` way with the lock's reason (which wins over its own). No
330
+ * provider, or an unlocked one: no effect. Button's `commit`, for keksdose K3.
331
+ */
332
+ commit?: boolean;
312
333
  href?: never;
313
334
  renderLink?: never;
314
335
  external?: never;
@@ -337,6 +358,8 @@ export type ListItemProps = ListItemBaseProps &
337
358
  * notification read here too, so a row opened into a background tab does not
338
359
  * stay unread. */
339
360
  onAuxClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
361
+ disabledReason?: never;
362
+ commit?: never;
340
363
  } & NotExpandable)
341
364
  | ({
342
365
  onClick?: never;
@@ -344,6 +367,8 @@ export type ListItemProps = ListItemBaseProps &
344
367
  renderLink?: never;
345
368
  external?: never;
346
369
  onAuxClick?: never;
370
+ disabledReason?: never;
371
+ commit?: never;
347
372
  } & NotExpandable)
348
373
  | {
349
374
  /**
@@ -369,6 +394,8 @@ export type ListItemProps = ListItemBaseProps &
369
394
  renderLink?: never;
370
395
  external?: never;
371
396
  onAuxClick?: never;
397
+ disabledReason?: never;
398
+ commit?: never;
372
399
  }
373
400
  );
374
401
 
@@ -427,6 +454,8 @@ export const ListItem = forwardRef<HTMLElement, ListItemProps>(function ListItem
427
454
  external = false,
428
455
  onClick,
429
456
  onAuxClick,
457
+ disabledReason,
458
+ commit,
430
459
  expandedContent,
431
460
  expanded: expandedProp,
432
461
  defaultExpanded = false,
@@ -446,7 +475,11 @@ export const ListItem = forwardRef<HTMLElement, ListItemProps>(function ListItem
446
475
  const isLink = href !== undefined && !disabled;
447
476
  const isButton = !isLink && (onClick !== undefined || expandable) && href === undefined;
448
477
  const interactive = isLink || isButton;
449
- const inert = disabled || loading;
478
+ // Only an onClick row takes a reason (the types allow no other); the hook runs on
479
+ // every row so the hook order never depends on which kind of row this is.
480
+ const lock = useLockReason(commit, disabledReason);
481
+ const locked = lock.locked && isButton && !expandable;
482
+ const inert = disabled || loading || locked;
450
483
  const hasBody = children !== undefined && children !== null && children !== false;
451
484
  // Anything under the row's own line: the box turns into a column, and the target
452
485
  // takes its own corners and hover fill (see `children`).
@@ -558,6 +591,7 @@ export const ListItem = forwardRef<HTMLElement, ListItemProps>(function ListItem
558
591
  interactive && TARGET_RING,
559
592
  interactive && !inert && "cursor-pointer",
560
593
  loading && "cursor-progress",
594
+ locked && !loading && "cursor-not-allowed",
561
595
  targetClassName,
562
596
  );
563
597
 
@@ -598,15 +632,21 @@ export const ListItem = forwardRef<HTMLElement, ListItemProps>(function ListItem
598
632
  ref={ref as Ref<HTMLButtonElement>}
599
633
  type="button"
600
634
  // A disabled row is `disabled`: nothing to do there, and nothing to reach. A
601
- // loading row is only `aria-disabled` — see `loading`.
602
- disabled={disabled}
603
- aria-disabled={loading || undefined}
635
+ // loading row is only `aria-disabled` — see `loading` — and so is a locked one:
636
+ // a reason wins over `disabled`, so the row stays reachable to say it.
637
+ disabled={locked ? undefined : disabled}
638
+ aria-disabled={loading || locked || undefined}
604
639
  aria-busy={loading || undefined}
605
640
  aria-current={selected ? "true" : undefined}
606
641
  aria-expanded={expandable ? expanded : undefined}
607
642
  aria-controls={expandable ? expandedId : undefined}
643
+ aria-describedby={
644
+ locked
645
+ ? mergeDescribedBy(rest["aria-describedby"] as string | undefined, lock.reasonId)
646
+ : (rest["aria-describedby"] as string | undefined)
647
+ }
608
648
  onClick={
609
- loading
649
+ loading || locked
610
650
  ? undefined
611
651
  : expandable
612
652
  ? toggle
@@ -617,6 +657,15 @@ export const ListItem = forwardRef<HTMLElement, ListItemProps>(function ListItem
617
657
  {body}
618
658
  </button>
619
659
  );
660
+ if (locked) {
661
+ // The Tooltip takes the target's place in the row, so it takes its flex sizing
662
+ // too; the button fills it.
663
+ main = (
664
+ <LockedReason lock={lock} className="flex min-w-0 flex-1">
665
+ {main}
666
+ </LockedReason>
667
+ );
668
+ }
620
669
  } else {
621
670
  main = (
622
671
  <div
@@ -652,7 +701,7 @@ export const ListItem = forwardRef<HTMLElement, ListItemProps>(function ListItem
652
701
  ? "border-[var(--border)]"
653
702
  : "border-transparent",
654
703
  !stacked && interactive && !inert && !selected && "hover:bg-[var(--bg-hover)]",
655
- disabled && "opacity-50",
704
+ (disabled || locked) && "opacity-50",
656
705
  className,
657
706
  )}
658
707
  >
@@ -39,6 +39,19 @@ function parseSettled(text: string): number | null | undefined {
39
39
  * rounding to the currency's minor unit and `min` / `max` are AmountInput's. A caller-
40
40
  * owned sign (`negative`) is not offered: the figure here carries its own minus.
41
41
  *
42
+ * So are two props a table cell needs (keksdose K4, K6): `error`, the message under the
43
+ * field that paints and describes it like {@link Input}'s, and `calculator={false}`,
44
+ * for an amount in a close-on-blur inline editor, where a click on the calculator icon
45
+ * blurs the field and tears the editor down before the popover can open (typed
46
+ * calculations still evaluate).
47
+ *
48
+ * `inputClassName` (0.23, keksdose G7) styles the `<input>` itself — `text-end` for a
49
+ * money column — where `className` sizes the wrapper; see {@link AmountInput}'s.
50
+ *
51
+ * This, not a `NumberField` with `digits={2}`, is the number-valued field for money:
52
+ * it settles to the CURRENCY's minor unit (JPY 0, CHF 2) and `digits` takes a unit
53
+ * price's finer scale (kastlan 5).
54
+ *
42
55
  * ```tsx
43
56
  * <MoneyField ariaLabel="Amount" currency="CHF" value={row.amount}
44
57
  * onCommit={(amount) => updateRow(row.id, { amount })} />