@eifi1/ui-kit 0.20.1 → 0.22.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 (286) hide show
  1. package/dist/chart.d.ts +10 -8
  2. package/dist/components/account-chips.d.ts +10 -3
  3. package/dist/components/account-chips.js +5 -2
  4. package/dist/components/account-chips.js.map +1 -1
  5. package/dist/components/account-settings.d.ts +33 -2
  6. package/dist/components/account-settings.js +43 -21
  7. package/dist/components/account-settings.js.map +1 -1
  8. package/dist/components/amount-input.d.ts +27 -8
  9. package/dist/components/amount-input.js +15 -28
  10. package/dist/components/amount-input.js.map +1 -1
  11. package/dist/components/autocomplete.d.ts +8 -0
  12. package/dist/components/autocomplete.js +9 -4
  13. package/dist/components/autocomplete.js.map +1 -1
  14. package/dist/components/button-group.d.ts +10 -8
  15. package/dist/components/calculator.d.ts +10 -8
  16. package/dist/components/checkbox-group.d.ts +104 -0
  17. package/dist/components/checkbox-group.js +111 -0
  18. package/dist/components/checkbox-group.js.map +1 -0
  19. package/dist/components/checkbox.d.ts +19 -0
  20. package/dist/components/checkbox.js +30 -7
  21. package/dist/components/checkbox.js.map +1 -1
  22. package/dist/components/chip.d.ts +18 -0
  23. package/dist/components/chip.js +47 -10
  24. package/dist/components/chip.js.map +1 -1
  25. package/dist/components/combobox.d.ts +13 -2
  26. package/dist/components/combobox.js +17 -8
  27. package/dist/components/combobox.js.map +1 -1
  28. package/dist/components/confirm-dialog.d.ts +66 -161
  29. package/dist/components/copy-button.d.ts +10 -8
  30. package/dist/components/country-select.d.ts +124 -0
  31. package/dist/components/country-select.js +237 -0
  32. package/dist/components/country-select.js.map +1 -0
  33. package/dist/components/currency-select.d.ts +17 -1
  34. package/dist/components/currency-select.js +56 -23
  35. package/dist/components/currency-select.js.map +1 -1
  36. package/dist/components/danger-confirm.d.ts +66 -166
  37. package/dist/components/danger-confirm.js +133 -49
  38. package/dist/components/danger-confirm.js.map +1 -1
  39. package/dist/components/data-table-cells.d.ts +1 -2
  40. package/dist/components/data-table-filter-popover.d.ts +1 -2
  41. package/dist/components/data-table-filters.d.ts +1 -2
  42. package/dist/components/data-table-labels.d.ts +4 -97
  43. package/dist/components/data-table-labels.js +9 -1
  44. package/dist/components/data-table-labels.js.map +1 -1
  45. package/dist/components/data-table-pagination.d.ts +3 -1
  46. package/dist/components/data-table.d.ts +1 -2
  47. package/dist/components/data-table.js +98 -3
  48. package/dist/components/data-table.js.map +1 -1
  49. package/dist/components/date-picker.d.ts +25 -5
  50. package/dist/components/date-picker.js +110 -59
  51. package/dist/components/date-picker.js.map +1 -1
  52. package/dist/components/entity-combobox.d.ts +8 -1
  53. package/dist/components/entity-combobox.js +9 -4
  54. package/dist/components/entity-combobox.js.map +1 -1
  55. package/dist/components/facing-pair.d.ts +10 -8
  56. package/dist/components/field-anatomy.d.ts +95 -0
  57. package/dist/components/field-anatomy.js +84 -0
  58. package/dist/components/field-anatomy.js.map +1 -0
  59. package/dist/components/field-parts.d.ts +114 -0
  60. package/dist/components/field-parts.js +72 -0
  61. package/dist/components/field-parts.js.map +1 -0
  62. package/dist/components/file-button.d.ts +10 -8
  63. package/dist/components/file-button.js +13 -3
  64. package/dist/components/file-button.js.map +1 -1
  65. package/dist/components/file-dropzone.d.ts +32 -9
  66. package/dist/components/file-dropzone.js +57 -34
  67. package/dist/components/file-dropzone.js.map +1 -1
  68. package/dist/components/form-actions.d.ts +10 -8
  69. package/dist/components/form-actions.js +61 -3
  70. package/dist/components/form-actions.js.map +1 -1
  71. package/dist/components/hover-menu.d.ts +3 -0
  72. package/dist/components/hover-menu.js +2 -2
  73. package/dist/components/hover-menu.js.map +1 -1
  74. package/dist/components/iban-input.d.ts +66 -0
  75. package/dist/components/iban-input.js +125 -0
  76. package/dist/components/iban-input.js.map +1 -0
  77. package/dist/components/icon-picker.d.ts +12 -2
  78. package/dist/components/icon-picker.js +9 -2
  79. package/dist/components/icon-picker.js.map +1 -1
  80. package/dist/components/inline-edit-field.d.ts +167 -0
  81. package/dist/components/inline-edit-field.js +293 -0
  82. package/dist/components/inline-edit-field.js.map +1 -0
  83. package/dist/components/language-select.d.ts +127 -0
  84. package/dist/components/language-select.js +18 -0
  85. package/dist/components/language-select.js.map +1 -0
  86. package/dist/components/line-items.d.ts +25 -3
  87. package/dist/components/line-items.js +55 -46
  88. package/dist/components/line-items.js.map +1 -1
  89. package/dist/components/list.d.ts +25 -0
  90. package/dist/components/list.js +16 -5
  91. package/dist/components/list.js.map +1 -1
  92. package/dist/components/money-field.d.ts +20 -8
  93. package/dist/components/money-field.js.map +1 -1
  94. package/dist/components/month-picker.d.ts +52 -4
  95. package/dist/components/month-picker.js +290 -86
  96. package/dist/components/month-picker.js.map +1 -1
  97. package/dist/components/multi-select.d.ts +8 -1
  98. package/dist/components/multi-select.js +9 -4
  99. package/dist/components/multi-select.js.map +1 -1
  100. package/dist/components/number-field.d.ts +25 -13
  101. package/dist/components/number-field.js +6 -20
  102. package/dist/components/number-field.js.map +1 -1
  103. package/dist/components/number-input.d.ts +70 -13
  104. package/dist/components/number-input.js +90 -38
  105. package/dist/components/number-input.js.map +1 -1
  106. package/dist/components/numpad-sheet.d.ts +10 -8
  107. package/dist/components/one-time-code-input.d.ts +100 -0
  108. package/dist/components/one-time-code-input.js +137 -0
  109. package/dist/components/one-time-code-input.js.map +1 -0
  110. package/dist/components/phone-input.d.ts +66 -0
  111. package/dist/components/phone-input.js +171 -0
  112. package/dist/components/phone-input.js.map +1 -0
  113. package/dist/components/series-chart.d.ts +10 -8
  114. package/dist/components/settings-fields.d.ts +10 -8
  115. package/dist/components/share-card.d.ts +8 -6
  116. package/dist/components/sign-chip.d.ts +66 -0
  117. package/dist/components/sign-chip.js +39 -0
  118. package/dist/components/sign-chip.js.map +1 -0
  119. package/dist/components/swatch-picker.d.ts +27 -2
  120. package/dist/components/swatch-picker.js +73 -31
  121. package/dist/components/swatch-picker.js.map +1 -1
  122. package/dist/components/switch.d.ts +19 -0
  123. package/dist/components/switch.js +24 -6
  124. package/dist/components/switch.js.map +1 -1
  125. package/dist/components/text-link.d.ts +10 -8
  126. package/dist/components/tile-radio.d.ts +68 -13
  127. package/dist/components/tile-radio.js +3 -3
  128. package/dist/components/tile-radio.js.map +1 -1
  129. package/dist/components/time-input.d.ts +15 -9
  130. package/dist/components/time-input.js.map +1 -1
  131. package/dist/components/toggle-group.d.ts +47 -1
  132. package/dist/components/toggle-group.js +110 -69
  133. package/dist/components/toggle-group.js.map +1 -1
  134. package/dist/components/ui.d.ts +10 -8
  135. package/dist/components/ui.js +280 -78
  136. package/dist/components/ui.js.map +1 -1
  137. package/dist/components/use-table-state.d.ts +1 -2
  138. package/dist/{data-table-3mi2R3so.d.ts → data-table-labels-B7OdnM0S.d.ts} +140 -3
  139. package/dist/data-table.d.ts +1 -2
  140. package/dist/data-table.js +2 -1
  141. package/dist/data-table.js.map +1 -1
  142. package/dist/feedback/feedback-attachment.d.ts +1 -1
  143. package/dist/feedback/feedback-attachment.js +210 -3
  144. package/dist/feedback/feedback-attachment.js.map +1 -1
  145. package/dist/feedback/feedback-dialog.d.ts +1 -1
  146. package/dist/feedback/feedback-dialog.js.map +1 -1
  147. package/dist/feedback/feedback-inbox.d.ts +1 -1
  148. package/dist/feedback/feedback-thread.d.ts +37 -3
  149. package/dist/feedback/feedback-thread.js +38 -9
  150. package/dist/feedback/feedback-thread.js.map +1 -1
  151. package/dist/{feedback-attachment-WFiXJ8We.d.ts → feedback-attachment-NhUm7Zga.d.ts} +70 -5
  152. package/dist/feedback.d.ts +2 -2
  153. package/dist/hooks/use-file-drop.d.ts +10 -8
  154. package/dist/i18n/defaults.d.ts +10 -8
  155. package/dist/i18n/defaults.js +17 -2
  156. package/dist/i18n/defaults.js.map +1 -1
  157. package/dist/i18n/german.d.ts +10 -8
  158. package/dist/i18n/german.js +50 -5
  159. package/dist/i18n/german.js.map +1 -1
  160. package/dist/i18n/kit-labels.d.ts +10 -8
  161. package/dist/i18n/kit-labels.js +8 -2
  162. package/dist/i18n/kit-labels.js.map +1 -1
  163. package/dist/i18n/languages.d.ts +10 -8
  164. package/dist/i18n/locales/de-CH.d.ts +10 -8
  165. package/dist/i18n/locales/en.d.ts +10 -8
  166. package/dist/i18n/locales/en.js +9 -0
  167. package/dist/i18n/locales/en.js.map +1 -1
  168. package/dist/i18n/locales/es.d.ts +10 -8
  169. package/dist/i18n/locales/es.js +48 -5
  170. package/dist/i18n/locales/es.js.map +1 -1
  171. package/dist/i18n/locales/fr.d.ts +10 -8
  172. package/dist/i18n/locales/fr.js +48 -5
  173. package/dist/i18n/locales/fr.js.map +1 -1
  174. package/dist/i18n/locales/hu.d.ts +10 -8
  175. package/dist/i18n/locales/hu.js +49 -5
  176. package/dist/i18n/locales/hu.js.map +1 -1
  177. package/dist/i18n/locales/it.d.ts +10 -8
  178. package/dist/i18n/locales/it.js +49 -5
  179. package/dist/i18n/locales/it.js.map +1 -1
  180. package/dist/i18n/locales/zh.d.ts +10 -8
  181. package/dist/i18n/locales/zh.js +48 -5
  182. package/dist/i18n/locales/zh.js.map +1 -1
  183. package/dist/i18n/review.d.ts +10 -8
  184. package/dist/i18n/review.js +8 -1
  185. package/dist/i18n/review.js.map +1 -1
  186. package/dist/index.d.ts +17 -10
  187. package/dist/index.js +57 -2
  188. package/dist/index.js.map +1 -1
  189. package/dist/{kit-labels-D1J2_jyD.d.ts → kit-labels-R8mIAc9N.d.ts} +811 -8
  190. package/dist/lib/countries.d.ts +43 -0
  191. package/dist/lib/countries.js +39 -0
  192. package/dist/lib/countries.js.map +1 -0
  193. package/dist/lib/decimal-marks.d.ts +46 -0
  194. package/dist/lib/decimal-marks.js +40 -0
  195. package/dist/lib/decimal-marks.js.map +1 -0
  196. package/dist/lib/iban.d.ts +98 -0
  197. package/dist/lib/iban.js +154 -0
  198. package/dist/lib/iban.js.map +1 -0
  199. package/dist/lib/isin.d.ts +30 -0
  200. package/dist/lib/isin.js +36 -0
  201. package/dist/lib/isin.js.map +1 -0
  202. package/dist/lib/phone.d.ts +69 -0
  203. package/dist/lib/phone.js +121 -0
  204. package/dist/lib/phone.js.map +1 -0
  205. package/dist/rhf/fields.d.ts +148 -11
  206. package/dist/rhf/fields.js +332 -3
  207. package/dist/rhf/fields.js.map +1 -1
  208. package/dist/rhf/form.d.ts +10 -8
  209. package/dist/rhf.d.ts +13 -9
  210. package/dist/rhf.js.map +1 -1
  211. package/dist/shell/app-shell.d.ts +10 -8
  212. package/dist/shell/top-bar-brand.d.ts +10 -8
  213. package/dist/shell/topbar-action-menu.d.ts +13 -1
  214. package/dist/shell/topbar-action-menu.js +31 -28
  215. package/dist/shell/topbar-action-menu.js.map +1 -1
  216. package/dist/shell.d.ts +10 -8
  217. package/dist/tour/tour.js +20 -24
  218. package/dist/tour/tour.js.map +1 -1
  219. package/dist/wizard/stepper-nav.d.ts +10 -8
  220. package/dist/wizard.d.ts +10 -8
  221. package/package.json +1 -1
  222. package/src/components/account-chips.tsx +17 -3
  223. package/src/components/account-settings.tsx +92 -26
  224. package/src/components/amount-input.tsx +49 -56
  225. package/src/components/autocomplete.tsx +133 -110
  226. package/src/components/checkbox-group.tsx +229 -0
  227. package/src/components/checkbox.tsx +72 -8
  228. package/src/components/chip.tsx +80 -10
  229. package/src/components/combobox.tsx +292 -253
  230. package/src/components/country-select.tsx +443 -0
  231. package/src/components/currency-select.tsx +109 -33
  232. package/src/components/danger-confirm.tsx +345 -77
  233. package/src/components/data-table-labels.ts +16 -1
  234. package/src/components/data-table.tsx +175 -6
  235. package/src/components/date-picker.tsx +173 -62
  236. package/src/components/entity-combobox.tsx +107 -87
  237. package/src/components/field-anatomy.tsx +190 -0
  238. package/src/components/field-parts.tsx +184 -0
  239. package/src/components/file-button.tsx +36 -3
  240. package/src/components/file-dropzone.tsx +106 -38
  241. package/src/components/form-actions.tsx +115 -2
  242. package/src/components/hover-menu.tsx +7 -4
  243. package/src/components/iban-input.tsx +231 -0
  244. package/src/components/icon-picker.tsx +26 -4
  245. package/src/components/inline-edit-field.tsx +533 -0
  246. package/src/components/language-select.tsx +78 -0
  247. package/src/components/line-items.tsx +35 -2
  248. package/src/components/list.tsx +55 -6
  249. package/src/components/money-field.tsx +10 -0
  250. package/src/components/month-picker.tsx +399 -63
  251. package/src/components/multi-select.tsx +80 -58
  252. package/src/components/number-field.tsx +28 -46
  253. package/src/components/number-input.tsx +210 -43
  254. package/src/components/one-time-code-input.tsx +288 -0
  255. package/src/components/phone-input.tsx +299 -0
  256. package/src/components/sign-chip.tsx +103 -0
  257. package/src/components/swatch-picker.tsx +118 -30
  258. package/src/components/switch.tsx +58 -7
  259. package/src/components/tile-radio.tsx +71 -16
  260. package/src/components/time-input.tsx +8 -1
  261. package/src/components/toggle-group.tsx +206 -73
  262. package/src/components/ui.tsx +472 -105
  263. package/src/data-table.ts +2 -1
  264. package/src/feedback/feedback-attachment.tsx +313 -9
  265. package/src/feedback/feedback-dialog.tsx +2 -0
  266. package/src/feedback/feedback-thread.tsx +66 -5
  267. package/src/i18n/defaults.ts +16 -1
  268. package/src/i18n/german.ts +47 -0
  269. package/src/i18n/kit-labels.tsx +78 -2
  270. package/src/i18n/locales/en.ts +10 -0
  271. package/src/i18n/locales/es.ts +46 -0
  272. package/src/i18n/locales/fr.ts +45 -0
  273. package/src/i18n/locales/hu.ts +46 -0
  274. package/src/i18n/locales/it.ts +47 -0
  275. package/src/i18n/locales/zh.ts +44 -0
  276. package/src/i18n/review.ts +7 -0
  277. package/src/index.ts +53 -3
  278. package/src/lib/countries.ts +91 -0
  279. package/src/lib/decimal-marks.ts +99 -0
  280. package/src/lib/iban.ts +171 -0
  281. package/src/lib/isin.ts +62 -0
  282. package/src/lib/phone.ts +211 -0
  283. package/src/rhf/fields.tsx +606 -9
  284. package/src/rhf.ts +2 -1
  285. package/src/shell/topbar-action-menu.tsx +53 -29
  286. package/src/tour/tour.tsx +66 -28
@@ -1,6 +1,6 @@
1
1
  import * as react from 'react';
2
- import { AnchorHTMLAttributes, Ref, ReactElement, ButtonHTMLAttributes, ReactNode, SelectHTMLAttributes, ComponentPropsWithoutRef, InputHTMLAttributes, TextareaHTMLAttributes } from 'react';
3
- import { DataTableLabels } from './components/data-table-labels.js';
2
+ import { InputHTMLAttributes, ReactNode, AnchorHTMLAttributes, Ref, ReactElement, ButtonHTMLAttributes, SelectHTMLAttributes, ComponentPropsWithoutRef, TextareaHTMLAttributes } from 'react';
3
+ import { f as DataTableLabels } from './data-table-labels-B7OdnM0S.js';
4
4
  import { MiniCalendarLabels, WeekDay } from './components/mini-calendar.js';
5
5
  import { CalendarHeatmapLabels } from './components/calendar-heatmap.js';
6
6
  import { PopoverLabels } from './components/popover.js';
@@ -19,15 +19,13 @@ import { SparklineLabels } from './components/sparkline.js';
19
19
  import { StatTileLabels } from './components/stat-tile.js';
20
20
  import { SignaturePadLabels } from './components/signature-pad.js';
21
21
  import { PasswordStrengthLabels } from './components/password-strength.js';
22
- import { DangerConfirmLabels } from './components/danger-confirm.js';
23
22
  import { SwatchPickerLabels } from './components/swatch-picker.js';
24
23
  import { IconPickerLabels } from './components/icon-picker.js';
25
24
  import { DialogFrameProps, DialogFrameLabels } from './components/dialog-frame.js';
26
25
  import { MeasuredGridLabels } from './components/measured-grid.js';
27
- import { f as FeedbackAttachmentFieldLabels, q as FeedbackDialogTextLabels } from './feedback-attachment-WFiXJ8We.js';
26
+ import { f as FeedbackAttachmentFieldLabels, t as FeedbackDialogTextLabels } from './feedback-attachment-NhUm7Zga.js';
28
27
  import { FeedbackThreadLabels, FeedbackComposerLabels } from './feedback/feedback-thread.js';
29
28
  import { AccountSettingsLabels } from './components/account-settings-labels.js';
30
- import { ConfirmDialogLabels } from './components/confirm-dialog.js';
31
29
  import { FloatingPanelLabels } from './components/floating-panel.js';
32
30
  import { BulkActionBarLabels } from './components/bulk-action-bar.js';
33
31
  import { ListLabels } from './components/list.js';
@@ -48,6 +46,11 @@ import { RoleDefinition, AccountStateLabels } from './components/account-chips.j
48
46
  import { ReauthDialogLabels } from './components/reauth-dialog.js';
49
47
  import { ServerWakeLabels } from './components/server-wake.js';
50
48
  import { TranslationReviewLabels } from './components/translation-review-labels.js';
49
+ import { CountrySelectLabels } from './components/country-select.js';
50
+ import { InlineEditLabels } from './components/inline-edit-field.js';
51
+ import { IbanKind } from './lib/iban.js';
52
+ import { PhoneCountry, PhoneCountryCode } from './lib/phone.js';
53
+ import { SignChipLabels } from './components/sign-chip.js';
51
54
 
52
55
  type ButtonVariant = "primary" | "secondary" | "ghost" | "danger" | "brand" | "link";
53
56
  type ButtonSize = "sm" | "md";
@@ -669,6 +672,22 @@ declare const DEFAULT_PASSWORD_REVEAL_LABELS: PasswordRevealLabels;
669
672
  /** Caller's labels over the English defaults — the same shape as
670
673
  * `resolveDataTableLabels`, so a consumer translates every kit string one way. */
671
674
  declare function resolvePasswordRevealLabels(partial?: Partial<PasswordRevealLabels>): PasswordRevealLabels;
675
+ /**
676
+ * What the character counter of {@link Input} and {@link Textarea} (`showCount`) says.
677
+ * The visible "12/80" is digits and a slash and is not translated; these are the words
678
+ * a screen reader gets instead of it.
679
+ */
680
+ interface CharacterCountLabels {
681
+ /** The counter as part of the field's description, read when focus reaches the
682
+ * field: "12 of 80 characters". */
683
+ count: (used: number, max: number) => string;
684
+ /** Said once, politely, on entering the last stretch before the limit: "8
685
+ * characters left". */
686
+ remaining: (left: number) => string;
687
+ /** Said once, politely, on reaching the limit. */
688
+ limitReached: string;
689
+ }
690
+ declare const DEFAULT_CHARACTER_COUNT_LABELS: CharacterCountLabels;
672
691
  interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
673
692
  label?: ReactNode;
674
693
  /** Classes for the `<input>` itself, as distinct from `className`, which
@@ -699,6 +718,30 @@ interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
699
718
  * `invalid`, so the field paints as well as announces. `invalid` alone still
700
719
  * covers the case where the message lives elsewhere. See {@link useFieldError}. */
701
720
  error?: ReactNode;
721
+ /**
722
+ * Standing advice for the field (keksdose K4: Input had `error` but no `hint`, so the
723
+ * app built the caption by hand under each field, and half of those never reached a
724
+ * screen reader). The {@link Select} rule, so every field reads alike:
725
+ * - plain TEXT (a string or a number) is a caption UNDER the field, attached with
726
+ * `aria-describedby` — after the caller's own ids, before the error's;
727
+ * - anything else (a {@link FieldHint} "?") rides the label line beside the
728
+ * animated label, as NumberInput's does — or, on a field with no label, sits at the
729
+ * field's end edge outside the box, and `className` then styles that row.
730
+ */
731
+ hint?: ReactNode;
732
+ /**
733
+ * A live "12/80" under the field, with `maxLength` (keksdose K11 — the broadcast,
734
+ * support and handoff-note fields counted by hand). Read with the field as "12 of 80
735
+ * characters", and announced politely only on entering the last stretch and on
736
+ * reaching the limit — never on every keystroke. See {@link CharacterCount}. Ignored
737
+ * without a `maxLength`: a count with no limit is not what this answers. Counts
738
+ * UTF-16 code units, as `maxLength` itself does. Controlled, it counts `value`;
739
+ * uncontrolled, `defaultValue` and then every change event — a value written from
740
+ * outside without one (a form library's `reset`) shows from the next keystroke.
741
+ */
742
+ showCount?: boolean;
743
+ /** The counter's words. See {@link CharacterCountLabels}. */
744
+ countLabels?: Partial<CharacterCountLabels>;
702
745
  /** Names for the password reveal toggle, English by default — it is the one
703
746
  * string this component renders on its own behalf, and a German form was
704
747
  * reading it out in English. See {@link PasswordRevealLabels}. */
@@ -735,6 +778,29 @@ interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, "siz
735
778
  * the chevron is positioned against — so it could set a width and nothing
736
779
  * else; this is the way to the element. See {@link Input}'s `inputClassName`. */
737
780
  selectClassName?: string;
781
+ /**
782
+ * Why the choice cannot be changed — {@link Button}'s `disabledReason`, for a select
783
+ * that SAVES on change (a role picker in a members table, a status in a row). keksdose
784
+ * K3: those sat inside a hand-rolled `SaveGuard` that forced a native `disabled`, which
785
+ * took the field out of the tab order so the reason never reached a keyboard.
786
+ *
787
+ * With a reason the select is `aria-disabled` instead — still focusable, still showing
788
+ * its value — its list does not open (pointer and keys are swallowed, Tab and Escape
789
+ * excepted), `onChange` is never called, and the reason is in the kit {@link Tooltip}
790
+ * and on `aria-describedby`. It wears the settled look of a disabled field and drops
791
+ * its chevron, as a disabled Select does (dev#474). It wins over `disabled`.
792
+ *
793
+ * Use it CONTROLLED (`value` + `onChange`): React puts a controlled value back after
794
+ * the swallowed change. A touch platform's own picker may still open; nothing it picks
795
+ * is kept or reported.
796
+ */
797
+ disabledReason?: ReactNode;
798
+ /**
799
+ * This select COMMITS — choosing saves. Under a locked {@link WriteLockProvider} it is
800
+ * locked the `disabledReason` way with the lock's reason (which wins over its own).
801
+ * No provider, or an unlocked one: no effect. Button's `commit`, for keksdose K3.
802
+ */
803
+ commit?: boolean;
738
804
  }
739
805
  declare const Select: react.ForwardRefExoticComponent<SelectProps & react.RefAttributes<HTMLSelectElement>>;
740
806
  interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
@@ -743,6 +809,14 @@ interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
743
809
  invalid?: boolean;
744
810
  /** See {@link Input}'s `error`. */
745
811
  error?: ReactNode;
812
+ /** See {@link Input}'s `hint` (keksdose K4): text is a caption under the field, a
813
+ * {@link FieldHint} rides the label line (or, unlabelled, the end edge). */
814
+ hint?: ReactNode;
815
+ /** See {@link Input}'s `showCount` (keksdose K11): a live "12/80" under the field,
816
+ * with `maxLength`, announced only near and at the limit. */
817
+ showCount?: boolean;
818
+ /** The counter's words. See {@link CharacterCountLabels}. */
819
+ countLabels?: Partial<CharacterCountLabels>;
746
820
  }
747
821
  declare const Textarea: react.ForwardRefExoticComponent<TextareaProps & react.RefAttributes<HTMLTextAreaElement>>;
748
822
  interface CardProps extends ComponentPropsWithoutRef<"div"> {
@@ -1164,6 +1238,317 @@ interface TabsProps<T extends string> extends Omit<ComponentPropsWithoutRef<"div
1164
1238
  }
1165
1239
  declare function Tabs<T extends string>({ tabs, active, onChange, className, wrap, label, panelId, onRemove, removeOn, onAdd, addLabel, labels, busy, orientation, "aria-label": ariaLabel, ...rest }: TabsProps<T>): react.JSX.Element;
1166
1240
 
1241
+ /**
1242
+ * How typed text is compared with the text it has to repeat — shared by
1243
+ * `DangerConfirm`'s `phraseMatch` and `useConfirm`'s `typedMatch`.
1244
+ *
1245
+ * - `"trim"` ignores spaces around the typed text — a phone keyboard's autocomplete
1246
+ * likes to add one. Case counts: "delete" is not "DELETE".
1247
+ * - `"exact"` compares character for character, spaces included.
1248
+ * - `"caseless"` trims AND ignores case: for an e-mail address, whose domain is
1249
+ * case-insensitive and whose local part every real mail server treats so. Both apps'
1250
+ * "type the account's address" rules (keksdose's `TYPE_EMAIL`, Kurvenschmiede's
1251
+ * `TypedConfirm`) compared `trim().toLowerCase()`, and a capital the user's keyboard
1252
+ * put at the start must not make the right row look like the wrong one.
1253
+ */
1254
+ type TypedMatch = "trim" | "exact" | "caseless";
1255
+ /** Whether `typed` repeats `target` under `mode` — see {@link TypedMatch}. Exported for
1256
+ * a caller that draws its own field (a dialog that also picks a recipient) and wants
1257
+ * the same rule as the kit's. */
1258
+ declare function typedMatches(typed: string, target: string, mode?: TypedMatch): boolean;
1259
+ /**
1260
+ * Busy-while-a-promise-runs, for an action that MAY return one: `run(result, onResolved)`
1261
+ * marks the caller pending until `result` settles, calls `onResolved` if it fulfilled,
1262
+ * and only clears the flag if it rejected — the caller shows why and the user retries.
1263
+ * Nothing runs after unmount. Shared by `DangerConfirm` and `ReauthDialog`, whose
1264
+ * contract is the same: resolve = done, reject = stay put.
1265
+ *
1266
+ * @internal
1267
+ */
1268
+ declare function usePromisePending(): {
1269
+ pending: boolean;
1270
+ run: (result: unknown, onResolved: () => void) => boolean;
1271
+ };
1272
+ /** The one word {@link CurrentPasswordInput} says on its own behalf — the
1273
+ * `dangerConfirm.password` label, shared with DangerConfirm's password field, so an
1274
+ * app translates "Password" once. */
1275
+ type CurrentPasswordInputLabels = Pick<DangerConfirmLabels, "password">;
1276
+ /**
1277
+ * Everything {@link Input} takes except what makes this field what it is: `type` is
1278
+ * always `"password"` and `autoComplete` always `"current-password"`.
1279
+ */
1280
+ interface CurrentPasswordInputProps extends Omit<InputProps, "type" | "autoComplete" | "label"> {
1281
+ /**
1282
+ * The visible label. Default `dangerConfirm.password` ("Password") — unless the field
1283
+ * is named another way (`aria-label` / `aria-labelledby`), which then stays the only
1284
+ * name. Pass the app's own word on a sign-in form ("Password", "Your password"), or
1285
+ * `null` for a field whose `<label htmlFor>` is drawn elsewhere (a form library's
1286
+ * `FormLabel`), which would otherwise be labelled twice.
1287
+ */
1288
+ label?: ReactNode;
1289
+ /** The value as a string — the `(next) => …` shape, so a caller holding it in
1290
+ * `useState` need not unwrap `event.target.value`. Fires alongside `onChange`,
1291
+ * never instead of it, so a form library's `onChange` keeps working. */
1292
+ onValueChange?: (value: string) => void;
1293
+ /**
1294
+ * A check is running (the sign-in request, the re-auth). The field turns `readOnly`,
1295
+ * NOT `disabled`: the field being submitted is usually the focused one (Enter was
1296
+ * pressed in it), and disabling a focused field drops focus to `<body>` — the user
1297
+ * lands nowhere when the "wrong password" answer comes back.
1298
+ */
1299
+ busy?: boolean;
1300
+ /** Per-instance override of `dangerConfirm.password`. */
1301
+ labels?: Partial<CurrentPasswordInputLabels>;
1302
+ }
1303
+ /**
1304
+ * The "prove it is you" field: a password input that a password manager fills with
1305
+ * the CURRENT password and never offers to generate a new one for.
1306
+ *
1307
+ * It is the field of a sign-in form, a re-auth step ({@link ReauthDialog} draws the same
1308
+ * field), and {@link DangerConfirm}'s `requirePassword`. Public since 0.22 for kastlan's
1309
+ * sign-in and re-auth (kastlan 1), which wrote `type="password"` +
1310
+ * `autoComplete="current-password"` by hand — and a sign-in field that forgets the
1311
+ * second half gets the browser's "suggest a strong password" offer on the one form where
1312
+ * a NEW password is never what is wanted. The reverse matters as much: a field that sets
1313
+ * a NEW password (a passphrase, a sign-up) must NOT be this one — keksdose #170 keeps
1314
+ * its passphrase doors on `new-password` / `off` for that reason.
1315
+ *
1316
+ * Everything else is {@link Input}: the reveal toggle, `error` (under the field,
1317
+ * `aria-describedby`, `aria-invalid`), `invalid`, `name`, `id`, `autoFocus`, `required`.
1318
+ * The ref is the `<input>`, so it works with `@eifi1/ui-kit/rhf`'s `FormControl` /
1319
+ * a `register()` spread, and controlled or uncontrolled.
1320
+ *
1321
+ * ```tsx
1322
+ * <CurrentPasswordInput name="password" label={t("password")} error={errors.password?.message}
1323
+ * busy={isSubmitting} {...register("password")} />
1324
+ * ```
1325
+ */
1326
+ declare const CurrentPasswordInput: react.ForwardRefExoticComponent<CurrentPasswordInputProps & react.RefAttributes<HTMLInputElement>>;
1327
+ /** The words {@link TypedConfirmField} says on its own behalf — the `dangerConfirm`
1328
+ * namespace's `phrase` and `phrasePlaceholder`, shared with DangerConfirm's own field,
1329
+ * so the sentence "Type “X” to confirm" is translated once. */
1330
+ type TypedConfirmFieldLabels = Pick<DangerConfirmLabels, "phrase" | "phrasePlaceholder">;
1331
+ interface TypedConfirmFieldProps extends Omit<InputProps, "type" | "value" | "defaultValue" | "label" | "placeholder"> {
1332
+ /** The text to repeat — a word ("DELETE"), a name, an e-mail address. */
1333
+ target: string;
1334
+ /** How the typed text is compared with `target` — {@link TypedMatch}. Default
1335
+ * `"trim"`, as {@link typedMatches}; `"caseless"` for an address or an
1336
+ * acknowledgement word a user may type in any case. */
1337
+ match?: TypedMatch;
1338
+ /** Controlled value. Left out, the field keeps its own (start: `defaultValue`). */
1339
+ value?: string;
1340
+ defaultValue?: string;
1341
+ /** The typed text as a string, on every keystroke. Fires alongside `onChange`. */
1342
+ onValueChange?: (value: string) => void;
1343
+ /**
1344
+ * The typed text started or stopped matching `target` — fired from the keystroke
1345
+ * that flipped it, so an uncontrolled field can still drive the button it guards. A
1346
+ * controlled caller that resets the value itself (on a close) knows the answer
1347
+ * already, and gets no call for that; it can also compute it with {@link typedMatches}.
1348
+ */
1349
+ onMatchedChange?: (matched: boolean) => void;
1350
+ /** The label. Default `dangerConfirm.phrase(target)` — "Type “DELETE” to confirm". */
1351
+ label?: ReactNode;
1352
+ /** Text inside the empty field. Default `dangerConfirm.phrasePlaceholder` (unset).
1353
+ * Given, the label moves ABOVE the field: a floating label occupies the empty
1354
+ * field, which is exactly where a placeholder shows. `""` means none. */
1355
+ placeholder?: string;
1356
+ /** The guarded action is running: the field turns `readOnly` (not `disabled`, which
1357
+ * would drop the focus Enter left in it). */
1358
+ busy?: boolean;
1359
+ labels?: Partial<TypedConfirmFieldLabels>;
1360
+ }
1361
+ /**
1362
+ * The "type this to confirm" field — {@link DangerConfirm}'s `phrase` field and
1363
+ * `useConfirm({ requireTyped })`'s, on its own (keksdose K7).
1364
+ *
1365
+ * keksdose writes it by hand four times: the privacy enrol, upgrade and sweep dialogs
1366
+ * (type the acknowledgement word, then the key doors appear) and the admin's "type the
1367
+ * user's address" (`UserActionConfirm`), each an `Input` plus its own
1368
+ * `trim().toUpperCase()` / `toLowerCase()` comparison. None of those places is an
1369
+ * arm → confirm tile or a modal confirm — what is guarded is a whole form, or the
1370
+ * next step of a dialog — so the field had to stand alone. What it brings over a plain
1371
+ * `Input`: one match rule ({@link typedMatches}, which lower-cases locale-independently
1372
+ * on purpose), the label worded from the target in the app's language, and the
1373
+ * attributes a phone keyboard needs to leave the text alone (no autocomplete, no
1374
+ * auto-capital, no autocorrect, no spellcheck — a keyboard that "corrects" an e-mail
1375
+ * address makes the guard unpassable).
1376
+ *
1377
+ * The field reports whether it matches (`onMatchedChange`, and `data-matched` on the
1378
+ * `<input>` for styling); what the match unlocks is the caller's. The ref is the
1379
+ * `<input>`.
1380
+ *
1381
+ * ```tsx
1382
+ * const [ok, setOk] = useState(false);
1383
+ * <TypedConfirmField target={t("privacy.enroll_ack_word")} match="caseless" onMatchedChange={setOk} />
1384
+ * {ok && <KeyDoorsForm … />}
1385
+ * ```
1386
+ */
1387
+ declare const TypedConfirmField: react.ForwardRefExoticComponent<TypedConfirmFieldProps & react.RefAttributes<HTMLInputElement>>;
1388
+ /**
1389
+ * Every string the tile renders — the `dangerConfirm` namespace of
1390
+ * `<UiKitProvider labels>`, overridable per instance through `labels`. The arm and
1391
+ * confirm buttons are usually worded per action ("Delete budget"), which is what the
1392
+ * `armLabel` / `confirmLabel` props are for; these are the fallbacks.
1393
+ */
1394
+ interface DangerConfirmLabels {
1395
+ /** The button that arms the tile. */
1396
+ arm: string;
1397
+ /** The button that runs the action once the guards are satisfied. */
1398
+ confirm: string;
1399
+ cancel: string;
1400
+ /** The warning above the fields, when no `prompt` is given. */
1401
+ prompt: string;
1402
+ /** Label of the password field (`requirePassword`). */
1403
+ password: string;
1404
+ /** Label of the type-to-confirm field. A FUNCTION of the phrase by default, like
1405
+ * every message that carries a value: where the phrase sits in the sentence moves
1406
+ * with the language. A plain STRING is taken as the finished label — for an app
1407
+ * whose catalogue already words it ("Type DELETE to confirm") and would otherwise
1408
+ * wrap it as `() => label`. */
1409
+ phrase: string | ((phrase: string) => string);
1410
+ /** Placeholder of the type-to-confirm field — a string, or a function of the
1411
+ * phrase (`(p) => p` echoes it). Optional and unset by default: the field keeps its
1412
+ * floating label, as before. Given, the label moves ABOVE the field (a floating
1413
+ * label occupies the empty field, which is exactly where a placeholder shows) and
1414
+ * the placeholder fills the field. An empty string means none. */
1415
+ phrasePlaceholder?: string | ((phrase: string) => string);
1416
+ /** 0.22: the checkbox `requireAcknowledge={true}` shows — "I have read what this does
1417
+ * and want to continue" (keksdose's `admin.users.confirm_ack`). */
1418
+ acknowledge: string;
1419
+ }
1420
+ /** `satisfies` rather than a type annotation, so `DEFAULT_DANGER_CONFIRM_LABELS.phrase`
1421
+ * stays callable for a caller that composes its own label from it. */
1422
+ declare const DEFAULT_DANGER_CONFIRM_LABELS: {
1423
+ arm: string;
1424
+ confirm: string;
1425
+ cancel: string;
1426
+ prompt: string;
1427
+ password: string;
1428
+ phrase: (phrase: string) => string;
1429
+ acknowledge: string;
1430
+ };
1431
+ /**
1432
+ * One line of {@link DangerConfirmProps.consequences}: what the action will do. A plain
1433
+ * string, or this, for a key of its own and the `severe` mark.
1434
+ */
1435
+ interface DangerConsequence {
1436
+ /** React key — keksdose keys its lines by what they say ("api_tokens", "only_door").
1437
+ * Default: the text when it is a string, else the position. */
1438
+ key?: string;
1439
+ text: ReactNode;
1440
+ /** "There is no way back" — painted in the danger colour rather than the list's
1441
+ * muted one. keksdose reserves it for the one line that loses something for good. */
1442
+ severe?: boolean;
1443
+ }
1444
+ interface DangerConfirmProps extends Omit<ComponentPropsWithoutRef<"div">, "onChange"> {
1445
+ /**
1446
+ * Runs the action, with the entered password when `requirePassword` is on.
1447
+ *
1448
+ * Return a promise and the tile manages itself: busy until it settles, disarmed
1449
+ * (fields wiped) when it resolves, still armed when it rejects — so the user can
1450
+ * correct a wrong password and retry. The rejection is not swallowed for you to
1451
+ * miss: handle it (and show why) in the caller, as with any mutation.
1452
+ */
1453
+ onConfirm: (password?: string) => void | Promise<unknown>;
1454
+ /** Show a password field; confirm stays disabled until it is filled. */
1455
+ requirePassword?: boolean;
1456
+ /**
1457
+ * Show an "I understand" checkbox; confirm stays disabled until it is ticked
1458
+ * (keksdose K7). `true` words it with `labels.acknowledge`; any other node IS the
1459
+ * checkbox's label ("I understand Anna will be signed out everywhere").
1460
+ *
1461
+ * The guard for an action whose risk is MIS-TARGETING less than carelessness:
1462
+ * keksdose's admin password reset asks for a typed address when the account has an
1463
+ * encrypted door to lose, and a tick otherwise (`UserActionConfirm`), and its plan
1464
+ * editor asks for the tick alone (`UserPlanEditor`). Both were hand-built beside this
1465
+ * tile because it had no tick. It combines with `phrase` and `requirePassword`: every
1466
+ * guard given must be satisfied.
1467
+ */
1468
+ requireAcknowledge?: ReactNode;
1469
+ /**
1470
+ * What the action will do, said before it happens — a list under the prompt, shown
1471
+ * once armed (keksdose K7, `UserActionConfirm`'s consequences). Strings, or
1472
+ * {@link DangerConsequence}s for a `severe` line in the danger colour.
1473
+ *
1474
+ * Derive them from the row's own facts rather than reciting the worst case: an admin
1475
+ * who reads the same paragraph on every row stops reading it (keksdose dev#488).
1476
+ */
1477
+ consequences?: readonly (string | DangerConsequence)[];
1478
+ /** Show a "type <phrase> to confirm" field; confirm stays disabled until the field
1479
+ * matches (case-sensitive; surrounding spaces ignored unless `phraseMatch="exact"`). */
1480
+ phrase?: string;
1481
+ /**
1482
+ * How the typed text is compared with `phrase`. `"trim"` (default) ignores spaces
1483
+ * around it — a phone keyboard's autocomplete likes to add one. `"exact"` compares
1484
+ * character for character, spaces included, for an app whose contract is "type
1485
+ * exactly this". `"caseless"` (0.18) also ignores case — for a phrase that is an
1486
+ * e-mail address (keksdose's "type the user's address" admin rule). See
1487
+ * {@link TypedMatch}.
1488
+ */
1489
+ phraseMatch?: TypedMatch;
1490
+ /** The warning above the fields. Defaults to `labels.prompt`. */
1491
+ prompt?: ReactNode;
1492
+ /** `"danger"` (default) for what cannot be undone; `"warning"` for what can, at a
1493
+ * cost (loading demo data over your own). Colours the prompt and the confirm. */
1494
+ tone?: "danger" | "warning";
1495
+ /** Visible text of the arm button; defaults to `labels.arm`. */
1496
+ armLabel?: ReactNode;
1497
+ /** Visible text of the confirm button; defaults to `labels.confirm`. */
1498
+ confirmLabel?: ReactNode;
1499
+ /** The action is running: confirm shows a spinner and nothing can be pressed. For a
1500
+ * caller that tracks the mutation itself (a `useMutation`'s `isPending`); a
1501
+ * promise returned from `onConfirm` does the same on its own. */
1502
+ busy?: boolean;
1503
+ /** The arm button is disabled. */
1504
+ disabled?: boolean;
1505
+ /**
1506
+ * Why the action is not available — a read-only demo, a write lock, a missing
1507
+ * permission. Disables the arm button like `disabled`, and SAYS so: the sentence is
1508
+ * shown under the button and attached to it with `aria-describedby`, and the button
1509
+ * stays focusable (`aria-disabled`) so a keyboard user can land on it and hear why.
1510
+ *
1511
+ * On the ARM button rather than the confirm: arming asks for a password, and asking
1512
+ * for a password for a write that can never land is the worse of the two. A tile that
1513
+ * is ALREADY armed when the reason arrives (controlled `armed`, or a lock that landed
1514
+ * while it was open) keeps its fields but its confirm takes the reason the same way
1515
+ * — focusable, `aria-disabled`, the reason in its tooltip — and Enter does nothing.
1516
+ */
1517
+ lockedReason?: ReactNode;
1518
+ /**
1519
+ * This tile COMMITS — the 0.18 write-lock opt-in that {@link Button} has (keksdose
1520
+ * K3). Under a locked {@link WriteLockProvider} the lock's reason becomes
1521
+ * `lockedReason` (and wins over one of the tile's own), so a page that mounts the
1522
+ * provider no longer threads `lockedReason={lock.locked ? lock.reason : undefined}`
1523
+ * into every tile. No provider, or an unlocked one: no effect.
1524
+ */
1525
+ commit?: boolean;
1526
+ /** Controlled armed state. The parent can then collapse the tile from a mutation's
1527
+ * own `onSuccess` without returning a promise. */
1528
+ armed?: boolean;
1529
+ /** Called with the next armed state — on arm, cancel, and a resolved `onConfirm`. */
1530
+ onArmedChange?: (armed: boolean) => void;
1531
+ /** User-facing strings; see {@link DangerConfirmLabels}. */
1532
+ labels?: Partial<DangerConfirmLabels>;
1533
+ }
1534
+ /**
1535
+ * An "arm → confirm" tile for destructive actions: one button, which expands into a
1536
+ * warning, an optional list of consequences, an optional "I understand" tick, an
1537
+ * optional type-to-confirm field, an optional password field and a confirm that stays
1538
+ * disabled until every guard is satisfied.
1539
+ *
1540
+ * Keksdose hand-rolled it three times (load demo data, wipe everything, reset a
1541
+ * budget) and then as `shared/components/danger-confirm.tsx`; the only app-specific
1542
+ * part was its write-lock hook, which is `lockedReason` here.
1543
+ *
1544
+ * The fields are a `<form>`, so Enter confirms once the guards allow it. They are
1545
+ * wiped every time the tile disarms — a password must not sit in a collapsed tile.
1546
+ * Arming moves focus to the first field (or Cancel, when there is none — never to the
1547
+ * destructive button itself), and disarming moves it back to the arm button, so the
1548
+ * keyboard user is never left on an element that just vanished.
1549
+ */
1550
+ declare function DangerConfirm({ onConfirm, requirePassword, requireAcknowledge, consequences, phrase, phraseMatch, prompt, tone, armLabel, confirmLabel, busy: busyProp, disabled, lockedReason: ownLockedReason, commit, armed: armedProp, onArmedChange, labels: labelsProp, className, ...rest }: DangerConfirmProps): react.JSX.Element;
1551
+
1167
1552
  /**
1168
1553
  * A button that opens the file picker — the shape all three apps kept writing by hand
1169
1554
  * as a `<Button>` plus a hidden `<input type="file">` plus a ref between them (seven
@@ -1361,6 +1746,22 @@ interface FileButtonProps extends Omit<UseFilePickerOptions, "disabled">, Omit<B
1361
1746
  * file leaves it as it was. Off by default, so the button's layout is unchanged.
1362
1747
  */
1363
1748
  showFileName?: boolean;
1749
+ /**
1750
+ * Why picking is not available — {@link Button}'s `disabledReason`, with the picker's
1751
+ * half added (keksdose K3): the button is `aria-disabled` but stays focusable, the
1752
+ * reason is in the kit Tooltip and its description, a click opens no picker, and a
1753
+ * `droppable` button takes no drop (the drag is still cancelled, so the browser does
1754
+ * not navigate to the file). Wins over `disabled`, as on Button.
1755
+ */
1756
+ disabledReason?: ReactNode;
1757
+ /**
1758
+ * This pick COMMITS — an upload that starts on pick, an attachment saved at once.
1759
+ * Under a locked {@link WriteLockProvider} it takes the `disabledReason` path with
1760
+ * the lock's reason (which wins over one of its own). No provider, or an unlocked
1761
+ * one: no effect. A picker that only fills a form the user saves later is not a
1762
+ * commit, and is left live — the form's Save carries the lock.
1763
+ */
1764
+ commit?: boolean;
1364
1765
  }
1365
1766
  /**
1366
1767
  * {@link useFilePicker} behind a {@link Button}: `variant` and every other button prop
@@ -1376,6 +1777,162 @@ interface FileButtonProps extends Omit<UseFilePickerOptions, "disabled">, Omit<B
1376
1777
  */
1377
1778
  declare const FileButton: react.ForwardRefExoticComponent<FileButtonProps & react.RefAttributes<HTMLButtonElement>>;
1378
1779
 
1780
+ /**
1781
+ * The `confirmDialog` namespace of `<UiKitProvider labels>`: the two buttons' fallback
1782
+ * text. A call that knows what it is confirming says so per call (`confirmLabel:
1783
+ * "Delete budget"`), which is what the dialog is for — these are for the rest.
1784
+ */
1785
+ interface ConfirmDialogLabels {
1786
+ confirm: string;
1787
+ cancel: string;
1788
+ /**
1789
+ * 0.18: label of the field a `requireTyped` confirm shows. A FUNCTION of the text to
1790
+ * type by default — where the address sits in the sentence moves with the language —
1791
+ * or a finished STRING. A call's own `typedLabel` wins over it.
1792
+ *
1793
+ * Required, like every key of a namespace: the shipped catalogues
1794
+ * (`@eifi1/ui-kit/i18n/<code>`) translate it, and an app's own full catalogue should
1795
+ * hear about the new string from the compiler rather than show it in English.
1796
+ */
1797
+ typed: string | ((text: string) => string);
1798
+ }
1799
+ /** `satisfies` rather than a type annotation, so `.typed` stays known-callable for a
1800
+ * caller composing its own label from it. */
1801
+ declare const DEFAULT_CONFIRM_DIALOG_LABELS: {
1802
+ confirm: string;
1803
+ cancel: string;
1804
+ typed: (text: string) => string;
1805
+ };
1806
+ type ConfirmTone = "danger" | "warning" | "neutral";
1807
+ interface ConfirmOptions {
1808
+ /** The question, and the dialog's accessible name. */
1809
+ title: ReactNode;
1810
+ /** The smaller line under it — the consequence. Wired to `aria-describedby`. */
1811
+ body?: ReactNode;
1812
+ /** Default: `confirmDialog.confirm` from the provider, else "Confirm". */
1813
+ confirmLabel?: ReactNode;
1814
+ /** Default: `confirmDialog.cancel` from the provider, else "Cancel". */
1815
+ cancelLabel?: ReactNode;
1816
+ /**
1817
+ * `"danger"` for what cannot be undone (a delete): the confirm is the destructive
1818
+ * button and focus starts on CANCEL. `"warning"` and `"neutral"` (default) start on
1819
+ * the confirm. See {@link ConfirmProvider} for why the two differ.
1820
+ */
1821
+ tone?: ConfirmTone;
1822
+ /**
1823
+ * 0.18: make the user TYPE this before confirm is enabled — the target account's
1824
+ * e-mail address, a budget's name. For the action whose risk is not "did you mean
1825
+ * it" but "is this the right row": an admin who deactivates somebody has already
1826
+ * proven who THEY are (they passed the admin gate); typing the address is what
1827
+ * proves they read which account they are on. Both keksdose (`TYPE_EMAIL`) and
1828
+ * Kurvenschmiede (`TypedConfirm`) built this beside `useConfirm` because it had no
1829
+ * field.
1830
+ *
1831
+ * The dialog then shows one text field under the body and focuses it (whatever the
1832
+ * tone: a reflexive Enter cannot confirm a field that does not match yet). Confirm
1833
+ * stays disabled until the text matches; Enter in the field confirms once it does.
1834
+ * The answer is still `true` / `false` — the text matched what you passed, so send
1835
+ * that to the server if it asks for the address too, not the user's spelling.
1836
+ */
1837
+ requireTyped?: string;
1838
+ /** Label of that field. Default: `confirmDialog.typed` — `Type “a@b.c” to confirm`. */
1839
+ typedLabel?: string;
1840
+ /**
1841
+ * How the typed text is compared. Default `"caseless"`: surrounding spaces and case
1842
+ * ignored, the rule both apps applied to an address. `"trim"` keeps case, `"exact"`
1843
+ * keeps spaces too — for a phrase like "DELETE" whose contract is "exactly this".
1844
+ */
1845
+ typedMatch?: TypedMatch;
1846
+ }
1847
+ type ConfirmFn = (options: ConfirmOptions) => Promise<boolean>;
1848
+ /**
1849
+ * `const confirm = useConfirm()` → `if (!(await confirm({ title }))) return`.
1850
+ *
1851
+ * The returned function is STABLE for the life of the provider, so it can sit in an
1852
+ * effect's or a `useCallback`'s dependency list without re-running it.
1853
+ *
1854
+ * Throws outside a {@link ConfirmProvider}, rather than falling back to
1855
+ * `window.confirm`: a silent fallback is exactly the native dialog the three apps are
1856
+ * migrating away from, and it would look like the migration had worked.
1857
+ */
1858
+ declare function useConfirm(): ConfirmFn;
1859
+ interface ConfirmProviderProps {
1860
+ children: ReactNode;
1861
+ }
1862
+ /**
1863
+ * The host for {@link useConfirm}: ONE confirm dialog for the whole app, rendered on
1864
+ * {@link DialogFrame}, answering a promise.
1865
+ *
1866
+ * Asked for by all three apps at once — keksdose has 20+ `window.confirm` calls,
1867
+ * kastlan 7, lenkbank 1. The native dialog cannot be translated past its OK/Cancel
1868
+ * (those are the BROWSER's language, not the app's), cannot say which button is the
1869
+ * destructive one, and on an installed PWA it prints the origin over the question.
1870
+ * A promise is what lets each of those call sites change by one `await`:
1871
+ *
1872
+ * if (!window.confirm(t("budget.delete_confirm"))) return;
1873
+ * // becomes
1874
+ * if (!(await confirm({ title: t("budget.delete_confirm"), tone: "danger" }))) return;
1875
+ *
1876
+ * ## Its own provider, not part of `UiKitProvider`
1877
+ *
1878
+ * `UiKitProvider` renders nothing: it is strings and a locale, it is optional, and it
1879
+ * NESTS (an inner one re-labels one table). A dialog host inside it would mount a host
1880
+ * per nested provider, and a component under the inner one would open a second dialog
1881
+ * layer of its own. So the host is its own component, mounted once — inside the
1882
+ * `UiKitProvider` whose strings it should speak, which is also the order the labels
1883
+ * need.
1884
+ *
1885
+ * ## Focus
1886
+ *
1887
+ * On CANCEL for `tone: "danger"`, on CONFIRM otherwise. A confirm is often answered by
1888
+ * a reflexive Enter — the key that pressed the Delete button in the first place may
1889
+ * still be down — and for an action that cannot be undone that reflex must land on the
1890
+ * harmless answer; `DangerConfirm` makes the same call ("never to the destructive
1891
+ * button itself"). For everything else the confirm is the expected answer and Enter
1892
+ * accepting it is what `window.confirm` taught users, which the migrating call sites'
1893
+ * users already rely on. Focus returns to whatever opened it, through `Modal`'s trap.
1894
+ *
1895
+ * ## Escape, the backdrop, Back and Cancel all answer `false`
1896
+ *
1897
+ * Only the confirm button answers `true`. Anything that merely dismisses is a no.
1898
+ *
1899
+ * ## A second `confirm` while one is open QUEUES
1900
+ *
1901
+ * It is shown when the first is answered, and each call gets its own answer. The
1902
+ * alternative — answering the open one `false` to make room — would answer a question
1903
+ * the user never answered: a background prompt ("your session is about to expire")
1904
+ * would cancel the delete they were reading. `false` has to mean "the user said no".
1905
+ * A double-click cannot queue two of the same: the first click opens the modal
1906
+ * synchronously (a discrete event's update is flushed before the next one), so the
1907
+ * second lands on the open dialog rather than on the button — at worst on its
1908
+ * backdrop, which answers `false`, the safe way to be wrong.
1909
+ *
1910
+ * Unmounting the provider answers everything still pending `false`, so no caller's
1911
+ * `await` hangs forever.
1912
+ *
1913
+ * ## keksdose's synchronous call (transaction-swipe-plan.ts)
1914
+ *
1915
+ * `buildTxSwipeActions` is synchronous, but its `window.confirm` sits inside the
1916
+ * action THUNK (`confirmed(key, run)` returns `() => { if (confirm(…)) run() }`), and a
1917
+ * thunk may be async without its caller knowing. So the plan builder stays
1918
+ * synchronous; it takes the confirm as an argument and the thunk awaits it:
1919
+ *
1920
+ * // mobile-transaction-list.tsx
1921
+ * const confirm = useConfirm();
1922
+ * buildTxSwipeActions({ tx, …, confirm });
1923
+ *
1924
+ * // transaction-swipe-plan.ts
1925
+ * const confirmed = (messageKey: string, run: () => void) => () => {
1926
+ * void confirm({ title: t(messageKey), tone: "danger" }).then((ok) => ok && run());
1927
+ * };
1928
+ *
1929
+ * Its test then passes `confirm: vi.fn(async () => false)` instead of stubbing the
1930
+ * global, and asserts after an `await` (the mutation now runs a microtask later). The
1931
+ * row snaps back when the swipe commits, before the answer — which is right: the
1932
+ * dialog, not the half-swiped row, is where the question now is.
1933
+ */
1934
+ declare function ConfirmProvider({ children }: ConfirmProviderProps): react.JSX.Element;
1935
+
1379
1936
  /** The words a copy button says. The visible three are short on purpose (they
1380
1937
  * replace the button's own text in the `label` variant); the two announcements
1381
1938
  * are the sentences a screen reader hears, since the button's focus never moves. */
@@ -1445,8 +2002,17 @@ declare function CopyButton({ text, variant, label, buttonVariant, size, tone, s
1445
2002
  interface FormActionsLabels {
1446
2003
  save: string;
1447
2004
  cancel: string;
2005
+ /**
2006
+ * The {@link FormActionsProps.submitShortcut} hint beside Save's text, when
2007
+ * {@link FormActionsProps.submitShortcutHint} shows it. `apple` is true on a Mac,
2008
+ * iPhone or iPad, where the key is Cmd: "⌘ Enter" there, "Ctrl+Enter" elsewhere
2009
+ * (a German keyboard says "Strg").
2010
+ */
2011
+ submitShortcut: (apple: boolean) => string;
1448
2012
  }
1449
2013
  declare const DEFAULT_FORM_ACTIONS_LABELS: FormActionsLabels;
2014
+ /** See {@link FormActionsProps.submitShortcut}. */
2015
+ type FormActionsSubmitShortcut = "mod-enter";
1450
2016
  type FormActionsAlign = "start" | "center" | "end" | "between";
1451
2017
  /**
1452
2018
  * Where the row sits:
@@ -1606,6 +2172,39 @@ interface FormActionsProps extends Omit<ComponentPropsWithoutRef<"div">, "childr
1606
2172
  /** The `id` of the `<form>` the save button submits, when the row is rendered
1607
2173
  * outside it (a dialog's footer slot). */
1608
2174
  form?: string;
2175
+ /**
2176
+ * A keyboard shortcut that presses Save from anywhere in the form. `"mod-enter"`:
2177
+ * Ctrl+Enter, or Cmd+Enter on a Mac — both are accepted everywhere, as keksdose's
2178
+ * editors always have. keksdose K14 (inputs audit 2026-10-02, S5): its transaction
2179
+ * editor, category editor and create card each wrote this as an `onKeyDown` on a
2180
+ * container, re-stating `!pending && !saveDisabled && !lock.locked` every time, and
2181
+ * the one that forgot the lock let the demo's refused save through from the keyboard.
2182
+ *
2183
+ * What it does is what pressing Save does — the same button is clicked, so a form
2184
+ * submits with its validation and its submitter, and `onSubmit` is called in the
2185
+ * `type="button"` mode — and it does NOTHING while Save could not be pressed:
2186
+ * `pending`, `submitDisabled`, or a `commit` under a locked {@link WriteLockProvider}.
2187
+ *
2188
+ * Scoped to the form Save submits (the `form` prop's, else the enclosing `<form>`):
2189
+ * the chord counts only when the focus is inside it, so two editors on one page each
2190
+ * answer for their own fields. Without a form (an `onSubmit` row in a plain container)
2191
+ * the scope is the nearest `role="dialog"`, else the row's parent element — the
2192
+ * container the row ends.
2193
+ *
2194
+ * Listened for on the document, so it runs AFTER every React handler: a field that
2195
+ * takes Ctrl+Enter for itself calls `preventDefault()` or `stopPropagation()` and
2196
+ * the row stays out of it (keksdose live #202 — the create card's Ctrl+Shift+Enter
2197
+ * must create ONE row; the chord here is exactly Ctrl/Cmd+Enter, never with Shift or
2198
+ * Alt). Not during IME composition, and not on a held key's repeats.
2199
+ *
2200
+ * The save button announces it with `aria-keyshortcuts`; {@link submitShortcutHint}
2201
+ * also shows it.
2202
+ */
2203
+ submitShortcut?: FormActionsSubmitShortcut;
2204
+ /** Show the {@link submitShortcut} beside Save's text ("Ctrl+Enter", "⌘ Enter"; the
2205
+ * `form.submitShortcut` label), dimmed. Hidden on a touch-only device, which has no
2206
+ * keyboard to press it on. Default `false`. */
2207
+ submitShortcutHint?: boolean;
1609
2208
  /** Extra actions, placed before Cancel. */
1610
2209
  children?: ReactNode;
1611
2210
  }
@@ -1624,7 +2223,7 @@ interface FormActionsProps extends Omit<ComponentPropsWithoutRef<"div">, "childr
1624
2223
  * The spinner is the kit's {@link Spinner}, drawn inside the button with `label={null}`
1625
2224
  * so the button's name stays its text; `aria-busy` is what says it is working.
1626
2225
  */
1627
- declare function FormActions({ onCancel, onSubmit, submitLabel, cancelLabel, pendingLabel, submitIcon: SubmitIcon, submitProps, pending, submitDisabled, submitDisabledReason, commit, submitVariant, destructive, start: startSlot, align, placement: placementProp, stickyWithin, bleed, form, children, className, style, ...rest }: FormActionsProps): react.JSX.Element;
2226
+ declare function FormActions({ onCancel, onSubmit, submitLabel, cancelLabel, pendingLabel, submitIcon: SubmitIcon, submitProps, pending, submitDisabled, submitDisabledReason, commit, submitVariant, destructive, start: startSlot, align, placement: placementProp, stickyWithin, bleed, form, submitShortcut, submitShortcutHint, children, className, style, ...rest }: FormActionsProps): react.JSX.Element;
1628
2227
 
1629
2228
  /**
1630
2229
  * Who holds one record, and the owner's every way of changing that — Kurvenschmiede's
@@ -1806,6 +2405,147 @@ interface ShareDialogProps extends SharePanelProps, Pick<DialogFrameProps, "open
1806
2405
  */
1807
2406
  declare function ShareDialog({ open, onClose, size, className, title, description, ...panel }: ShareDialogProps): react.JSX.Element;
1808
2407
 
2408
+ /** The messages {@link IbanInput} shows under itself, one per {@link IbanProblem}. */
2409
+ interface IbanInputLabels {
2410
+ /** The value does not start with a two-letter country code and two check digits. */
2411
+ format: string;
2412
+ /** The first two letters are no country in the IBAN registry. `code` is those two
2413
+ * letters, as typed (upper case). */
2414
+ country: (code: string) => string;
2415
+ /** The wrong length for the country: `actual` characters typed, `expected` for that
2416
+ * country (spaces not counted in either). */
2417
+ length: (actual: number, expected: number) => string;
2418
+ /** The mod-97 check digits do not match — a mistyped or swapped character. */
2419
+ checksum: string;
2420
+ /** `kind="qr"`: a valid IBAN, but not a QR-IBAN. */
2421
+ qrRequired: string;
2422
+ /** `kind="plain"`: a QR-IBAN, which cannot receive an ordinary transfer. */
2423
+ qrNotAllowed: string;
2424
+ }
2425
+ declare const DEFAULT_IBAN_INPUT_LABELS: IbanInputLabels;
2426
+ interface IbanInputProps extends Omit<InputProps, "type" | "value" | "defaultValue"> {
2427
+ /** The IBAN, compact or not — it is shown in groups of four either way. `""` is
2428
+ * "no IBAN". */
2429
+ value: string;
2430
+ /** The compact, upper-case IBAN (the electronic format, what to store) on every
2431
+ * change — valid or not: a half-typed IBAN is still the field's value. Plain
2432
+ * `onChange` still fires alongside it with the raw event, whose text is the GROUPED
2433
+ * one, so bind this rather than `onChange` to a form. */
2434
+ onValueChange?: (value: string) => void;
2435
+ /** Which IBANs the field accepts. Default `"any"`. `"qr"` / `"plain"`: see
2436
+ * {@link IbanKind} — and why the difference matters, {@link isQrIban}. */
2437
+ kind?: IbanKind;
2438
+ /** The messages, per field. See {@link IbanInputLabels}. */
2439
+ labels?: Partial<IbanInputLabels>;
2440
+ /** The caller's own message, shown INSTEAD of the built-in one while it is set (a
2441
+ * server's "this account is already on file"). `null`, `false` and `""` are no
2442
+ * message, so `touched && errors.iban` can be passed as it is. */
2443
+ error?: ReactNode;
2444
+ }
2445
+ /**
2446
+ * An IBAN field (kastlan, keksdose): typed in any shape, shown in groups of four,
2447
+ * reported compact.
2448
+ *
2449
+ * Both apps had a text field and a regex. What that left out is everything between
2450
+ * the keyboard and the stored value: a pasted "ch09 0000-0000…" stayed lower case
2451
+ * with its hyphens, the grouping that makes 21 characters checkable by eye was
2452
+ * missing, and a wrong IBAN was found out by the bank. Here:
2453
+ *
2454
+ * - **Grouped as you type.** The field shows the paper format ({@link formatIban});
2455
+ * spaces, hyphens and lower case in what is typed or pasted are folded away, and
2456
+ * the caret stays between the same two characters while the groups move around it.
2457
+ * - **Compact out.** `onValueChange` gets the electronic format — upper case, no
2458
+ * spaces — which is the one to store and to compare.
2459
+ * - **Checked, never blocked.** A wrong country, length or checksum shows a message
2460
+ * under the field, through the kit's `error` (so it is the field's
2461
+ * `aria-describedby` and paints it), but every keystroke still lands: a field that
2462
+ * refuses characters cannot be corrected one character at a time. While the field
2463
+ * has focus only what more typing cannot fix is shown; the rest waits for the full
2464
+ * length or for blur. Whether the field is REQUIRED stays the form's rule — an empty
2465
+ * field says nothing.
2466
+ * - **QR-IBAN or not** (kastlan): `kind="qr"` refuses a regular IBAN and
2467
+ * `kind="plain"` refuses a QR-IBAN — a QR-bill can only be paid to a QR-IBAN, and a
2468
+ * QR-IBAN can receive nothing else. See {@link isQrIban}.
2469
+ *
2470
+ * It is {@link Input} underneath, so `label`, `hint`, `error`, `invalid` and the rest
2471
+ * are Input's own and the field reads like every other. A form that must not submit
2472
+ * an invalid IBAN checks {@link isValidIban} / {@link ibanProblem} itself — the field
2473
+ * shows the problem but does not own the submit.
2474
+ */
2475
+ declare const IbanInput: react.ForwardRefExoticComponent<IbanInputProps & react.RefAttributes<HTMLInputElement>>;
2476
+
2477
+ /** The words {@link PhoneInput} renders on its own behalf. */
2478
+ interface PhoneInputLabels {
2479
+ /** The country-code select's accessible name. */
2480
+ countryCode: string;
2481
+ /** The select's last option: a number from a country not listed, typed with its
2482
+ * own "+". */
2483
+ other: string;
2484
+ }
2485
+ declare const DEFAULT_PHONE_INPUT_LABELS: PhoneInputLabels;
2486
+ interface PhoneInputProps extends Omit<InputProps, "type" | "value" | "defaultValue"> {
2487
+ /** The number: E.164 ("+41210000001") for one the field could read, or free text —
2488
+ * a value saved before the field existed is shown exactly as it is. `""` is no
2489
+ * number. */
2490
+ value: string;
2491
+ /** On every change: E.164 when the typed text reads as a number of the selected
2492
+ * country (or carries its own "+"), otherwise the typed text UNCHANGED. Nothing is
2493
+ * refused — see {@link PhoneInput}. Plain `onChange` still fires with the raw event;
2494
+ * bind this one to a form. */
2495
+ onValueChange?: (value: string) => void;
2496
+ /** The country a number typed without "+" belongs to, until the value says
2497
+ * otherwise. Default `"CH"`. */
2498
+ defaultCountry?: PhoneCountry;
2499
+ /** The countries the select offers, in order; "Other" always follows. Default
2500
+ * {@link PHONE_COUNTRIES} (CH, LI, DE, AT, FR, IT). A stored number from a country
2501
+ * left out still shows under its own code. */
2502
+ countries?: readonly PhoneCountryCode[];
2503
+ /** See {@link PhoneInputLabels}. */
2504
+ labels?: Partial<PhoneInputLabels>;
2505
+ /** Classes for the whole field — the select, the input and the lines under them.
2506
+ * `inputClassName` reaches the `<input>`. */
2507
+ className?: string;
2508
+ }
2509
+ /**
2510
+ * A phone number field (kastlan): a country-code select and the number, stored as
2511
+ * E.164 and shown grouped.
2512
+ *
2513
+ * kastlan's contacts kept whatever was typed — "079 …", "+41 (0)79 …", "0041 79 …" —
2514
+ * which nobody could dial from the app or match against the next import. This field
2515
+ * turns all of those into one stored form, "+41210000001", without taking anything
2516
+ * away from the person typing:
2517
+ *
2518
+ * - **A country to type against.** The select (default CH, then LI, DE, AT, FR, IT
2519
+ * and "Other") says which country a number without "+" belongs to; the national
2520
+ * trunk 0 may be typed or left out. Typing a "+" or "00" overrides it, and the
2521
+ * select follows the number ("+49 …" switches it to DE). Changing the select moves
2522
+ * the national number to the new country code.
2523
+ * - **E.164 when it reads, the text when it does not.** A number the rules read is
2524
+ * emitted as E.164; anything else ("ask reception", an extension) is emitted exactly
2525
+ * as typed. Never refused, never an error of its own: a contact's number is often
2526
+ * known only roughly, and a field that will not save "079 … (mornings)" makes
2527
+ * people type it into the notes instead. An app that needs a dialable number
2528
+ * checks {@link isE164} on submit.
2529
+ * - **Existing values untouched.** A free-text value from before is shown exactly as
2530
+ * it is, and normalised only when someone edits it — the field never rewrites a
2531
+ * record just by being opened.
2532
+ * - **Grouped when settled.** While typing the field shows what was typed; on blur a
2533
+ * number it read is regrouped by its country ("21 000 00 01").
2534
+ *
2535
+ * **The limitation** — there is no libphonenumber behind this (≈150 kB of metadata in
2536
+ * every bundle): the six countries have light rules (the trunk 0, a plausible length)
2537
+ * and every other country is any "+" with 8–15 digits. A well-formed number that does
2538
+ * not exist passes, and German and Austrian landlines, whose area codes vary in length,
2539
+ * are grouped only where the split is known (mobile prefixes, the largest cities) and
2540
+ * otherwise shown as one block. See `lib/phone.ts`.
2541
+ *
2542
+ * `label`, `hint`, `error` and `invalid` read as on {@link Input}: the label floats in
2543
+ * the number's box, and a text hint and the error go under the whole row, attached to
2544
+ * the number with `aria-describedby`. The select is named by `labels.countryCode`, and
2545
+ * the pair is a group named by a string `label`.
2546
+ */
2547
+ declare const PhoneInput: react.ForwardRefExoticComponent<PhoneInputProps & react.RefAttributes<HTMLInputElement>>;
2548
+
1809
2549
  /**
1810
2550
  * EVERY string the kit renders, as one typed tree — and an optional provider that
1811
2551
  * hands it to every component at once.
@@ -2032,6 +2772,18 @@ interface UiKitLabels {
2032
2772
  translationReview: TranslationReviewLabels;
2033
2773
  /** 0.19.0: `LegalLinks`' navigation name — the legal pages' shell, for every app. */
2034
2774
  legal: LegalLabels;
2775
+ /** 0.22.0: the screen-reader words of `Input` / `Textarea`'s `showCount` counter. */
2776
+ characterCount: CharacterCountLabels;
2777
+ /** 0.22.0: `CountrySelect`. Its list's "no results" and counts are `combobox`'s. */
2778
+ countrySelect: CountrySelectLabels;
2779
+ /** 0.22.0: `InlineEditField` — keksdose K9. */
2780
+ inlineEdit: InlineEditLabels;
2781
+ /** 0.22.0: `IbanInput`'s messages, one per problem. */
2782
+ ibanInput: IbanInputLabels;
2783
+ /** 0.22.0: `PhoneInput`'s country-code select. */
2784
+ phoneInput: PhoneInputLabels;
2785
+ /** 0.22.0: `SignChip` — the outflow / inflow toggle beside an amount. */
2786
+ signChip: SignChipLabels;
2035
2787
  }
2036
2788
  /**
2037
2789
  * A label override, as deep as the labels go: an object of labels (a namespace, or a
@@ -2065,6 +2817,35 @@ declare const DEFAULT_FILE_LABELS: FileLabels;
2065
2817
  /** "12 kB", "3.4 MB" — `Intl`'s unit formatting, so the digits, the decimal mark and
2066
2818
  * the unit's spelling all follow `locale`. */
2067
2819
  declare function formatFileSize(bytes: number, locale?: string): string;
2820
+ /** What a {@link KitDateFormatter} is told about the date it is formatting. */
2821
+ interface KitDateFormatContext {
2822
+ /**
2823
+ * How much of a date the string is: `"day"` — the `iso` is `"YYYY-MM-DD"`; `"month"` —
2824
+ * `"YYYY-MM"` (a `MonthPicker`); `"year"` — `"YYYY"` (`MonthPicker mode="year"`).
2825
+ */
2826
+ unit: "day" | "month" | "year";
2827
+ /** Which kit surface asks: the trigger of a `DatePicker`, of a `DateRangePicker`
2828
+ * (called once per end), of a `MonthPicker`, or a `DateMark`. */
2829
+ source: "datePicker" | "dateRangePicker" | "monthPicker" | "dateMark";
2830
+ /** The locale the kit would have formatted in — the component's `locale` prop, else
2831
+ * the provider's — or `undefined` for the runtime's. */
2832
+ locale: string | undefined;
2833
+ /**
2834
+ * The kit's suggestion: whether this date stands alone where the day of the week
2835
+ * helps — `true` for a `DatePicker`'s trigger and a `DateMark`, `false` for the two
2836
+ * ends of a range (twice the width, in a sentence-like "from – to") and for a month
2837
+ * or a year. keksdose's rule, which this follows: *"A date in a COLUMN carries the
2838
+ * weekday. A date in a SENTENCE does not."* (date-cell.tsx, dev#546). A formatter
2839
+ * is free to ignore it.
2840
+ */
2841
+ weekday: boolean;
2842
+ }
2843
+ /**
2844
+ * The app's ONE answer to "what does a date look like here" — keksdose K12. Returns the
2845
+ * text for an ISO date (see {@link KitDateFormatContext.unit} for the three shapes).
2846
+ * Return `""` to fall back to the kit's own formatting for that date.
2847
+ */
2848
+ type KitDateFormatter = (iso: string, context: KitDateFormatContext) => string;
2068
2849
  /** Where a chart shows the values under the pointer — see `SeriesChartTooltip.placement`. */
2069
2850
  type ChartTooltipPlacement = "cursor" | "above" | "below" | "auto";
2070
2851
  /**
@@ -2127,6 +2908,25 @@ interface UiKitProviderProps {
2127
2908
  * pointer, as before.
2128
2909
  */
2129
2910
  chartTooltipPlacement?: ChartTooltipPlacement;
2911
+ /**
2912
+ * How every kit date below is written, when the component is not told otherwise —
2913
+ * keksdose K12. keksdose has a date-format preference (Settings ▸ date format, feedback
2914
+ * #180) that is not the UI language, and a weekday in the UI language beside digits
2915
+ * in the preference's order (live #246, dev#546): two locales in one string, which
2916
+ * no `locale` + `Intl` options can say. Its `DateField` wraps every `DatePicker` to
2917
+ * pass `formatValue`, and its report range field formats both ends by hand; with this
2918
+ * set once on the provider, those wrappers thin to nothing.
2919
+ *
2920
+ * Used for the trigger text of `DatePicker`, `DateRangePicker` and `MonthPicker`
2921
+ * (month and year mode), and for `DateMark display="date"`. A component's own
2922
+ * `formatValue` or `formatOptions` (`dateStyle` on a DateMark) wins: prop >
2923
+ * provider > the kit's `Intl` default, the order every kit setting resolves in. Left
2924
+ * out (or returning `""`), everything formats exactly as before.
2925
+ *
2926
+ * Keep it stable (`useCallback`, or a module function): it is in the provider's
2927
+ * context value, so a new function every render re-renders every consumer.
2928
+ */
2929
+ formatDate?: KitDateFormatter;
2130
2930
  children: ReactNode;
2131
2931
  }
2132
2932
  /**
@@ -2137,7 +2937,7 @@ interface UiKitProviderProps {
2137
2937
  * it names, so a page can re-label one table's `dataTable.table` without restating
2138
2938
  * the language.
2139
2939
  */
2140
- declare function UiKitProvider({ labels, locale, weekStartsOn, linkComponent, chartTooltipPlacement, children, }: UiKitProviderProps): react.JSX.Element;
2940
+ declare function UiKitProvider({ labels, locale, weekStartsOn, linkComponent, chartTooltipPlacement, formatDate, children, }: UiKitProviderProps): react.JSX.Element;
2141
2941
  /** What the nearest provider says about one namespace — `undefined` outside one.
2142
2942
  * For a component whose own resolver does more than a merge (the data table
2143
2943
  * derives `columnsCount` from `columns`): feed `{ ...overrides, ...props }` to it. */
@@ -2159,6 +2959,9 @@ declare function useKitLink(): KitLinkComponent | undefined;
2159
2959
  declare function useKitWeekStart(): WeekDay | undefined;
2160
2960
  /** The provider's `chartTooltipPlacement`, or `undefined` — a chart's own prop wins. */
2161
2961
  declare function useKitChartTooltipPlacement(): ChartTooltipPlacement | undefined;
2962
+ /** The nearest provider's `formatDate`, or `undefined` — a component's own
2963
+ * `formatValue` / `formatOptions` wins over it. See {@link KitDateFormatter}. */
2964
+ declare function useKitDateFormatter(): KitDateFormatter | undefined;
2162
2965
  /** {@link DEFAULT_FILE_LABELS}, but formatting in the provider's locale. */
2163
2966
  declare function useKitFileLabels(prop?: Partial<FileLabels>): FileLabels;
2164
2967
  /**
@@ -2175,4 +2978,4 @@ declare function useKitFileLabels(prop?: Partial<FileLabels>): FileLabels;
2175
2978
  */
2176
2979
  declare function missingKitLabels(labels: UiKitLabelOverrides | undefined, reference: UiKitLabels): string[];
2177
2980
 
2178
- export { FIELD_BASE as $, type AppShellLabels as A, Button as B, type CalculatorLabels as C, CopyButton as D, type CopyButtonLabels as E, type CopyButtonProps as F, type CurrencyLabels as G, DEFAULT_APP_SHELL_LABELS as H, DEFAULT_CALCULATOR_LABELS as I, DEFAULT_COMBOBOX_LABELS as J, DEFAULT_COMMON_LABELS as K, DEFAULT_COPY_BUTTON_LABELS as L, DEFAULT_CURRENCY_LABELS as M, DEFAULT_DATE_PICKER_LABELS as N, DEFAULT_FILE_LABELS as O, DEFAULT_FILE_PICKER_LABELS as P, DEFAULT_FORM_ACTIONS_LABELS as Q, DEFAULT_MULTI_SELECT_LABELS as R, DEFAULT_PASSWORD_REVEAL_LABELS as S, DEFAULT_PICKER_SHEET_LABELS as T, DEFAULT_SHARE_CARD_LABELS as U, DEFAULT_SWIPEABLE_ROW_LABELS as V, DEFAULT_TABS_LABELS as W, DEFAULT_TOP_BAR_LABELS as X, type DatePickerLabels as Y, EmptyState as Z, type EmptyStateProps as _, type ButtonClassesOptions as a, type SharePendingGrant as a$, FIELD_DISPLAY as a0, FIELD_FLOATING_PAD as a1, FIELD_INVALID as a2, FIELD_TRIGGER as a3, FIELD_WRITABLE_LOOK as a4, FLOATING_INPUT_CLASS as a5, FLOATING_LABEL_CLASS as a6, FLOATING_LABEL_STATIC as a7, FieldChevron as a8, type FieldChevronProps as a9, type IconButtonTone as aA, type IconButtonToneColor as aB, type IconButtonVariant as aC, Input as aD, type InputProps as aE, type KitLinkComponent as aF, type KitLinkProps as aG, Label as aH, type LabelOverride as aI, type LabelProps as aJ, type MultiSelectLabels as aK, PHONE_QUERY as aL, type PasswordRevealLabels as aM, type PickerSheetLabels as aN, type ResponsiveFormActionsPlacement as aO, Select as aP, type SelectProps as aQ, type ShareAddRequest as aR, type ShareCandidate as aS, ShareCard as aT, type ShareCardLabels as aU, type ShareCardProps as aV, ShareDialog as aW, type ShareDialogProps as aX, type ShareGrantee as aY, SharePanel as aZ, type SharePanelProps as a_, FieldHint as aa, type FieldHintProps as ab, FieldLabel as ac, type FieldLabelProps as ad, FileButton as ae, type FileButtonProps as af, type FileLabels as ag, type FilePickerLabels as ah, type FileRejection as ai, type FileRejectionReason as aj, type FileScreenOptions as ak, FloatingField as al, type FloatingFieldProps as am, FormActions as an, type FormActionsAlign as ao, type FormActionsDestructive as ap, type FormActionsLabels as aq, type FormActionsPlacement as ar, type FormActionsProps as as, type FormActionsStickyWithin as at, type FormActionsSubmitProps as au, IconButton as av, type IconButtonGlyphSize as aw, type IconButtonLinkProps as ax, type IconButtonProps as ay, type IconButtonSize as az, type ButtonLinkProps as b, type ShareRole as b0, Spinner as b1, type SpinnerProps as b2, type SwipeableRowLabels as b3, type TabItem as b4, Tabs as b5, type TabsLabels as b6, type TabsProps as b7, Textarea as b8, type TextareaProps as b9, type TopBarLabels as ba, type UiKitLabelOverrides as bb, type UiKitLabels as bc, UiKitProvider as bd, type UiKitProviderProps as be, type UseFilePickerOptions as bf, type UseFilePickerReturn as bg, buttonClasses as bh, formatFileSize as bi, matchesAccept as bj, missingKitLabels as bk, resolvePasswordRevealLabels as bl, useFilePicker as bm, useKitChartTooltipPlacement as bn, useKitFileLabels as bo, useKitLabelOverrides as bp, useKitLabels as bq, useKitLink as br, useKitLocale as bs, useKitWeekStart as bt, type FilePickHandler as bu, formatAccept as bv, judgePick as bw, screenFiles as bx, summariseRejections as by, type ButtonProps as c, type ButtonSize as d, type ButtonTone as e, type ButtonVariant as f, Card as g, CardAction as h, type CardActionProps as i, CardContent as j, type CardContentProps as k, type CardDensity as l, CardDescription as m, type CardDescriptionProps as n, CardFooter as o, type CardFooterProps as p, CardHeader as q, type CardHeaderProps as r, type CardProps as s, CardTitle as t, type CardTitleLevel as u, type CardTitleProps as v, type CardTone as w, type ChartTooltipPlacement as x, type ComboboxLabels as y, type CommonLabels as z };
2981
+ export { DEFAULT_FILE_LABELS as $, type AppShellLabels as A, Button as B, type CalculatorLabels as C, type CommonLabels as D, type ConfirmDialogLabels as E, type ConfirmFn as F, type ConfirmOptions as G, ConfirmProvider as H, type ConfirmProviderProps as I, type ConfirmTone as J, CopyButton as K, type CopyButtonLabels as L, type CopyButtonProps as M, type CurrencyLabels as N, CurrentPasswordInput as O, type CurrentPasswordInputLabels as P, type CurrentPasswordInputProps as Q, DEFAULT_APP_SHELL_LABELS as R, DEFAULT_CALCULATOR_LABELS as S, DEFAULT_CHARACTER_COUNT_LABELS as T, DEFAULT_COMBOBOX_LABELS as U, DEFAULT_COMMON_LABELS as V, DEFAULT_CONFIRM_DIALOG_LABELS as W, DEFAULT_COPY_BUTTON_LABELS as X, DEFAULT_CURRENCY_LABELS as Y, DEFAULT_DANGER_CONFIRM_LABELS as Z, DEFAULT_DATE_PICKER_LABELS as _, type ButtonClassesOptions as a, type InputProps as a$, DEFAULT_FILE_PICKER_LABELS as a0, DEFAULT_FORM_ACTIONS_LABELS as a1, DEFAULT_IBAN_INPUT_LABELS as a2, DEFAULT_MULTI_SELECT_LABELS as a3, DEFAULT_PASSWORD_REVEAL_LABELS as a4, DEFAULT_PHONE_INPUT_LABELS as a5, DEFAULT_PICKER_SHEET_LABELS as a6, DEFAULT_SHARE_CARD_LABELS as a7, DEFAULT_SWIPEABLE_ROW_LABELS as a8, DEFAULT_TABS_LABELS as a9, type FilePickerLabels as aA, type FileRejection as aB, type FileRejectionReason as aC, type FileScreenOptions as aD, FloatingField as aE, type FloatingFieldProps as aF, FormActions as aG, type FormActionsAlign as aH, type FormActionsDestructive as aI, type FormActionsLabels as aJ, type FormActionsPlacement as aK, type FormActionsProps as aL, type FormActionsStickyWithin as aM, type FormActionsSubmitProps as aN, type FormActionsSubmitShortcut as aO, IbanInput as aP, type IbanInputLabels as aQ, type IbanInputProps as aR, IconButton as aS, type IconButtonGlyphSize as aT, type IconButtonLinkProps as aU, type IconButtonProps as aV, type IconButtonSize as aW, type IconButtonTone as aX, type IconButtonToneColor as aY, type IconButtonVariant as aZ, Input as a_, DEFAULT_TOP_BAR_LABELS as aa, DangerConfirm as ab, type DangerConfirmLabels as ac, type DangerConfirmProps as ad, type DangerConsequence as ae, type DatePickerLabels as af, EmptyState as ag, type EmptyStateProps as ah, FIELD_BASE as ai, FIELD_DISPLAY as aj, FIELD_FLOATING_PAD as ak, FIELD_INVALID as al, FIELD_TRIGGER as am, FIELD_WRITABLE_LOOK as an, FLOATING_INPUT_CLASS as ao, FLOATING_LABEL_CLASS as ap, FLOATING_LABEL_STATIC as aq, FieldChevron as ar, type FieldChevronProps as as, FieldHint as at, type FieldHintProps as au, FieldLabel as av, type FieldLabelProps as aw, FileButton as ax, type FileButtonProps as ay, type FileLabels as az, type ButtonLinkProps as b, useKitLocale as b$, type KitDateFormatContext as b0, type KitDateFormatter as b1, type KitLinkComponent as b2, type KitLinkProps as b3, Label as b4, type LabelOverride as b5, type LabelProps as b6, type MultiSelectLabels as b7, PHONE_QUERY as b8, type PasswordRevealLabels as b9, Textarea as bA, type TextareaProps as bB, type TopBarLabels as bC, TypedConfirmField as bD, type TypedConfirmFieldLabels as bE, type TypedConfirmFieldProps as bF, type TypedMatch as bG, type UiKitLabelOverrides as bH, type UiKitLabels as bI, UiKitProvider as bJ, type UiKitProviderProps as bK, type UseFilePickerOptions as bL, type UseFilePickerReturn as bM, buttonClasses as bN, formatFileSize as bO, matchesAccept as bP, missingKitLabels as bQ, resolvePasswordRevealLabels as bR, typedMatches as bS, useConfirm as bT, useFilePicker as bU, useKitChartTooltipPlacement as bV, useKitDateFormatter as bW, useKitFileLabels as bX, useKitLabelOverrides as bY, useKitLabels as bZ, useKitLink as b_, PhoneInput as ba, type PhoneInputLabels as bb, type PhoneInputProps as bc, type PickerSheetLabels as bd, type ResponsiveFormActionsPlacement as be, Select as bf, type SelectProps as bg, type ShareAddRequest as bh, type ShareCandidate as bi, ShareCard as bj, type ShareCardLabels as bk, type ShareCardProps as bl, ShareDialog as bm, type ShareDialogProps as bn, type ShareGrantee as bo, SharePanel as bp, type SharePanelProps as bq, type SharePendingGrant as br, type ShareRole as bs, Spinner as bt, type SpinnerProps as bu, type SwipeableRowLabels as bv, type TabItem as bw, Tabs as bx, type TabsLabels as by, type TabsProps as bz, type ButtonProps as c, useKitWeekStart as c0, type FilePickHandler as c1, usePromisePending as c2, formatAccept as c3, judgePick as c4, screenFiles as c5, summariseRejections as c6, type ButtonSize as d, type ButtonTone as e, type ButtonVariant as f, Card as g, CardAction as h, type CardActionProps as i, CardContent as j, type CardContentProps as k, type CardDensity as l, CardDescription as m, type CardDescriptionProps as n, CardFooter as o, type CardFooterProps as p, CardHeader as q, type CardHeaderProps as r, type CardProps as s, CardTitle as t, type CardTitleLevel as u, type CardTitleProps as v, type CardTone as w, type CharacterCountLabels as x, type ChartTooltipPlacement as y, type ComboboxLabels as z };