@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 +1 @@
1
- {"version":3,"sources":["../../src/components/date-picker.tsx"],"sourcesContent":["import { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport type { ComponentPropsWithoutRef, ComponentType, ReactNode, RefObject } from \"react\";\nimport { Calendar, CalendarClock, ChevronLeft, ChevronRight, X } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { dirOf, type Direction } from \"../lib/direction\";\nimport { addDaysIso, parseIsoDate } from \"../lib/dates\";\nimport {\n DEFAULT_DATE_PICKER_LABELS,\n DEFAULT_PICKER_SHEET_LABELS,\n useKitLabels,\n useKitLocale,\n type DatePickerLabels,\n} from \"../i18n/kit-labels\";\nimport { splitTriggerAria } from \"./trigger-aria\";\nimport type { TriggerAria } from \"./trigger-aria\";\nimport { Button, FieldLabel, FIELD_BASE, FIELD_TRIGGER, FIELD_FLOATING_PAD, FIELD_INVALID, PHONE_QUERY } from \"./ui\";\nimport { FullBleedDialog } from \"./full-bleed-dialog\";\nimport { useMediaQuery } from \"../hooks/use-media-query\";\nimport { MiniCalendar, type MiniCalendarProps } from \"./mini-calendar\";\nimport { Popover } from \"./popover\";\nimport { Tooltip } from \"./tooltip\";\n\n/**\n * What both pickers share — and, through `ComponentPropsWithoutRef<\"div\">`, everything a\n * `<div>` takes, so a `data-tour` anchor, a test id or an `aria-describedby` reaches the\n * field's root element instead of being dropped (audit §api-design).\n *\n * `onChange` is omitted from those attributes: the DOM event of that name carries a\n * `FormEvent`, while both pickers answer with ISO date strings. Each declares its own\n * below.\n */\ninterface DatePickerBaseProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\"> {\n /** BCP 47 tag for the trigger's date and the calendar. Falls back to\n * `<UiKitProvider locale>`, then to the runtime's default. */\n locale?: string;\n /** Embedded top-boundary label, matching the native Input/Select fields. */\n label?: ReactNode;\n placeholder?: string;\n /** Inclusive ISO bounds for selectable days. */\n min?: string;\n max?: string;\n /** Show an X to clear the value (emits `\"\"`). */\n clearable?: boolean;\n /** Accessible name of the clear button. Wins over `datePicker.clear` from\n * `<UiKitProvider labels>`; kept as a prop because it predates the provider. */\n clearLabel?: string;\n disabled?: boolean;\n /** Required and unanswered — {@link FIELD_INVALID}. See {@link Input}'s `invalid`. */\n invalid?: boolean;\n className?: string;\n /** Intl options for the trigger's formatted date (default: locale short date). */\n formatOptions?: Intl.DateTimeFormatOptions;\n /** Render the trigger's text for an ISO date yourself; wins over `formatOptions`.\n * For a host whose date rendering is more than one `Intl` call can say — Keksdose\n * puts the weekday's name in the UI language beside digits ordered by a separate\n * format preference, two locales in one string. `locale` still drives the calendar. */\n formatValue?: (iso: string) => string;\n /** Passed through to the {@link MiniCalendar} this opens. Its month arrows are\n * icon-only, so their `aria-label` is the only name they have — and the package\n * ships English defaults, which a translating host has to be able to replace.\n * Usually unnecessary now: the calendar reads `miniCalendar` from the provider. */\n calendarLabels?: MiniCalendarProps[\"labels\"];\n}\n\n/** Format an ISO date for the trigger. `lib/dates`' `formatIsoDate` insists on a\n * locale, and here there may be none — no prop and no provider — which `Intl` reads\n * as \"the runtime's default\", as every other kit formatter does. */\nfunction formatDate(iso: string, locale: string | undefined, options?: Intl.DateTimeFormatOptions) {\n return parseIsoDate(iso)?.toLocaleDateString(locale, options) ?? \"\";\n}\n\n/**\n * Everything the kit's own trigger button carries, handed to a custom one\n * ({@link DateRangePickerProps.renderTrigger}) to spread onto its `<button>`. Spread\n * it WHOLE: the ref is how focus comes back when the panel closes, the id pair and\n * `aria-labelledby` are the 0.5.1 naming (an external `<label htmlFor>`, a form\n * library's `aria-describedby`), and `role`/`aria-haspopup`/`aria-controls`/\n * `aria-expanded` are what tell a screen reader this opens a calendar and whether it\n * is open. `className` is the kit's field look (42px with a label, `pe-9` for the\n * glyph the field draws at its end) — merge yours after it with `cn`.\n */\nexport interface DateTriggerAttributes {\n ref: RefObject<HTMLButtonElement | null>;\n type: \"button\";\n id?: string;\n role: \"combobox\";\n \"aria-haspopup\": \"dialog\";\n \"aria-controls\": string;\n \"aria-expanded\": boolean;\n \"aria-labelledby\"?: string;\n \"aria-label\"?: string;\n \"aria-describedby\"?: string;\n \"aria-invalid\"?: true;\n /** The field's required state — `Field required` hands it down with the id pair. */\n \"aria-required\"?: boolean | \"true\" | \"false\";\n disabled?: boolean;\n onClick: () => void;\n className: string;\n}\n\n/** A custom trigger, as `DateField` calls it; each picker adapts it to its own props. */\ntype TriggerRenderer = (attrs: DateTriggerAttributes, valueId: string) => ReactNode;\n\n/** A caller's trigger, as an element of its own: the attributes carry the trigger's\n * ref, and a ref is handed to a child as a prop, not passed to a function in the\n * middle of the parent's render. */\nfunction CustomTrigger({\n render,\n attrs,\n valueId,\n}: {\n render: TriggerRenderer;\n attrs: DateTriggerAttributes;\n valueId: string;\n}) {\n return render(attrs, valueId);\n}\n\n/**\n * The field-shaped button that opens the calendar.\n *\n * A component of its own rather than JSX inline in `Popover`'s `trigger` render prop,\n * because handing focus back to it is an effect and a render prop is not somewhere a\n * hook can go.\n */\nfunction DateFieldTrigger({\n open,\n toggle,\n triggerRef,\n triggerText,\n hasValue,\n labelledBy,\n valueId,\n padded,\n disabled,\n invalid,\n required,\n panelId,\n aria,\n renderTrigger,\n}: {\n /** The caller's naming/description attributes, routed here from the field. */\n aria: TriggerAria;\n /** Draw the button yourself, with these attributes — see {@link DateTriggerAttributes}. */\n renderTrigger?: TriggerRenderer;\n open: boolean;\n toggle: () => void;\n triggerRef: RefObject<HTMLButtonElement | null>;\n /** The panel's id — `role=\"combobox\"` requires `aria-controls`. */\n panelId: string;\n triggerText: string;\n hasValue: boolean;\n /** ids of the hidden label twin + the value span, in the order they are spoken. */\n labelledBy?: string;\n valueId: string;\n padded: boolean;\n disabled?: boolean;\n invalid?: boolean;\n /** `aria-required`, on the trigger with the rest of the field's ARIA. */\n required?: boolean | \"true\" | \"false\";\n}) {\n const wasOpen = useRef(false);\n useEffect(() => {\n const justClosed = wasOpen.current && !open;\n wasOpen.current = open;\n if (!justClosed) return;\n // Only when the panel still HAD focus. The day the user was standing on has just\n // been unmounted with the panel, which drops focus to <body> and leaves a\n // keyboard user back at the top of the document with no idea where they were.\n // But an outside click closes this panel too, and that click has already given\n // focus to whatever it landed on — dragging it back here would be worse than the\n // bug, so a focus that is somewhere real is left alone.\n const active = document.activeElement;\n if (!active || active === document.body) triggerRef.current?.focus();\n // Here rather than through `useFocusTrap`'s `restoreFocus`, and not only because\n // `Popover` runs no trap to carry it: that option restores to whatever held focus\n // when the panel opened, and on Safari — and on Firefox on macOS — clicking a\n // button does not focus it, so \"whatever held focus\" is the page. The requirement\n // is this trigger, every time, however the panel was opened.\n }, [open, triggerRef]);\n\n // One attribute object, worn either by the kit's own button below or by a caller's\n // (`renderTrigger`) — so a custom trigger cannot quietly fall out of step with the\n // naming and state this one announces.\n const isInvalid = Boolean(invalid) || aria[\"aria-invalid\"] === true || aria[\"aria-invalid\"] === \"true\";\n const attrs: DateTriggerAttributes = {\n ref: triggerRef,\n type: \"button\",\n disabled,\n onClick: toggle,\n // The name has to carry the VALUE. A bare `aria-label={label}` — which is what\n // this was — replaces the button's text outright, so the one field in the form\n // whose entire job is to show a date announced \"Due date\" and stopped there,\n // and a screen-reader user could not read back what they had picked without\n // opening the calendar and hunting for the selected day.\n //\n // Two ids rather than one composed string (`common.fieldValue`): \"label: value\"\n // is a sentence, and a name reference is spoken as the two texts in order with\n // no punctuation to translate at all. The hidden twin of the FieldLabel lives\n // in DateField, next to the label it copies.\n id: aria.id,\n \"aria-labelledby\": aria[\"aria-label\"] && !aria[\"aria-labelledby\"] ? undefined : labelledBy,\n \"aria-label\": aria[\"aria-label\"],\n \"aria-describedby\": aria[\"aria-describedby\"],\n // `role=\"combobox\"` on a button that opens a calendar is the APG date-picker\n // shape, and it is what makes the next two lines legal: `button` supports\n // neither `aria-expanded` nor `aria-invalid`, so the previous markup set an\n // invalid state that announced nothing (ESLint's `role-supports-aria-props`).\n //\n // `aria-haspopup=\"dialog\"` is now honest — it was not when this comment first\n // said so. `Popover` gained a real `role=\"dialog\"` in the same wave, so the\n // trigger's promise and the panel's role finally agree. The phone sheet is a\n // `role=\"dialog\"` too, and carries the same id.\n role: \"combobox\",\n \"aria-haspopup\": \"dialog\",\n \"aria-controls\": panelId,\n \"aria-expanded\": open,\n \"aria-invalid\": isInvalid || undefined,\n \"aria-required\": required,\n className: cn(\n FIELD_TRIGGER,\n \"pe-9\",\n padded && FIELD_FLOATING_PAD,\n disabled && \"cursor-not-allowed opacity-50\",\n // An `aria-invalid` from a Field spread paints too, as on Input and AmountInput.\n isInvalid && FIELD_INVALID,\n ),\n };\n if (renderTrigger) return <CustomTrigger render={renderTrigger} attrs={attrs} valueId={valueId} />;\n\n return (\n <button {...attrs}>\n <span id={valueId} className={cn(\"truncate\", !hasValue && \"text-[var(--text-placeholder)]\")}>\n {/* `|| \" \"` (a NON-BREAKING space) — triggerText is \"\" when there is no value and no\n placeholder was passed. An empty span has no line box, so the\n trigger collapses to its padding and sits shorter than every\n other field beside it.\n\n It has to be a NON-BREAKING space. This read `|| \" \"` first, and\n that fix did nothing at all: an ordinary space is collapsible\n white space, and white space at the start and end of a line is\n removed — so the span rendered with height 0 and the trigger\n stayed 22px against the 42px of the fields either side of it\n (Keksdose live #294, measured in the browser: `span.textContent`\n WAS `\" \"` and `getBoundingClientRect().height` was 0). U+00A0 is\n not collapsible, so it holds the line. */}\n {triggerText || \" \"}\n </span>\n </button>\n );\n}\n\n/**\n * How a panel's body and its action row are put on screen — the popover stacks them,\n * the phone sheet pins the actions in its footer. Handed to the panel rather than\n * applied around it because the actions belong to the panel's own draft state\n * (`RangePanel`), while the frame they sit in belongs to the field.\n */\ntype PanelFrame = (body: ReactNode, actions?: ReactNode) => ReactNode;\n\nconst popoverFrame: PanelFrame = (body, actions) =>\n actions ? (\n <div className=\"flex flex-col gap-2\">\n {body}\n <div className=\"flex items-center justify-end gap-2 border-t border-[var(--border)] pt-2\">{actions}</div>\n </div>\n ) : (\n body\n );\n\n/** A field-styled trigger that opens a portalled MiniCalendar. Shared by the\n * single- and range-date pickers below. */\nfunction DateField({\n triggerText,\n hasValue,\n label,\n disabled,\n clearable,\n clearLabel,\n panelLabel,\n className,\n width,\n onClear,\n invalid,\n sheet = false,\n sheetBackCloses = true,\n renderTrigger,\n children,\n // The trigger's, not the wrapper's: `Field`'s render-prop spreads it with the id\n // pair, and a required date read as optional while it sat on the role-less div.\n // Taken here rather than in `splitTriggerAria`, which MonthPicker shares.\n \"aria-required\": ariaRequired,\n ...rest\n}: Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> & {\n triggerText: string;\n hasValue: boolean;\n label?: ReactNode;\n disabled?: boolean;\n clearable?: boolean;\n /** Already resolved by the picker (prop > provider > English). */\n clearLabel: string;\n /** The popover panel's accessible name, resolved likewise. */\n panelLabel: string;\n invalid?: boolean;\n className?: string;\n /** Popover panel width; omit for the default (a bare calendar). */\n width?: number;\n onClear: () => void;\n /** Open as a full-screen sheet instead of the popover — the picker decides when\n * (below {@link PHONE_QUERY}). */\n sheet?: boolean;\n /** The sheet's `FullBleedDialog backCloses`. */\n sheetBackCloses?: boolean;\n renderTrigger?: TriggerRenderer;\n children: (close: () => void, frame: PanelFrame) => ReactNode;\n}) {\n const showClear = Boolean(clearable && hasValue && !disabled);\n const [aria, wrapperRest] = splitTriggerAria(rest);\n // The panel is portalled to <body> and leaves the subtree whose `dir` it inherited,\n // so a calendar in an RTL form opened LTR — the grid's columns, its arrow keys and\n // its chevrons all the wrong way round. Read at the moment of opening (the only way\n // the popover opens is through `toggle`) and put back on the panel.\n const rootRef = useRef<HTMLDivElement>(null);\n const [dir, setDir] = useState<Direction>(\"ltr\");\n const id = useId();\n const labelId = `${id}-label`;\n const valueId = `${id}-value`;\n const panelId = `${id}-panel`;\n // Only a STRING label can be reused as a name; a ReactNode may be a whole row with\n // its own interactive \"?\" hint in it, and naming a button with that reads the hint\n // out as part of the field's name.\n const named = typeof label === \"string\";\n // The sheet's open flag. Only the phone branch owns one: on desktop `Popover` owns\n // it, so the two can never disagree about what is on screen.\n const [sheetOpen, setSheetOpen] = useState(false);\n const sheetTriggerRef = useRef<HTMLButtonElement>(null);\n const closeSheet = () => setSheetOpen(false);\n const sheetLabels = useKitLabels(\"pickerSheet\", DEFAULT_PICKER_SHEET_LABELS);\n const sheetFrame: PanelFrame = (body, actions) => (\n <FullBleedDialog\n open\n onClose={closeSheet}\n // The same id the popover panel wears, so the trigger's `aria-controls` holds in\n // both presentations; named the way the popover is.\n id={panelId}\n aria-label={panelLabel}\n header={panelLabel}\n // `pickerSheet.close`: the one \"close this full-screen picker\" string the kit\n // already translates, rather than a second key saying the same thing.\n closeLabel={sheetLabels.close}\n backCloses={sheetBackCloses}\n dir={dir}\n // Portalled to <body>, the sheet is not inside the field's wrapper, so a\n // document-level \"outside click\" listener — a Popover or dropdown this field\n // sits in — would read a tap on a day as outside and unmount the cell before\n // its click (Keksdose dev#477, the fix `PickerSheet` carries).\n onMouseDown={(e) => e.stopPropagation()}\n footer={actions}\n >\n {body}\n </FullBleedDialog>\n );\n const fieldTrigger = ({\n open,\n toggle,\n ref,\n }: {\n open: boolean;\n toggle: () => void;\n ref: RefObject<HTMLButtonElement | null>;\n }) => (\n <DateFieldTrigger\n open={open}\n toggle={() => {\n setDir(dirOf(rootRef.current));\n toggle();\n }}\n triggerRef={ref}\n triggerText={triggerText}\n hasValue={hasValue}\n // ALWAYS a name reference, never `undefined`. A `<button>` takes its\n // accessible name from its contents, so an unlabelled trigger used to\n // announce its own date text for free. `role=\"combobox\"` does not —\n // content is excluded from name computation for that role — so leaving\n // this undefined made an unlabelled date field announce nothing at all.\n // Named: \"label, value\". Unnamed: the value alone, which is what the\n // button was saying before.\n //\n // A caller's own reference comes first. And when the caller gives the field\n // an `id` (so an external `<label htmlFor>` can point at it), the trigger\n // lists ITSELF first: a self-reference in `aria-labelledby` is resolved\n // from the element's native label (accname 2B → 2D), so \"Due date\" from\n // that <label> is spoken before the value, instead of being overridden.\n labelledBy={[\n aria[\"aria-labelledby\"],\n named ? labelId : !aria[\"aria-labelledby\"] && aria.id ? aria.id : undefined,\n valueId,\n ]\n .filter(Boolean)\n .join(\" \")}\n aria={aria}\n panelId={panelId}\n valueId={valueId}\n padded={label !== undefined}\n disabled={disabled}\n invalid={invalid}\n required={ariaRequired}\n renderTrigger={renderTrigger}\n />\n );\n return (\n // The caller's attributes land here, on the field's own box — the trigger inside is\n // named by `aria-labelledby` and must keep the id pair it is given.\n <div {...wrapperRest} ref={rootRef} className={cn(\"relative\", className)}>\n {label !== undefined && <FieldLabel>{label}</FieldLabel>}\n {/* The visible FieldLabel is a plain span, not a `<label htmlFor>`, so it names\n nothing on its own — this hidden twin is what the trigger is named by.\n `aria-hidden` because a name reference reads a hidden element deliberately\n (accname §4.3.1), and without it the label would be announced twice: once as\n loose text before the button, once inside the button's own name.\n `sr-only-fixed` rather than `sr-only` — see the note on the class in\n tokens.css. */}\n {named && (\n <span id={labelId} aria-hidden className=\"sr-only-fixed\">\n {label}\n </span>\n )}\n {sheet ? (\n <>\n {fieldTrigger({ open: sheetOpen, toggle: () => setSheetOpen((v) => !v), ref: sheetTriggerRef })}\n {/* Mounted only while open, like the popover's children: the panel's draft is\n born with it, so every open starts again from the committed value. */}\n {sheetOpen && children(closeSheet, sheetFrame)}\n </>\n ) : (\n <Popover\n width={width}\n panelId={panelId}\n // Named for what it is. Unnamed, it fell back to `popover.panel`, and a date\n // field announced its calendar as \"Popover\" — in English, in every language.\n labels={{ panel: panelLabel }}\n dir={dir}\n trigger={fieldTrigger}\n >\n {(close) => children(close, popoverFrame)}\n </Popover>\n )}\n {showClear ? (\n <button\n type=\"button\"\n aria-label={clearLabel}\n onClick={onClear}\n className=\"absolute end-1.5 top-1/2 -translate-y-1/2 rounded p-1 text-[var(--text-placeholder)] hover:text-[var(--text-secondary)]\"\n >\n <X className=\"size-4\" />\n </button>\n ) : (\n <Calendar\n aria-hidden\n className=\"pointer-events-none absolute end-2.5 top-1/2 size-4 -translate-y-1/2 text-[var(--text-placeholder)]\"\n />\n )}\n </div>\n );\n}\n\n/** One ‹ / › day-step button, sized to sit flush beside the field. */\nfunction StepButton({\n icon: Icon,\n label,\n disabled,\n onClick,\n mirror,\n className,\n}: {\n icon: ComponentType<{ className?: string }>;\n label: string;\n disabled: boolean;\n onClick: () => void;\n /** Flip the icon in RTL — for the ‹ › day steps, whose \"previous\" points to the\n * reading start. Not for the today icon, which has no direction. */\n mirror?: boolean;\n /** The button's corners in the joined group (see DatePicker). */\n className?: string;\n}) {\n return (\n <Tooltip label={label} portal>\n <button\n type=\"button\"\n aria-label={label}\n disabled={disabled}\n onClick={onClick}\n className={cn(\n FIELD_BASE,\n // FIELD_BASE is `block w-full` for text inputs; a step button is neither, and\n // px-3 would make it wider than it needs to be. twMerge lets these win.\n \"relative flex h-full w-10 shrink-0 items-center justify-center px-0 text-[var(--text-muted)]\",\n \"hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]\",\n // Raised while hovered or focused, so the shared border and the focus ring are\n // drawn on top of the neighbour they overlap by a pixel.\n \"hover:z-10 focus-visible:z-10\",\n // Dimmed ICON, not a dimmed box: at 40% opacity the whole button — border\n // included — faded out of the group and read as a broken, detached control.\n \"disabled:cursor-not-allowed disabled:text-[var(--text-placeholder)] disabled:opacity-60 disabled:hover:bg-[var(--bg-surface)]\",\n className,\n )}\n >\n <Icon className={cn(\"size-4\", mirror && \"rtl:-scale-x-100\")} />\n </button>\n </Tooltip>\n );\n}\n\nexport interface DatePickerProps extends DatePickerBaseProps {\n /** ISO \"YYYY-MM-DD\", or \"\" for empty. */\n value: string;\n onChange: (iso: string) => void;\n /**\n * Flank the field with ‹ › buttons that move the value one day. For fields whose\n * typical edit is a day or two — opening a calendar to go \"yesterday\" is three\n * interactions for what should be one.\n *\n * Off by default: on a filter or a far-away date the buttons are dead weight, and\n * they cost ~5rem of width that a narrow layout may not have.\n */\n step?: boolean;\n /** Accessible names for the step buttons. Optional: they resolve from\n * `datePicker.previousDay` / `nextDay` in `<UiKitProvider labels>`, then from the\n * English defaults. This prop wins over both, for the one field that says it\n * differently. */\n stepLabels?: { prev: string; next: string };\n /**\n * Today's date as ISO \"YYYY-MM-DD\". Adds a jump-to-today button beside the field,\n * disabled once the value already is that day.\n *\n * Passed in rather than read from a clock here: a component that decides what \"today\"\n * is cannot be tested at a fixed date, and the consumer already has one definition of\n * it. The button is always RENDERED — going flat instead of disappearing — because a\n * control that comes and goes with the value reflows whatever sits beside it, which\n * is the report this exists to answer.\n */\n today?: string;\n /** Accessible name for the today button; wins over `datePicker.today`. */\n todayLabel?: string;\n}\n\n/**\n * The picker's own label props, folded into one `datePicker` override so they sit at\n * the top of the same precedence chain as everything else: prop > provider > English.\n * They predate the provider and stay public API, which is why they are not simply\n * replaced by a `labels` prop.\n */\nfunction useDatePickerLabels(\n clearLabel: string | undefined,\n stepLabels: { prev: string; next: string } | undefined,\n todayLabel: string | undefined,\n): DatePickerLabels {\n const prev = stepLabels?.prev;\n const next = stepLabels?.next;\n const fromProps = useMemo(() => {\n const out: Partial<DatePickerLabels> = {};\n if (clearLabel !== undefined) out.clear = clearLabel;\n if (prev !== undefined) out.previousDay = prev;\n if (next !== undefined) out.nextDay = next;\n if (todayLabel !== undefined) out.today = todayLabel;\n return out;\n }, [clearLabel, prev, next, todayLabel]);\n return useKitLabels(\"datePicker\", DEFAULT_DATE_PICKER_LABELS, fromProps);\n}\n\n/** Single-date picker: a field showing the formatted date, opening a calendar. */\nexport function DatePicker({\n value,\n onChange,\n locale: localeProp,\n placeholder,\n min,\n max,\n formatOptions,\n formatValue,\n step,\n stepLabels,\n today,\n todayLabel,\n className,\n calendarLabels,\n label,\n clearable,\n clearLabel,\n disabled,\n invalid,\n // Named so it reaches the field (and so its trigger) when a step/today row is the\n // root and takes the rest of the caller's props.\n \"aria-required\": ariaRequired,\n ...rest\n}: DatePickerProps) {\n const locale = useKitLocale(localeProp);\n const text = useDatePickerLabels(clearLabel, stepLabels, todayLabel);\n const render = formatValue ?? ((iso: string) => formatDate(iso, locale, formatOptions));\n // With a step or a today button beside it the field is no longer the outermost\n // element — the flex row at the bottom is, and a caller's attributes belong on\n // whichever of the two is actually the root. Naming the shared props rather than\n // letting `...rest` carry them is what makes that choice possible.\n const wrapped = Boolean(step || today);\n const [triggerAria, wrapperRest] = splitTriggerAria(rest);\n const field = (\n <DateField\n // With step/today buttons the flex row is the root and takes the caller's props —\n // except the ones that name the field, which still belong on its trigger.\n {...(wrapped ? triggerAria : rest)}\n aria-required={ariaRequired}\n label={label}\n clearable={clearable}\n clearLabel={text.clear}\n panelLabel={text.panel}\n disabled={disabled}\n invalid={invalid}\n className={step ? \"min-w-0 flex-1\" : className}\n hasValue={Boolean(value)}\n triggerText={value ? render(value) : (placeholder ?? \"\")}\n onClear={() => onChange(\"\")}\n >\n {(close) => (\n <MiniCalendar\n mode=\"single\"\n focusOnOpen\n from={value}\n to={value}\n locale={locale}\n min={min}\n max={max}\n labels={calendarLabels}\n onSelect={(iso) => {\n onChange(iso);\n close();\n }}\n />\n )}\n </DateField>\n );\n if (!wrapped) return field;\n\n // An empty field has nothing to step from, so both buttons are dead until a date\n // is picked. Bounds are compared as strings: \"YYYY-MM-DD\" sorts chronologically.\n const target = (days: number) => (value ? addDaysIso(value, days) : \"\");\n const outOfBounds = (iso: string) => Boolean((min && iso < min) || (max && iso > max));\n const blocked = (days: number) => {\n const next = target(days);\n if (!next || disabled) return true;\n return outOfBounds(next);\n };\n return (\n // ONE joined control, not three boxes in a row: the buttons and the field share\n // their borders (each piece overlaps the previous by a pixel) and only the outer\n // ends are rounded — the button-group shape, so ‹ date › reads as one field with\n // controls rather than a field with loose buttons parked beside it, which is what\n // the `gap-1` version looked like.\n //\n // items-stretch, not items-center: the buttons match the field's height, which\n // varies with whether it carries a floating label. The field's trigger is nested\n // inside DateField, so its inner corners are squared from here.\n <div\n {...wrapperRest}\n className={cn(\n \"flex items-stretch [&>*:not(:first-child)]:-ms-px\",\n step && \"[&_[role=combobox]]:rounded-s-none\",\n (step || today) && \"[&_[role=combobox]]:rounded-e-none\",\n \"[&_[role=combobox]:focus-visible]:relative [&_[role=combobox]:focus-visible]:z-10\",\n className,\n )}\n >\n {step && (\n <StepButton\n icon={ChevronLeft}\n mirror\n label={text.previousDay}\n disabled={blocked(-1)}\n onClick={() => onChange(target(-1))}\n className=\"rounded-e-none\"\n />\n )}\n {field}\n {step && (\n <StepButton\n icon={ChevronRight}\n mirror\n label={text.nextDay}\n disabled={blocked(1)}\n onClick={() => onChange(target(1))}\n className={cn(\"rounded-s-none\", today && \"rounded-e-none\")}\n />\n )}\n {today && (\n <StepButton\n icon={CalendarClock}\n label={text.today}\n disabled={Boolean(disabled) || value === today || outOfBounds(today)}\n onClick={() => onChange(today)}\n className=\"rounded-s-none\"\n />\n )}\n </div>\n );\n}\n\nexport interface DateRangePickerPreset {\n /**\n * The preset's identity, independent of its dates — `\"last_3_months\"`, not two ISO\n * strings. Optional for the 0.x callers that pass anonymous pairs.\n *\n * keksdose's report range field (report-range-field.tsx, \"Why this is app-level\")\n * is why it exists: its URL stores `?preset=last_3_months` and recomputes the dates\n * from today on every read, so a preset that is only its dates froze \"last 3 months\"\n * at whatever day the panel rendered, and two presets that happen to cover the same\n * days (this week and today, on a Monday) could not be told apart. With an `id` the\n * choice is reported to `onChange` and shown as selected for as long as the range\n * still matches it. Recompute `from`/`to` on every render (`calendarMonthPresets()`,\n * `dateRangePresets()`) and pass the key as the id.\n */\n id?: string;\n label: ReactNode;\n from: string;\n to: string;\n}\n\n/**\n * When the picker hands its range to `onChange`.\n *\n * - `\"immediate\"` (the default, and all of 0.7): a preset click commits and closes,\n * and each calendar click is reported as it happens — the half-made `(from, \"\")`\n * included. The right shape for quick ranges on a filter, where one click is one\n * answer.\n * - `\"apply\"`: the panel works on a DRAFT. A preset ARMS the calendar instead of\n * committing, days can then be nudged (\"last 3 months, but end it last Friday\"),\n * and nothing reaches `onChange` until Apply — which stays disabled until both ends\n * exist. Cancel, Escape and an outside click discard the draft, and every open\n * starts again from the committed value. keksdose's report range asked for exactly\n * this: each commit there re-runs a whole page of charts, so an intermediate state\n * must never escape the panel.\n */\nexport type DateRangeCommit = \"immediate\" | \"apply\";\n\nexport interface DateRangePickerProps extends DatePickerBaseProps {\n from: string;\n to: string;\n /** The committed range. `presetId` is the chosen preset's `id` when a preset\n * produced it (and the range was not nudged since), `undefined` for days picked by\n * hand. A 0.x two-argument handler simply ignores it. */\n onChange: (from: string, to: string, presetId?: string) => void;\n /** Separator between the two formatted dates in the trigger (default \" – \"). */\n separator?: string;\n /** Optional named ranges rendered as a start-side column (last month / YTD / …). */\n presets?: DateRangePickerPreset[];\n /**\n * The active preset's `id`, controlled — e.g. straight from keksdose's `?preset=`.\n * `null` (or an id no preset has, such as `\"custom\"`) says \"no preset\": nothing in\n * the column is marked even if a preset happens to cover the same days. A preset is\n * marked only while its dates still equal `from`/`to`.\n *\n * Leave it `undefined` and the picker remembers the last preset it committed\n * itself; with no memory at all it falls back to the first preset whose dates match.\n */\n preset?: string | null;\n /** See {@link DateRangeCommit}. Default `\"immediate\"`. */\n commit?: DateRangeCommit;\n /**\n * Draw the trigger yourself. keksdose's report range field\n * (reports/report-range-field.tsx, \"Why this is app-level\") is why: its trigger\n * NAMES the active preset (\"Last 3 months\", the window as a muted suffix) where the\n * kit's shows two dates, sits inside a Tooltip, and is squared off on one side so the\n * granularity control can join it flush at the field's 42px.\n *\n * Spread `props.triggerProps` onto a `<button>` whole and put `props.valueProps` on\n * the element that shows the value — see {@link DateTriggerAttributes} for what each\n * part is for. The field around it (label, hidden name twin, clear/calendar glyph,\n * popover or phone sheet) is unchanged.\n */\n renderTrigger?: (props: DateRangeTriggerRenderProps) => ReactNode;\n /**\n * Below {@link PHONE_QUERY} the panel opens as a full-screen sheet (`FullBleedDialog`)\n * with presets in a grid above the calendar and Apply pinned in the footer, instead\n * of the 440px popover that hung off a 360px screen (keksdose's report range field,\n * which hand-rolled exactly this). This is its `backCloses`: on by default, so Back\n * dismisses the sheet; off for a caller whose commit rewrites the URL with\n * `replaceState` in the same tick — keksdose's `?preset/from/to` — where the router\n * would overwrite the sheet's history marker.\n */\n sheetBackCloses?: boolean;\n /**\n * With `commit=\"apply\"`: a line above the calendar saying what Apply would commit —\n * keksdose's report range writes \"Custom · 1 Mar 2026 – …\" there\n * (report-range-field.tsx), the one place a preset armed and then nudged by a day is\n * seen to have become \"Custom\", and a half-made range to still lack its end. Called\n * with the DRAFT on every change, in the popover and the phone sheet alike; `preset`\n * is the preset the column marks for it (none once a day was picked by hand), `to` is\n * `\"\"` until the second click. The line is a polite live region, so a screen reader\n * hears the draft change as the calendar is used.\n *\n * Ignored in `\"immediate\"` mode, where there is no draft: every click is the value.\n */\n renderDraftSummary?: (draft: DateRangeDraftSummary) => ReactNode;\n}\n\n/** What {@link DateRangePickerProps.renderDraftSummary} is called with. */\nexport interface DateRangeDraftSummary {\n from: string;\n to: string;\n /** The preset the column marks for the draft, if any. */\n preset: DateRangePickerPreset | undefined;\n}\n\n/** What {@link DateRangePickerProps.renderTrigger} is called with. */\nexport interface DateRangeTriggerRenderProps {\n /** Spread onto the `<button>`, whole. Merge your class after `triggerProps.className`. */\n triggerProps: DateTriggerAttributes;\n /** Put on the element showing the value: the trigger is named \"label, value\" by\n * reference to this id. Without it the name is the label alone. */\n valueProps: { id: string };\n open: boolean;\n from: string;\n to: string;\n /** The preset the column marks for the committed range, if any — the one to name. */\n preset: DateRangePickerPreset | undefined;\n /** What the kit's own trigger would show: the formatted range, or the placeholder. */\n text: string;\n}\n\ninterface RangeDraft {\n from: string;\n to: string;\n presetId: string | undefined;\n}\n\n/** Which preset (by index) the column marks, or -1. See `DateRangePickerProps.preset`. */\nfunction markedPreset(\n presets: readonly DateRangePickerPreset[],\n from: string,\n to: string,\n activeId: string | null | undefined,\n controlled: boolean,\n): number {\n const matches = (p: DateRangePickerPreset) => p.from === from && p.to === to;\n if (activeId != null) {\n const byId = presets.findIndex((p) => p.id === activeId && matches(p));\n if (byId !== -1 || controlled) return byId;\n } else if (controlled) {\n return -1;\n }\n return presets.findIndex(matches);\n}\n\n/**\n * The preset column: a named group of toggle buttons, `aria-pressed` on the marked one.\n * Pressed-state rather than colour alone, because \"which range is this\" is the one\n * thing a screen-reader user opening the panel needs to hear.\n */\nfunction PresetColumn({\n presets,\n marked,\n label,\n onPick,\n sheet,\n}: {\n presets: readonly DateRangePickerPreset[];\n marked: number;\n label: string;\n onPick: (p: DateRangePickerPreset) => void;\n /** The phone sheet's shape: a two-column grid ABOVE the calendar with 44px rows —\n * a 128px column beside a month grid leaves the grid ~200px on a 360px screen. */\n sheet?: boolean;\n}) {\n return (\n // `border-e`/`pe`, not `-r`: the preset column is on the START side, and the rule\n // between it and the calendar has to follow it in a right-to-left UI.\n <div\n role=\"group\"\n aria-label={label}\n className={\n sheet\n ? \"grid grid-cols-2 gap-0.5\"\n : \"flex w-32 shrink-0 flex-col gap-0.5 border-e border-[var(--border)] pe-2\"\n }\n >\n {presets.map((p, i) => {\n const selected = i === marked;\n return (\n <button\n key={p.id ?? i}\n type=\"button\"\n aria-pressed={selected}\n data-preset={p.id}\n onClick={() => onPick(p)}\n className={cn(\n \"rounded px-2 text-start\",\n // 44px on a phone: the kit's touch-target size (`SHEET_ROW_CLASS`).\n sheet ? \"min-h-11 py-2 text-sm\" : \"py-1.5 text-xs\",\n selected\n ? \"bg-[var(--bg-active)] font-medium text-[var(--text-primary)]\"\n : \"text-[var(--text-secondary)] hover:bg-[var(--bg-hover)]\",\n )}\n >\n {p.label}\n </button>\n );\n })}\n </div>\n );\n}\n\n/**\n * The panel's body. A component of its own so `commit=\"apply\"` can keep its draft in\n * state that is born when the panel opens: `Popover` unmounts its children on close,\n * so the draft is re-seeded from the committed value on every open, and an abandoned\n * half-selection cannot survive into the next one.\n */\nfunction RangePanel({\n from,\n to,\n presets,\n activeId,\n controlled,\n commit,\n close,\n frame,\n sheet,\n commitRange,\n calendarProps,\n labels,\n renderDraftSummary,\n}: {\n from: string;\n to: string;\n presets: readonly DateRangePickerPreset[] | undefined;\n activeId: string | null | undefined;\n controlled: boolean;\n commit: DateRangeCommit;\n close: () => void;\n /** Where the body and the Apply row go — see `PanelFrame`. */\n frame: PanelFrame;\n sheet: boolean;\n commitRange: (from: string, to: string, presetId: string | undefined) => void;\n calendarProps: Pick<MiniCalendarProps, \"locale\" | \"min\" | \"max\" | \"labels\">;\n labels: DatePickerLabels;\n renderDraftSummary?: DateRangePickerProps[\"renderDraftSummary\"];\n}) {\n const drafting = commit === \"apply\";\n const [draft, setDraft] = useState<RangeDraft>({\n from,\n to,\n presetId: activeId ?? undefined,\n });\n // What the panel SHOWS: the draft while drafting, the committed value otherwise.\n const shown = drafting ? draft : { from, to, presetId: activeId ?? undefined };\n const marked = presets\n ? markedPreset(presets, shown.from, shown.to, drafting ? draft.presetId : activeId, drafting || controlled)\n : -1;\n const complete = Boolean(draft.from && draft.to);\n\n const summary =\n drafting && renderDraftSummary\n ? renderDraftSummary({ from: draft.from, to: draft.to, preset: presets?.[marked] })\n : null;\n\n const picker = (\n <MiniCalendar\n {...calendarProps}\n focusOnOpen\n from={shown.from}\n to={shown.to}\n onSelect={(f, t) => {\n if (drafting) {\n setDraft({ from: f, to: t, presetId: undefined });\n return;\n }\n commitRange(f, t, undefined);\n // Two-click range: only dismiss once both ends are chosen.\n if (f && t) close();\n }}\n />\n );\n // The summary heads the CALENDAR's column, not the panel: beside a preset column it\n // reads as the calendar's caption, and in the sheet it sits between the presets and\n // the month it describes. `null` renders no line — a caller may opt out per draft —\n // but keeps the wrapper, so the calendar is not remounted (and its month and focus\n // lost) when a line comes or goes.\n const calendar =\n drafting && renderDraftSummary ? (\n <div className=\"flex flex-col gap-2\">\n {summary != null && (\n <div aria-live=\"polite\" data-draft-summary=\"\" className=\"text-xs text-[var(--text-muted)]\">\n {summary}\n </div>\n )}\n {picker}\n </div>\n ) : (\n picker\n );\n\n const body =\n presets && presets.length > 0 ? (\n <div className={sheet ? \"flex flex-col gap-3\" : \"flex gap-3\"}>\n <PresetColumn\n sheet={sheet}\n presets={presets}\n marked={marked}\n label={labels.presets}\n onPick={(p) => {\n if (drafting) {\n setDraft({ from: p.from, to: p.to, presetId: p.id });\n return;\n }\n commitRange(p.from, p.to, p.id);\n close();\n }}\n />\n {/* `flex-1`: the calendar takes the rest of the panel. Content-sized it was\n seven tiny cells beside a wide preset column. */}\n <div className=\"min-w-0 flex-1\">{calendar}</div>\n </div>\n ) : (\n calendar\n );\n\n if (!drafting) return frame(body);\n return frame(\n body,\n <>\n {/* `flex-1` in the sheet: two thumb-wide halves of the footer, not two small\n buttons in its corner. */}\n <Button type=\"button\" variant=\"ghost\" className={sheet ? \"flex-1\" : undefined} onClick={close}>\n {labels.cancel}\n </Button>\n <Button\n type=\"button\"\n className={sheet ? \"flex-1\" : undefined}\n // A half-made `(from, \"\")` would commit a window with no end — a blank\n // report, one click into a two-click gesture.\n disabled={!complete}\n onClick={() => {\n if (!complete) return;\n commitRange(draft.from, draft.to, draft.presetId);\n close();\n }}\n >\n {labels.apply}\n </Button>\n </>,\n );\n}\n\n/** Two-date range picker: click a start then an end; closes once both are set.\n * With `presets`, a column of named ranges is shown beside the calendar; with\n * `commit=\"apply\"` the panel drafts and commits on Apply (see {@link DateRangeCommit}). */\nexport function DateRangePicker({\n from,\n to,\n onChange,\n locale: localeProp,\n placeholder,\n min,\n max,\n formatOptions,\n formatValue,\n separator = \" – \",\n presets,\n preset,\n commit = \"immediate\",\n renderTrigger,\n sheetBackCloses,\n renderDraftSummary,\n calendarLabels,\n label,\n clearable,\n clearLabel,\n disabled,\n invalid,\n ...rest\n}: DateRangePickerProps) {\n const locale = useKitLocale(localeProp);\n const text = useDatePickerLabels(clearLabel, undefined, undefined);\n const render = formatValue ?? ((iso: string) => formatDate(iso, locale, formatOptions));\n // The preset this picker committed itself, for a caller that does not control\n // `preset`. Cleared by a hand-picked range or a clear.\n const [ownPreset, setOwnPreset] = useState<string | undefined>(undefined);\n const controlled = preset !== undefined;\n const activeId = controlled ? preset : ownPreset;\n const a = from ? render(from) : \"\";\n const b = to ? render(to) : \"\";\n const triggerText = from\n ? to\n ? `${a}${separator}${b}`\n : `${a}${separator}…`\n : (placeholder ?? \"\");\n const hasPresets = Boolean(presets && presets.length > 0);\n const sheet = useMediaQuery(PHONE_QUERY, false);\n const commitRange = (f: string, t: string, presetId: string | undefined) => {\n setOwnPreset(presetId);\n // Two arguments when no preset is involved — exactly the 0.7 call, so a caller's\n // `toHaveBeenCalledWith(from, to)` and a variadic handler see no difference.\n if (presetId === undefined) onChange(f, t);\n else onChange(f, t, presetId);\n };\n return (\n <DateField\n {...rest}\n label={label}\n clearable={clearable}\n clearLabel={text.clear}\n panelLabel={text.rangePanel}\n disabled={disabled}\n invalid={invalid}\n // Widen so the preset column sits beside the calendar (default otherwise).\n width={hasPresets ? 440 : undefined}\n hasValue={Boolean(from || to)}\n triggerText={triggerText}\n onClear={() => commitRange(\"\", \"\", undefined)}\n sheet={sheet}\n sheetBackCloses={sheetBackCloses}\n renderTrigger={\n renderTrigger &&\n ((triggerProps, valueId) =>\n renderTrigger({\n triggerProps,\n valueProps: { id: valueId },\n open: triggerProps[\"aria-expanded\"],\n from,\n to,\n preset: presets?.[markedPreset(presets, from, to, activeId, controlled)],\n text: triggerText,\n }))\n }\n >\n {(close, frame) => (\n <RangePanel\n from={from}\n to={to}\n presets={presets}\n activeId={activeId}\n controlled={controlled}\n commit={commit}\n close={close}\n frame={frame}\n sheet={sheet}\n commitRange={commitRange}\n calendarProps={{ locale, min, max, labels: calendarLabels }}\n labels={text}\n renderDraftSummary={renderDraftSummary}\n />\n )}\n </DateField>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/date-picker.tsx"],"sourcesContent":["import { useEffect, useId, useMemo, useRef, useState } from \"react\";\nimport type { ComponentPropsWithoutRef, ComponentType, KeyboardEvent, ReactNode, RefObject } from \"react\";\nimport { Calendar, CalendarClock, ChevronDown, ChevronLeft, ChevronRight, X } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { dirOf, horizontalStep, type Direction } from \"../lib/direction\";\nimport { addDaysIso, monthKey, pad, parseIsoDate } from \"../lib/dates\";\nimport {\n DEFAULT_DATE_PICKER_LABELS,\n DEFAULT_PICKER_SHEET_LABELS,\n useKitDateFormatter,\n useKitLabels,\n useKitLocale,\n type DatePickerLabels,\n type KitDateFormatContext,\n} from \"../i18n/kit-labels\";\nimport { splitTriggerAria } from \"./trigger-aria\";\nimport type { TriggerAria } from \"./trigger-aria\";\nimport { Button, FIELD_BASE, FIELD_TRIGGER, FIELD_FLOATING_PAD, FIELD_INVALID, PHONE_QUERY } from \"./ui\";\nimport { FieldBox, FieldLabelLine, useFieldMessages } from \"./field-parts\";\nimport { FullBleedDialog } from \"./full-bleed-dialog\";\nimport { useMediaQuery } from \"../hooks/use-media-query\";\nimport { DEFAULT_MINI_CALENDAR_LABELS, MiniCalendar, type MiniCalendarProps } from \"./mini-calendar\";\nimport { DEFAULT_MONTH_PICKER_LABELS } from \"./month-picker\";\nimport { Popover } from \"./popover\";\nimport { Tooltip } from \"./tooltip\";\n\n/**\n * What both pickers share — and, through `ComponentPropsWithoutRef<\"div\">`, everything a\n * `<div>` takes, so a `data-tour` anchor, a test id or an `aria-describedby` reaches the\n * field's root element instead of being dropped (audit §api-design).\n *\n * `onChange` is omitted from those attributes: the DOM event of that name carries a\n * `FormEvent`, while both pickers answer with ISO date strings. Each declares its own\n * below.\n */\ninterface DatePickerBaseProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"onChange\"> {\n /** BCP 47 tag for the trigger's date and the calendar. Falls back to\n * `<UiKitProvider locale>`, then to the runtime's default. */\n locale?: string;\n /** Embedded top-boundary label, matching the native Input/Select fields. */\n label?: ReactNode;\n placeholder?: string;\n /** Inclusive ISO bounds for selectable days. */\n min?: string;\n max?: string;\n /** Show an X to clear the value (emits `\"\"`). */\n clearable?: boolean;\n /** Accessible name of the clear button. Wins over `datePicker.clear` from\n * `<UiKitProvider labels>`; kept as a prop because it predates the provider. */\n clearLabel?: string;\n disabled?: boolean;\n /** Required and unanswered — {@link FIELD_INVALID}. See {@link Input}'s `invalid`. */\n invalid?: boolean;\n /**\n * Standing advice for the field (keksdose K4, \"uniform field anatomy\"), as on a\n * {@link Select}: plain TEXT is a caption under the field, attached with\n * `aria-describedby`; a {@link FieldHint} rides the label line (or, unlabelled, sits\n * at the field's end, outside the box).\n */\n hint?: ReactNode;\n /**\n * What is wrong with the date, in the caller's words (\"The due date is before the\n * invoice date\") — {@link Input}'s `error` (keksdose K4: DateField errors had no slot).\n * Rendered under the field (under the whole row, with `step` / `today`), attached to\n * the trigger with `aria-describedby` — merged with any the caller passed, after the\n * caption — and implies `invalid`. Passing the key at all keeps the field's box while\n * there is no message, so the trigger is never remounted as one comes and goes.\n */\n error?: ReactNode;\n className?: string;\n /** Intl options for the trigger's formatted date (default: locale short date). Win\n * over the provider's `formatDate`. */\n formatOptions?: Intl.DateTimeFormatOptions;\n /** Render the trigger's text for an ISO date yourself; wins over `formatOptions` and\n * over the provider's `formatDate`.\n * For a host whose date rendering is more than one `Intl` call can say — Keksdose\n * puts the weekday's name in the UI language beside digits ordered by a separate\n * format preference, two locales in one string. `locale` still drives the calendar.\n * Set that rendering once with `<UiKitProvider formatDate>` (keksdose K12) and this\n * is for the one field that differs. */\n formatValue?: (iso: string) => string;\n /** Passed through to the {@link MiniCalendar} this opens. Its month arrows are\n * icon-only, so their `aria-label` is the only name they have — and the package\n * ships English defaults, which a translating host has to be able to replace.\n * Usually unnecessary now: the calendar reads `miniCalendar` from the provider. */\n calendarLabels?: MiniCalendarProps[\"labels\"];\n /**\n * Make the panel's caption (\"July 2026\") a button that opens the year's twelve\n * months, with ‹ › stepping a year; picking a month returns to its days. Far dates\n * stop being a march of month arrows.\n *\n * kastlan is why: its service-charge period runs July to June, and with one month on\n * screen and ‹ › the only way across, the end of the period was eleven clicks past\n * the start — so kastlan kept two date fields instead of `RhfDateRangePicker`. With\n * the jump the whole period is open, caption, Jul, 1, caption, next year, Jun, 30.\n *\n * What it adds, for the keyboard and a screen reader:\n * - the caption is a disclosure button (`aria-expanded`). Its name is the month it\n * shows, so speech input can say what it sees; it is `aria-live`, as the\n * calendar's caption was, so paging months still says where you landed; and it is\n * described by `monthPicker.panel` (\"Choose a month\").\n * - the month grid is `MonthPicker`'s grid: one tab stop, arrows walk the months\n * (across the year's edge), Home/End the row, PageUp/PageDown a year, Enter or a\n * click picks. Focus opens on the month on show and comes back on its days — the\n * selected day if it is in that month, else the 1st. Months outside `min`/`max`\n * are `aria-disabled`, and a year arrow is once the whole year beyond is.\n * - Escape in the month grid goes back to the days without closing the panel (focus\n * on the caption); in the days, Escape closes the panel as before.\n *\n * No new strings: the month arrows keep `miniCalendar.previousMonth` / `nextMonth`\n * (`calendarLabels`), and the month grid speaks with `MonthPicker`'s words —\n * `monthPicker.previousYear` / `nextYear` / `month` / `panel` — because it is that\n * grid, and a host that translated it once should not have to again.\n *\n * Off by default: the caption becomes one more stop in the panel's tab order, which a\n * 0.x minor does not spring on a form. Works in the popover and in\n * `DateRangePicker`'s phone sheet alike, beside presets and in `commit=\"apply\"`\n * (a preset picked while the months are open goes back to the days, on its start).\n *\n * Why not a two-month panel (`months={2}`) instead, or beside it: for a twelve-month\n * period it still pages six times where this takes one year step, and two\n * `MiniCalendar`s side by side each follow the value on their own — picking the end\n * in the right-hand month would pull the left one onto it — so the calendar would\n * have to learn to share a month first. Not worth that for a saving of one arrow on a\n * range across a single month boundary.\n */\n monthJump?: boolean;\n}\n\n/** Format an ISO date for the trigger. `lib/dates`' `formatIsoDate` insists on a\n * locale, and here there may be none — no prop and no provider — which `Intl` reads\n * as \"the runtime's default\", as every other kit formatter does. */\nfunction formatDate(iso: string, locale: string | undefined, options?: Intl.DateTimeFormatOptions) {\n return parseIsoDate(iso)?.toLocaleDateString(locale, options) ?? \"\";\n}\n\n/**\n * The trigger's date renderer, in the kit's order of precedence: the picker's own\n * `formatValue`, then its `formatOptions` (an explicit `Intl` look for this field),\n * then the provider's `formatDate` (keksdose K12), then the locale's short date. A\n * provider formatter that answers `\"\"` falls through to the default for that date.\n */\nfunction useTriggerFormat(\n source: KitDateFormatContext[\"source\"],\n locale: string | undefined,\n formatOptions: Intl.DateTimeFormatOptions | undefined,\n formatValue: ((iso: string) => string) | undefined,\n): (iso: string) => string {\n const fromProvider = useKitDateFormatter();\n if (formatValue) return formatValue;\n return (iso) => {\n if (formatOptions === undefined && fromProvider) {\n // A single date stands alone, where the weekday helps; a range's two ends do not.\n const text = fromProvider(iso, { unit: \"day\", source, locale, weekday: source === \"datePicker\" });\n if (text) return text;\n }\n return formatDate(iso, locale, formatOptions);\n };\n}\n\n/**\n * Everything the kit's own trigger button carries, handed to a custom one\n * ({@link DateRangePickerProps.renderTrigger}) to spread onto its `<button>`. Spread\n * it WHOLE: the ref is how focus comes back when the panel closes, the id pair and\n * `aria-labelledby` are the 0.5.1 naming (an external `<label htmlFor>`, a form\n * library's `aria-describedby`), and `role`/`aria-haspopup`/`aria-controls`/\n * `aria-expanded` are what tell a screen reader this opens a calendar and whether it\n * is open. `className` is the kit's field look (42px with a label, `pe-9` for the\n * glyph the field draws at its end) — merge yours after it with `cn`.\n */\nexport interface DateTriggerAttributes {\n ref: RefObject<HTMLButtonElement | null>;\n type: \"button\";\n id?: string;\n role: \"combobox\";\n \"aria-haspopup\": \"dialog\";\n \"aria-controls\": string;\n \"aria-expanded\": boolean;\n \"aria-labelledby\"?: string;\n \"aria-label\"?: string;\n \"aria-describedby\"?: string;\n \"aria-invalid\"?: true;\n /** The field's required state — `Field required` hands it down with the id pair. */\n \"aria-required\"?: boolean | \"true\" | \"false\";\n disabled?: boolean;\n onClick: () => void;\n className: string;\n}\n\n/** A custom trigger, as `DateField` calls it; each picker adapts it to its own props. */\ntype TriggerRenderer = (attrs: DateTriggerAttributes, valueId: string) => ReactNode;\n\n/** A caller's trigger, as an element of its own: the attributes carry the trigger's\n * ref, and a ref is handed to a child as a prop, not passed to a function in the\n * middle of the parent's render. */\nfunction CustomTrigger({\n render,\n attrs,\n valueId,\n}: {\n render: TriggerRenderer;\n attrs: DateTriggerAttributes;\n valueId: string;\n}) {\n return render(attrs, valueId);\n}\n\n/**\n * The field-shaped button that opens the calendar.\n *\n * A component of its own rather than JSX inline in `Popover`'s `trigger` render prop,\n * because handing focus back to it is an effect and a render prop is not somewhere a\n * hook can go.\n */\nfunction DateFieldTrigger({\n open,\n toggle,\n triggerRef,\n triggerText,\n hasValue,\n labelledBy,\n valueId,\n padded,\n disabled,\n invalid,\n required,\n panelId,\n aria,\n renderTrigger,\n}: {\n /** The caller's naming/description attributes, routed here from the field. */\n aria: TriggerAria;\n /** Draw the button yourself, with these attributes — see {@link DateTriggerAttributes}. */\n renderTrigger?: TriggerRenderer;\n open: boolean;\n toggle: () => void;\n triggerRef: RefObject<HTMLButtonElement | null>;\n /** The panel's id — `role=\"combobox\"` requires `aria-controls`. */\n panelId: string;\n triggerText: string;\n hasValue: boolean;\n /** ids of the hidden label twin + the value span, in the order they are spoken. */\n labelledBy?: string;\n valueId: string;\n padded: boolean;\n disabled?: boolean;\n invalid?: boolean;\n /** `aria-required`, on the trigger with the rest of the field's ARIA. */\n required?: boolean | \"true\" | \"false\";\n}) {\n const wasOpen = useRef(false);\n useEffect(() => {\n const justClosed = wasOpen.current && !open;\n wasOpen.current = open;\n if (!justClosed) return;\n // Only when the panel still HAD focus. The day the user was standing on has just\n // been unmounted with the panel, which drops focus to <body> and leaves a\n // keyboard user back at the top of the document with no idea where they were.\n // But an outside click closes this panel too, and that click has already given\n // focus to whatever it landed on — dragging it back here would be worse than the\n // bug, so a focus that is somewhere real is left alone.\n const active = document.activeElement;\n if (!active || active === document.body) triggerRef.current?.focus();\n // Here rather than through `useFocusTrap`'s `restoreFocus`, and not only because\n // `Popover` runs no trap to carry it: that option restores to whatever held focus\n // when the panel opened, and on Safari — and on Firefox on macOS — clicking a\n // button does not focus it, so \"whatever held focus\" is the page. The requirement\n // is this trigger, every time, however the panel was opened.\n }, [open, triggerRef]);\n\n // One attribute object, worn either by the kit's own button below or by a caller's\n // (`renderTrigger`) — so a custom trigger cannot quietly fall out of step with the\n // naming and state this one announces.\n const isInvalid = Boolean(invalid) || aria[\"aria-invalid\"] === true || aria[\"aria-invalid\"] === \"true\";\n const attrs: DateTriggerAttributes = {\n ref: triggerRef,\n type: \"button\",\n disabled,\n onClick: toggle,\n // The name has to carry the VALUE. A bare `aria-label={label}` — which is what\n // this was — replaces the button's text outright, so the one field in the form\n // whose entire job is to show a date announced \"Due date\" and stopped there,\n // and a screen-reader user could not read back what they had picked without\n // opening the calendar and hunting for the selected day.\n //\n // Two ids rather than one composed string (`common.fieldValue`): \"label: value\"\n // is a sentence, and a name reference is spoken as the two texts in order with\n // no punctuation to translate at all. The hidden twin of the FieldLabel lives\n // in DateField, next to the label it copies.\n id: aria.id,\n \"aria-labelledby\": aria[\"aria-label\"] && !aria[\"aria-labelledby\"] ? undefined : labelledBy,\n \"aria-label\": aria[\"aria-label\"],\n \"aria-describedby\": aria[\"aria-describedby\"],\n // `role=\"combobox\"` on a button that opens a calendar is the APG date-picker\n // shape, and it is what makes the next two lines legal: `button` supports\n // neither `aria-expanded` nor `aria-invalid`, so the previous markup set an\n // invalid state that announced nothing (ESLint's `role-supports-aria-props`).\n //\n // `aria-haspopup=\"dialog\"` is now honest — it was not when this comment first\n // said so. `Popover` gained a real `role=\"dialog\"` in the same wave, so the\n // trigger's promise and the panel's role finally agree. The phone sheet is a\n // `role=\"dialog\"` too, and carries the same id.\n role: \"combobox\",\n \"aria-haspopup\": \"dialog\",\n \"aria-controls\": panelId,\n \"aria-expanded\": open,\n \"aria-invalid\": isInvalid || undefined,\n \"aria-required\": required,\n className: cn(\n FIELD_TRIGGER,\n \"pe-9\",\n padded && FIELD_FLOATING_PAD,\n disabled && \"cursor-not-allowed opacity-50\",\n // An `aria-invalid` from a Field spread paints too, as on Input and AmountInput.\n isInvalid && FIELD_INVALID,\n ),\n };\n if (renderTrigger) return <CustomTrigger render={renderTrigger} attrs={attrs} valueId={valueId} />;\n\n return (\n <button {...attrs}>\n <span id={valueId} className={cn(\"truncate\", !hasValue && \"text-[var(--text-placeholder)]\")}>\n {/* `|| \" \"` (a NON-BREAKING space) — triggerText is \"\" when there is no value and no\n placeholder was passed. An empty span has no line box, so the\n trigger collapses to its padding and sits shorter than every\n other field beside it.\n\n It has to be a NON-BREAKING space. This read `|| \" \"` first, and\n that fix did nothing at all: an ordinary space is collapsible\n white space, and white space at the start and end of a line is\n removed — so the span rendered with height 0 and the trigger\n stayed 22px against the 42px of the fields either side of it\n (Keksdose live #294, measured in the browser: `span.textContent`\n WAS `\" \"` and `getBoundingClientRect().height` was 0). U+00A0 is\n not collapsible, so it holds the line. */}\n {triggerText || \" \"}\n </span>\n </button>\n );\n}\n\n/**\n * How a panel's body and its action row are put on screen — the popover stacks them,\n * the phone sheet pins the actions in its footer. Handed to the panel rather than\n * applied around it because the actions belong to the panel's own draft state\n * (`RangePanel`), while the frame they sit in belongs to the field.\n */\ntype PanelFrame = (body: ReactNode, actions?: ReactNode) => ReactNode;\n\nconst popoverFrame: PanelFrame = (body, actions) =>\n actions ? (\n <div className=\"flex flex-col gap-2\">\n {body}\n <div className=\"flex items-center justify-end gap-2 border-t border-[var(--border)] pt-2\">{actions}</div>\n </div>\n ) : (\n body\n );\n\n/** A field-styled trigger that opens a portalled MiniCalendar. Shared by the\n * single- and range-date pickers below. */\nfunction DateField({\n triggerText,\n hasValue,\n label,\n disabled,\n clearable,\n clearLabel,\n panelLabel,\n className,\n width,\n onClear,\n invalid,\n sheet = false,\n sheetBackCloses = true,\n renderTrigger,\n labelHint,\n children,\n // The trigger's, not the wrapper's: `Field`'s render-prop spreads it with the id\n // pair, and a required date read as optional while it sat on the role-less div.\n // Taken here rather than in `splitTriggerAria`, which MonthPicker shares.\n \"aria-required\": ariaRequired,\n ...rest\n}: Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> & {\n triggerText: string;\n hasValue: boolean;\n label?: ReactNode;\n /** A `FieldHint` for the label line, beside the label. */\n labelHint?: ReactNode;\n disabled?: boolean;\n clearable?: boolean;\n /** Already resolved by the picker (prop > provider > English). */\n clearLabel: string;\n /** The popover panel's accessible name, resolved likewise. */\n panelLabel: string;\n invalid?: boolean;\n className?: string;\n /** Popover panel width; omit for the default (a bare calendar). */\n width?: number;\n onClear: () => void;\n /** Open as a full-screen sheet instead of the popover — the picker decides when\n * (below {@link PHONE_QUERY}). */\n sheet?: boolean;\n /** The sheet's `FullBleedDialog backCloses`. */\n sheetBackCloses?: boolean;\n renderTrigger?: TriggerRenderer;\n children: (close: () => void, frame: PanelFrame) => ReactNode;\n}) {\n const showClear = Boolean(clearable && hasValue && !disabled);\n const [aria, wrapperRest] = splitTriggerAria(rest);\n // The panel is portalled to <body> and leaves the subtree whose `dir` it inherited,\n // so a calendar in an RTL form opened LTR — the grid's columns, its arrow keys and\n // its chevrons all the wrong way round. Read at the moment of opening (the only way\n // the popover opens is through `toggle`) and put back on the panel.\n const rootRef = useRef<HTMLDivElement>(null);\n const [dir, setDir] = useState<Direction>(\"ltr\");\n const id = useId();\n const labelId = `${id}-label`;\n const valueId = `${id}-value`;\n const panelId = `${id}-panel`;\n // Only a STRING label can be reused as a name; a ReactNode may be a whole row with\n // its own interactive \"?\" hint in it, and naming a button with that reads the hint\n // out as part of the field's name.\n const named = typeof label === \"string\";\n // The sheet's open flag. Only the phone branch owns one: on desktop `Popover` owns\n // it, so the two can never disagree about what is on screen.\n const [sheetOpen, setSheetOpen] = useState(false);\n const sheetTriggerRef = useRef<HTMLButtonElement>(null);\n const closeSheet = () => setSheetOpen(false);\n const sheetLabels = useKitLabels(\"pickerSheet\", DEFAULT_PICKER_SHEET_LABELS);\n const sheetFrame: PanelFrame = (body, actions) => (\n <FullBleedDialog\n open\n onClose={closeSheet}\n // The same id the popover panel wears, so the trigger's `aria-controls` holds in\n // both presentations; named the way the popover is.\n id={panelId}\n aria-label={panelLabel}\n header={panelLabel}\n // `pickerSheet.close`: the one \"close this full-screen picker\" string the kit\n // already translates, rather than a second key saying the same thing.\n closeLabel={sheetLabels.close}\n backCloses={sheetBackCloses}\n dir={dir}\n // Portalled to <body>, the sheet is not inside the field's wrapper, so a\n // document-level \"outside click\" listener — a Popover or dropdown this field\n // sits in — would read a tap on a day as outside and unmount the cell before\n // its click (Keksdose dev#477, the fix `PickerSheet` carries).\n onMouseDown={(e) => e.stopPropagation()}\n footer={actions}\n >\n {body}\n </FullBleedDialog>\n );\n const fieldTrigger = ({\n open,\n toggle,\n ref,\n }: {\n open: boolean;\n toggle: () => void;\n ref: RefObject<HTMLButtonElement | null>;\n }) => (\n <DateFieldTrigger\n open={open}\n toggle={() => {\n setDir(dirOf(rootRef.current));\n toggle();\n }}\n triggerRef={ref}\n triggerText={triggerText}\n hasValue={hasValue}\n // ALWAYS a name reference, never `undefined`. A `<button>` takes its\n // accessible name from its contents, so an unlabelled trigger used to\n // announce its own date text for free. `role=\"combobox\"` does not —\n // content is excluded from name computation for that role — so leaving\n // this undefined made an unlabelled date field announce nothing at all.\n // Named: \"label, value\". Unnamed: the value alone, which is what the\n // button was saying before.\n //\n // A caller's own reference comes first. And when the caller gives the field\n // an `id` (so an external `<label htmlFor>` can point at it), the trigger\n // lists ITSELF first: a self-reference in `aria-labelledby` is resolved\n // from the element's native label (accname 2B → 2D), so \"Due date\" from\n // that <label> is spoken before the value, instead of being overridden.\n labelledBy={[\n aria[\"aria-labelledby\"],\n named ? labelId : !aria[\"aria-labelledby\"] && aria.id ? aria.id : undefined,\n valueId,\n ]\n .filter(Boolean)\n .join(\" \")}\n aria={aria}\n panelId={panelId}\n valueId={valueId}\n padded={label !== undefined}\n disabled={disabled}\n invalid={invalid}\n required={ariaRequired}\n renderTrigger={renderTrigger}\n />\n );\n return (\n // The caller's attributes land here, on the field's own box — the trigger inside is\n // named by `aria-labelledby` and must keep the id pair it is given.\n <div {...wrapperRest} ref={rootRef} className={cn(\"relative\", className)}>\n {label !== undefined && <FieldLabelLine label={label} hint={labelHint} />}\n {/* The visible FieldLabel is a plain span, not a `<label htmlFor>`, so it names\n nothing on its own — this hidden twin is what the trigger is named by.\n `aria-hidden` because a name reference reads a hidden element deliberately\n (accname §4.3.1), and without it the label would be announced twice: once as\n loose text before the button, once inside the button's own name.\n `sr-only-fixed` rather than `sr-only` — see the note on the class in\n tokens.css. */}\n {named && (\n <span id={labelId} aria-hidden className=\"sr-only-fixed\">\n {label}\n </span>\n )}\n {sheet ? (\n <>\n {fieldTrigger({ open: sheetOpen, toggle: () => setSheetOpen((v) => !v), ref: sheetTriggerRef })}\n {/* Mounted only while open, like the popover's children: the panel's draft is\n born with it, so every open starts again from the committed value. */}\n {sheetOpen && children(closeSheet, sheetFrame)}\n </>\n ) : (\n <Popover\n width={width}\n panelId={panelId}\n // Named for what it is. Unnamed, it fell back to `popover.panel`, and a date\n // field announced its calendar as \"Popover\" — in English, in every language.\n labels={{ panel: panelLabel }}\n dir={dir}\n trigger={fieldTrigger}\n >\n {(close) => children(close, popoverFrame)}\n </Popover>\n )}\n {showClear ? (\n <button\n type=\"button\"\n aria-label={clearLabel}\n onClick={onClear}\n className=\"absolute end-1.5 top-1/2 -translate-y-1/2 rounded p-1 text-[var(--text-placeholder)] hover:text-[var(--text-secondary)]\"\n >\n <X className=\"size-4\" />\n </button>\n ) : (\n <Calendar\n aria-hidden\n className=\"pointer-events-none absolute end-2.5 top-1/2 size-4 -translate-y-1/2 text-[var(--text-placeholder)]\"\n />\n )}\n </div>\n );\n}\n\n/** One ‹ / › day-step button, sized to sit flush beside the field. */\nfunction StepButton({\n icon: Icon,\n label,\n disabled,\n onClick,\n mirror,\n className,\n}: {\n icon: ComponentType<{ className?: string }>;\n label: string;\n disabled: boolean;\n onClick: () => void;\n /** Flip the icon in RTL — for the ‹ › day steps, whose \"previous\" points to the\n * reading start. Not for the today icon, which has no direction. */\n mirror?: boolean;\n /** The button's corners in the joined group (see DatePicker). */\n className?: string;\n}) {\n return (\n <Tooltip label={label} portal>\n <button\n type=\"button\"\n aria-label={label}\n disabled={disabled}\n onClick={onClick}\n className={cn(\n FIELD_BASE,\n // FIELD_BASE is `block w-full` for text inputs; a step button is neither, and\n // px-3 would make it wider than it needs to be. twMerge lets these win.\n \"relative flex h-full w-10 shrink-0 items-center justify-center px-0 text-[var(--text-muted)]\",\n \"hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]\",\n // Raised while hovered or focused, so the shared border and the focus ring are\n // drawn on top of the neighbour they overlap by a pixel.\n \"hover:z-10 focus-visible:z-10\",\n // Dimmed ICON, not a dimmed box: at 40% opacity the whole button — border\n // included — faded out of the group and read as a broken, detached control.\n \"disabled:cursor-not-allowed disabled:text-[var(--text-placeholder)] disabled:opacity-60 disabled:hover:bg-[var(--bg-surface)]\",\n className,\n )}\n >\n <Icon className={cn(\"size-4\", mirror && \"rtl:-scale-x-100\")} />\n </button>\n </Tooltip>\n );\n}\n\n// ── Month jump (`monthJump`, kastlan) ─────────────────────────────────────\n\n/** A \"YYYY-MM\" key as a count of months, and back: month arithmetic with no `Date`\n * rollover to step around. */\nconst monthIndex = (key: string) => Number(key.slice(0, 4)) * 12 + Number(key.slice(5, 7)) - 1;\nconst keyOfIndex = (index: number) =>\n `${String(Math.floor(index / 12)).padStart(4, \"0\")}-${pad((index % 12) + 1)}`;\nconst shiftKey = (key: string, delta: number) => keyOfIndex(monthIndex(key) + delta);\n/** The 1st of a \"YYYY-MM\" month, for `Intl` to name. */\nconst firstOfKey = (key: string) => new Date(Number(key.slice(0, 4)), Number(key.slice(5, 7)) - 1, 1);\n\n/** Upper-case the first letter only, as `MonthPicker` does: \"julio de 2026\" heads a\n * panel as \"Julio de 2026\" — not \"Julio De 2026\", which CSS `capitalize` would write. */\nconst upperFirst = (text: string, locale: string | undefined) =>\n text.charAt(0).toLocaleUpperCase(locale) + text.slice(1);\n\n/** Months per row of the month grid — `MonthPicker`'s 4×3. */\nconst MONTH_COLUMNS = 3;\n\n/**\n * The month `MiniCalendar` opens on — the selection, or today, pulled inside the\n * bounds — worked out the way it does, so the caption drawn over a calendar that was\n * given no `month` names the month that calendar is showing.\n */\nfunction openingMonth(from: string, to: string, lo: Date | null, hi: Date | null): string {\n const start = parseIsoDate(from) ?? parseIsoDate(to) ?? new Date();\n if (lo && start < lo) return monthKey(lo);\n if (hi && start > hi) return monthKey(hi);\n return monthKey(start);\n}\n\ntype PanelCalendarProps = Pick<\n MiniCalendarProps,\n \"mode\" | \"from\" | \"to\" | \"locale\" | \"min\" | \"max\" | \"labels\" | \"onSelect\" | \"focusOnOpen\"\n> & {\n /** See {@link DatePickerBaseProps.monthJump}. */\n monthJump?: boolean;\n};\n\n/** The panel's calendar: the plain {@link MiniCalendar}, exactly as before — or, with\n * `monthJump`, the same calendar under a caption that opens a month grid. */\nfunction PanelCalendar({ monthJump, ...calendar }: PanelCalendarProps) {\n return monthJump ? <JumpCalendar {...calendar} /> : <MiniCalendar {...calendar} />;\n}\n\n/** How the day grid is mounted: `key` remounts it, `month` is the month it follows\n * (left out until the panel first moves, so it opens exactly where it always has), and\n * `focus` is its `focusOnOpen`. */\ninterface DayGridMount {\n key: number;\n month?: string;\n focus: boolean;\n}\n\n/**\n * {@link MiniCalendar} with its navigation drawn here — ‹ caption › — so the caption can\n * be a button that swaps the days for a month grid ({@link DatePickerBaseProps.monthJump}).\n *\n * Built on the calendar's own `hideNavigation` / `month` / `onMonthChange`, not a fork\n * of it: the days, their keys, the range band and every announcement stay the\n * calendar's. What this keeps is `shown`, the month on screen, for the caption — fed by\n * the arrows here, by the calendar's `onMonthChange` (PageUp, arrowing off the edge),\n * and by the value, whose changes the calendar follows WITHOUT reporting them (a preset\n * in `commit=\"apply\"` moves the grid to its start). That last one is mirrored below with\n * the calendar's own rule, and when it lands in another month the day grid is remounted\n * there rather than steered by `month`: a `month` that changes in the same render as the\n * value would win over the value's day, and the grid's tab stop would land on the\n * month's same-numbered day instead of the preset's start.\n *\n * The month grid is `MonthPicker`'s, redrawn here because that one is internal to its\n * file; same roles, keys and words (`monthPicker` labels).\n */\nfunction JumpCalendar({\n from = \"\",\n to = \"\",\n mode = \"range\",\n locale,\n min,\n max,\n labels,\n onSelect,\n focusOnOpen,\n}: Omit<PanelCalendarProps, \"monthJump\">) {\n // The month arrows say what they said when the calendar drew them; the month grid\n // says what `MonthPicker`'s says.\n const calendarText = useKitLabels(\"miniCalendar\", DEFAULT_MINI_CALENDAR_LABELS, labels);\n const monthText = useKitLabels(\"monthPicker\", DEFAULT_MONTH_PICKER_LABELS);\n const id = useId();\n const captionId = `${id}-caption`;\n const hintId = `${id}-hint`;\n const captionRef = useRef<HTMLButtonElement>(null);\n const monthsRef = useRef<HTMLDivElement>(null);\n\n const lo = min ? parseIsoDate(min) : null;\n const hi = max ? parseIsoDate(max) : null;\n const minKey = lo ? monthKey(lo) : null;\n const maxKey = hi ? monthKey(hi) : null;\n const outOfBounds = (key: string) => Boolean((minKey && key < minKey) || (maxKey && key > maxKey));\n const clampKey = (key: string) => (minKey && key < minKey ? minKey : maxKey && key > maxKey ? maxKey : key);\n\n const [view, setView] = useState<\"days\" | \"months\">(\"days\");\n const [shown, setShown] = useState(() => openingMonth(from, to, lo, hi));\n const [dayGrid, setDayGrid] = useState<DayGridMount>(() => ({ key: 0, focus: Boolean(focusOnOpen) }));\n // The month holding the month grid's tab stop — and with it the year on show.\n const [active, setActive] = useState(shown);\n\n // The value moved from outside the day grid (a preset, a reset): follow the end that\n // changed, by the calendar's own rule, and remount the days there — see above. Also\n // what takes an open month grid back to the days when a preset is picked beside it.\n const [lastValue, setLastValue] = useState({ from, to });\n if (lastValue.from !== from || lastValue.to !== to) {\n const changed = lastValue.from !== from ? from : to;\n setLastValue({ from, to });\n const next = parseIsoDate(changed) ?? parseIsoDate(from) ?? parseIsoDate(to);\n const key = next ? monthKey(next) : null;\n if (key && (key !== shown || view === \"months\")) {\n setShown(key);\n setView(\"days\");\n setDayGrid((d) => ({ key: d.key + 1, month: key, focus: false }));\n }\n }\n\n // A month cell that has to take focus once it exists: opening the grid, and arrowing\n // across a year's edge, which relabels the same twelve buttons.\n const pendingFocus = useRef<string | null>(null);\n useEffect(() => {\n const key = pendingFocus.current;\n if (!key) return;\n pendingFocus.current = null;\n monthsRef.current?.querySelector<HTMLElement>(`[data-month=\"${key}\"]`)?.focus();\n });\n\n const showMonth = (key: string) => {\n setShown(key);\n // Follows by `month`: the calendar keeps the day of the month (clamped), and the\n // focus stays on the arrow being pressed.\n setDayGrid((d) => ({ ...d, month: key }));\n };\n const openMonths = () => {\n const start = clampKey(shown);\n pendingFocus.current = start;\n setActive(start);\n setView(\"months\");\n };\n const closeMonths = (focusCaption: boolean) => {\n setView(\"days\");\n setDayGrid((d) => ({ ...d, month: shown, focus: false }));\n if (focusCaption) captionRef.current?.focus();\n };\n const pick = (key: string) => {\n setShown(key);\n setView(\"days\");\n // Picked to go there: the focus goes with it, onto the days.\n setDayGrid((d) => ({ key: d.key + 1, month: key, focus: true }));\n };\n const moveActive = (key: string, focus: boolean) => {\n if (focus) pendingFocus.current = key;\n setActive(key);\n };\n\n const inDays = view === \"days\";\n // Escape in the month grid is \"back\", not \"close\": the month grid is a step INSIDE the\n // panel, and losing the half-made range to a key meant to undo that step would be the\n // worse surprise. Stopped here, so neither the popover nor the phone sheet sees it.\n const escapeToDays = (e: KeyboardEvent<HTMLElement>) => {\n if (e.key !== \"Escape\" || inDays) return;\n e.preventDefault();\n e.stopPropagation();\n closeMonths(true);\n };\n\n const onMonthKeyDown = (e: KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === \"Escape\") return escapeToDays(e);\n // Left and right are visual: the grid runs the other way in a right-to-left panel.\n const step = horizontalStep(e.key, e.currentTarget);\n const col = (monthIndex(active) % 12) % MONTH_COLUMNS;\n let delta: number;\n switch (step ? \"horizontal\" : e.key) {\n case \"horizontal\":\n delta = step;\n break;\n case \"ArrowUp\":\n delta = -MONTH_COLUMNS;\n break;\n case \"ArrowDown\":\n delta = MONTH_COLUMNS;\n break;\n case \"Home\":\n delta = -col;\n break;\n case \"End\":\n delta = MONTH_COLUMNS - 1 - col;\n break;\n case \"PageUp\":\n delta = -12;\n break;\n case \"PageDown\":\n delta = 12;\n break;\n default:\n return;\n }\n // Before the no-op check: these keys also scroll the panel.\n e.preventDefault();\n if (delta !== 0) moveActive(shiftKey(active, delta), true);\n };\n\n const year = Number(active.slice(0, 4));\n // A year arrow is dead once every month of the year it leads to is out of bounds.\n const prevYearBlocked = Boolean(minKey && year - 1 < Number(minKey.slice(0, 4)));\n const nextYearBlocked = Boolean(maxKey && year + 1 > Number(maxKey.slice(0, 4)));\n const prevBlocked = !inDays && prevYearBlocked;\n const nextBlocked = !inDays && nextYearBlocked;\n\n const longName = useMemo(() => new Intl.DateTimeFormat(locale, { month: \"long\", year: \"numeric\" }), [locale]);\n const shortNames = useMemo(() => {\n const fmt = new Intl.DateTimeFormat(locale, { month: \"short\" });\n return Array.from({ length: 12 }, (_, i) => upperFirst(fmt.format(new Date(2000, i, 1)), locale));\n }, [locale]);\n const caption = inDays\n ? upperFirst(longName.format(firstOfKey(shown)), locale)\n : new Intl.DateTimeFormat(locale, { year: \"numeric\" }).format(new Date(year, 0, 1));\n\n // The selection, as months: both ends, and the months between (a range's band).\n const fromDate = parseIsoDate(from);\n const toDate = parseIsoDate(to);\n const fromKey = fromDate ? monthKey(fromDate) : null;\n const toKey = toDate ? monthKey(toDate) : null;\n const low = fromKey ?? toKey;\n const high = toKey ?? fromKey;\n const currentKey = monthKey(new Date());\n\n // Aria-disabled rather than disabled, like the day cells: a focused button that\n // turns `disabled` drops the focus to <body>, mid-panel.\n const arrow =\n \"rounded p-1 text-[var(--text-muted)] hover:bg-[var(--bg-hover)] aria-disabled:cursor-not-allowed aria-disabled:opacity-30 aria-disabled:hover:bg-transparent\";\n\n return (\n <div className=\"select-none\">\n {/* The calendar's own row, redrawn: the same arrows in the same places. Each\n button stays mounted across the two views and changes what it steps, so a\n press never pulls the element out from under the pointer or the focus. */}\n <div className=\"flex items-center justify-between gap-1 pb-1\">\n <button\n type=\"button\"\n aria-label={inDays ? calendarText.previousMonth : monthText.previousYear}\n aria-disabled={prevBlocked || undefined}\n onClick={() => {\n if (inDays) showMonth(shiftKey(shown, -1));\n else if (!prevBlocked) moveActive(shiftKey(active, -12), false);\n }}\n onKeyDown={escapeToDays}\n className={arrow}\n >\n {/* Mirrored in RTL: \"previous\" points to the reading START, which is right. */}\n <ChevronLeft className=\"size-4 rtl:-scale-x-100\" aria-hidden />\n </button>\n <button\n ref={captionRef}\n type=\"button\"\n aria-expanded={!inDays}\n aria-describedby={inDays ? hintId : undefined}\n onClick={inDays ? openMonths : () => closeMonths(false)}\n onKeyDown={escapeToDays}\n className=\"inline-flex min-w-0 items-center gap-1 rounded px-1.5 py-0.5 text-xs font-medium text-[var(--text-secondary)] hover:bg-[var(--bg-hover)]\"\n >\n {/* The name is the text, so it says what is on screen; `aria-live` because\n pressing an arrow leaves focus ON the arrow, and this is the only thing\n that says where it went. It also names the month grid. */}\n <span id={captionId} aria-live=\"polite\" className=\"truncate\">\n {caption}\n </span>\n <ChevronDown\n aria-hidden\n className={cn(\"size-3.5 shrink-0 text-[var(--text-muted)]\", !inDays && \"rotate-180\")}\n />\n </button>\n <button\n type=\"button\"\n aria-label={inDays ? calendarText.nextMonth : monthText.nextYear}\n aria-disabled={nextBlocked || undefined}\n onClick={() => {\n if (inDays) showMonth(shiftKey(shown, 1));\n else if (!nextBlocked) moveActive(shiftKey(active, 12), false);\n }}\n onKeyDown={escapeToDays}\n className={arrow}\n >\n <ChevronRight className=\"size-4 rtl:-scale-x-100\" aria-hidden />\n </button>\n </div>\n {/* The caption's description — read through the reference, never on its own. */}\n <span id={hintId} hidden>\n {monthText.panel}\n </span>\n {inDays ? (\n <MiniCalendar\n key={dayGrid.key}\n mode={mode}\n from={from}\n to={to}\n locale={locale}\n min={min}\n max={max}\n labels={labels}\n onSelect={onSelect}\n focusOnOpen={dayGrid.focus}\n hideNavigation\n month={dayGrid.month}\n onMonthChange={showMonth}\n />\n ) : (\n <div ref={monthsRef} role=\"grid\" aria-labelledby={captionId} className=\"grid gap-1\">\n {Array.from({ length: 12 / MONTH_COLUMNS }, (_, row) => (\n <div key={row} role=\"row\" className=\"grid grid-cols-3 gap-1\">\n {Array.from({ length: MONTH_COLUMNS }, (_, c) => {\n const index = row * MONTH_COLUMNS + c;\n const key = keyOfIndex(year * 12 + index);\n const disabled = outOfBounds(key);\n const selected = low !== null && high !== null && key >= low && key <= high;\n const end = key === fromKey || key === toKey;\n const current = key === currentKey;\n return (\n <button\n key={c}\n type=\"button\"\n role=\"gridcell\"\n data-month={key}\n tabIndex={key === active ? 0 : -1}\n aria-label={monthText.month(longName.format(firstOfKey(key)))}\n aria-selected={selected}\n aria-disabled={disabled || undefined}\n aria-current={current ? \"date\" : undefined}\n onClick={() => {\n // The guard `disabled` would have been: out-of-bounds months stay\n // focusable, so the roving tab stop never falls into a hole.\n if (!disabled) pick(key);\n }}\n onKeyDown={onMonthKeyDown}\n className={cn(\n // 44px rows: the touch-target size, and four of them stand as tall\n // as a month of days, so the panel does not jump as the views swap.\n \"h-11 rounded px-1 text-sm tabular-nums transition-colors\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset\",\n // A --brand ring on a --brand fill exists only in the DOM.\n end && !disabled ? \"focus-visible:ring-[var(--brand-contrast)]\" : \"focus-visible:ring-[var(--brand)]\",\n disabled\n ? \"cursor-not-allowed text-[var(--text-placeholder)]\"\n : end\n ? \"bg-[var(--brand)] text-[var(--brand-contrast)] hover:bg-[var(--brand-hover)]\"\n : selected\n ? \"bg-[var(--brand-bg)] font-medium text-[var(--brand-muted)] hover:bg-[var(--brand-bg-hover)]\"\n : \"text-[var(--text-primary)] hover:bg-[var(--bg-hover)]\",\n current && !selected && !disabled && \"ring-1 ring-inset ring-[var(--border-strong)]\",\n )}\n >\n {shortNames[index]}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport interface DatePickerProps extends DatePickerBaseProps {\n /** ISO \"YYYY-MM-DD\", or \"\" for empty. */\n value: string;\n onChange: (iso: string) => void;\n /**\n * Flank the field with ‹ › buttons that move the value one day. For fields whose\n * typical edit is a day or two — opening a calendar to go \"yesterday\" is three\n * interactions for what should be one.\n *\n * Off by default: on a filter or a far-away date the buttons are dead weight, and\n * they cost ~5rem of width that a narrow layout may not have.\n */\n step?: boolean;\n /** Accessible names for the step buttons. Optional: they resolve from\n * `datePicker.previousDay` / `nextDay` in `<UiKitProvider labels>`, then from the\n * English defaults. This prop wins over both, for the one field that says it\n * differently. */\n stepLabels?: { prev: string; next: string };\n /**\n * Today's date as ISO \"YYYY-MM-DD\". Adds a jump-to-today button beside the field,\n * disabled once the value already is that day.\n *\n * Passed in rather than read from a clock here: a component that decides what \"today\"\n * is cannot be tested at a fixed date, and the consumer already has one definition of\n * it. The button is always RENDERED — going flat instead of disappearing — because a\n * control that comes and goes with the value reflows whatever sits beside it, which\n * is the report this exists to answer.\n */\n today?: string;\n /** Accessible name for the today button; wins over `datePicker.today`. */\n todayLabel?: string;\n}\n\n/**\n * The picker's own label props, folded into one `datePicker` override so they sit at\n * the top of the same precedence chain as everything else: prop > provider > English.\n * They predate the provider and stay public API, which is why they are not simply\n * replaced by a `labels` prop.\n */\nfunction useDatePickerLabels(\n clearLabel: string | undefined,\n stepLabels: { prev: string; next: string } | undefined,\n todayLabel: string | undefined,\n): DatePickerLabels {\n const prev = stepLabels?.prev;\n const next = stepLabels?.next;\n const fromProps = useMemo(() => {\n const out: Partial<DatePickerLabels> = {};\n if (clearLabel !== undefined) out.clear = clearLabel;\n if (prev !== undefined) out.previousDay = prev;\n if (next !== undefined) out.nextDay = next;\n if (todayLabel !== undefined) out.today = todayLabel;\n return out;\n }, [clearLabel, prev, next, todayLabel]);\n return useKitLabels(\"datePicker\", DEFAULT_DATE_PICKER_LABELS, fromProps);\n}\n\n/**\n * The caller's `hint` when it has no label line to ride: at the end of the field,\n * outside the box, as an unlabelled {@link Select} places its FieldHint (0.15.5 P8).\n */\nfunction withEndHint(field: ReactNode, hint: ReactNode): ReactNode {\n if (hint === undefined) return field;\n return (\n <div className=\"flex items-center gap-1.5\">\n <div className=\"min-w-0 flex-1\">{field}</div>\n <span className=\"flex shrink-0 items-center\">{hint}</span>\n </div>\n );\n}\n\n/** Single-date picker: a field showing the formatted date, opening a calendar. */\nexport function DatePicker(props: DatePickerProps) {\n const {\n value,\n onChange,\n locale: localeProp,\n placeholder,\n min,\n max,\n formatOptions,\n formatValue,\n step,\n stepLabels,\n today,\n todayLabel,\n className,\n calendarLabels,\n monthJump,\n label,\n clearable,\n clearLabel,\n disabled,\n invalid,\n hint,\n error,\n // Named so it reaches the field (and so its trigger) when a step/today row is the\n // root and takes the rest of the caller's props.\n \"aria-required\": ariaRequired,\n ...rest\n } = props;\n const locale = useKitLocale(localeProp);\n const text = useDatePickerLabels(clearLabel, stepLabels, todayLabel);\n const render = useTriggerFormat(\"datePicker\", locale, formatOptions, formatValue);\n // keksdose K4: the caption and the error under the field, attached to the TRIGGER\n // (the element a screen reader is on) after any description of the caller's own.\n const messages = useFieldMessages({\n hint,\n error,\n invalid,\n describedBy: rest[\"aria-describedby\"],\n ariaInvalid: rest[\"aria-invalid\"],\n });\n // With a step or a today button beside it the field is no longer the outermost\n // element — the flex row at the bottom is, and a caller's attributes belong on\n // whichever of the two is actually the root. Naming the shared props rather than\n // letting `...rest` carry them is what makes that choice possible.\n const wrapped = Boolean(step || today);\n const [triggerAria, wrapperRest] = splitTriggerAria(rest);\n const field = (\n <DateField\n // With step/today buttons the flex row is the root and takes the caller's props —\n // except the ones that name the field, which still belong on its trigger.\n {...(wrapped ? triggerAria : rest)}\n aria-describedby={messages.describedBy}\n aria-required={ariaRequired}\n label={label}\n labelHint={messages.labelHint}\n clearable={clearable}\n clearLabel={text.clear}\n panelLabel={text.panel}\n disabled={disabled}\n invalid={messages.isInvalid}\n className={step ? \"min-w-0 flex-1\" : className}\n hasValue={Boolean(value)}\n triggerText={value ? render(value) : (placeholder ?? \"\")}\n onClear={() => onChange(\"\")}\n >\n {(close) => (\n <PanelCalendar\n monthJump={monthJump}\n mode=\"single\"\n focusOnOpen\n from={value}\n to={value}\n locale={locale}\n min={min}\n max={max}\n labels={calendarLabels}\n onSelect={(iso) => {\n onChange(iso);\n close();\n }}\n />\n )}\n </DateField>\n );\n // Passed at all — even as `undefined` — the field keeps its box; see FieldBox.\n const reserve = \"error\" in props || \"hint\" in props;\n const endHint = label === undefined ? messages.labelHint : undefined;\n if (!wrapped) {\n return (\n <FieldBox below={messages.below} reserve={reserve}>\n {withEndHint(field, endHint)}\n </FieldBox>\n );\n }\n\n // An empty field has nothing to step from, so both buttons are dead until a date\n // is picked. Bounds are compared as strings: \"YYYY-MM-DD\" sorts chronologically.\n const target = (days: number) => (value ? addDaysIso(value, days) : \"\");\n const outOfBounds = (iso: string) => Boolean((min && iso < min) || (max && iso > max));\n const blocked = (days: number) => {\n const next = target(days);\n if (!next || disabled) return true;\n return outOfBounds(next);\n };\n const row = (\n // ONE joined control, not three boxes in a row: the buttons and the field share\n // their borders (each piece overlaps the previous by a pixel) and only the outer\n // ends are rounded — the button-group shape, so ‹ date › reads as one field with\n // controls rather than a field with loose buttons parked beside it, which is what\n // the `gap-1` version looked like.\n //\n // items-stretch, not items-center: the buttons match the field's height, which\n // varies with whether it carries a floating label. The field's trigger is nested\n // inside DateField, so its inner corners are squared from here.\n <div\n {...wrapperRest}\n className={cn(\n \"flex items-stretch [&>*:not(:first-child)]:-ms-px\",\n step && \"[&_[role=combobox]]:rounded-s-none\",\n (step || today) && \"[&_[role=combobox]]:rounded-e-none\",\n \"[&_[role=combobox]:focus-visible]:relative [&_[role=combobox]:focus-visible]:z-10\",\n className,\n )}\n >\n {step && (\n <StepButton\n icon={ChevronLeft}\n mirror\n label={text.previousDay}\n disabled={blocked(-1)}\n onClick={() => onChange(target(-1))}\n className=\"rounded-e-none\"\n />\n )}\n {field}\n {step && (\n <StepButton\n icon={ChevronRight}\n mirror\n label={text.nextDay}\n disabled={blocked(1)}\n onClick={() => onChange(target(1))}\n className={cn(\"rounded-s-none\", today && \"rounded-e-none\")}\n />\n )}\n {today && (\n <StepButton\n icon={CalendarClock}\n label={text.today}\n disabled={Boolean(disabled) || value === today || outOfBounds(today)}\n onClick={() => onChange(today)}\n className=\"rounded-s-none\"\n />\n )}\n </div>\n );\n // Under the whole row, not under the field between the buttons: the message is about\n // the date, and the steppers are part of how it is entered.\n return (\n <FieldBox below={messages.below} reserve={reserve}>\n {withEndHint(row, endHint)}\n </FieldBox>\n );\n}\n\nexport interface DateRangePickerPreset {\n /**\n * The preset's identity, independent of its dates — `\"last_3_months\"`, not two ISO\n * strings. Optional for the 0.x callers that pass anonymous pairs.\n *\n * keksdose's report range field (report-range-field.tsx, \"Why this is app-level\")\n * is why it exists: its URL stores `?preset=last_3_months` and recomputes the dates\n * from today on every read, so a preset that is only its dates froze \"last 3 months\"\n * at whatever day the panel rendered, and two presets that happen to cover the same\n * days (this week and today, on a Monday) could not be told apart. With an `id` the\n * choice is reported to `onChange` and shown as selected for as long as the range\n * still matches it. Recompute `from`/`to` on every render (`calendarMonthPresets()`,\n * `dateRangePresets()`) and pass the key as the id.\n */\n id?: string;\n label: ReactNode;\n from: string;\n to: string;\n}\n\n/**\n * When the picker hands its range to `onChange`.\n *\n * - `\"immediate\"` (the default, and all of 0.7): a preset click commits and closes,\n * and each calendar click is reported as it happens — the half-made `(from, \"\")`\n * included. The right shape for quick ranges on a filter, where one click is one\n * answer.\n * - `\"apply\"`: the panel works on a DRAFT. A preset ARMS the calendar instead of\n * committing, days can then be nudged (\"last 3 months, but end it last Friday\"),\n * and nothing reaches `onChange` until Apply — which stays disabled until both ends\n * exist. Cancel, Escape and an outside click discard the draft, and every open\n * starts again from the committed value. keksdose's report range asked for exactly\n * this: each commit there re-runs a whole page of charts, so an intermediate state\n * must never escape the panel.\n */\nexport type DateRangeCommit = \"immediate\" | \"apply\";\n\nexport interface DateRangePickerProps extends DatePickerBaseProps {\n from: string;\n to: string;\n /** The committed range. `presetId` is the chosen preset's `id` when a preset\n * produced it (and the range was not nudged since), `undefined` for days picked by\n * hand. A 0.x two-argument handler simply ignores it. */\n onChange: (from: string, to: string, presetId?: string) => void;\n /** Separator between the two formatted dates in the trigger (default \" – \"). */\n separator?: string;\n /** Optional named ranges rendered as a start-side column (last month / YTD / …). */\n presets?: DateRangePickerPreset[];\n /**\n * The active preset's `id`, controlled — e.g. straight from keksdose's `?preset=`.\n * `null` (or an id no preset has, such as `\"custom\"`) says \"no preset\": nothing in\n * the column is marked even if a preset happens to cover the same days. A preset is\n * marked only while its dates still equal `from`/`to`.\n *\n * Leave it `undefined` and the picker remembers the last preset it committed\n * itself; with no memory at all it falls back to the first preset whose dates match.\n */\n preset?: string | null;\n /** See {@link DateRangeCommit}. Default `\"immediate\"`. */\n commit?: DateRangeCommit;\n /**\n * Draw the trigger yourself. keksdose's report range field\n * (reports/report-range-field.tsx, \"Why this is app-level\") is why: its trigger\n * NAMES the active preset (\"Last 3 months\", the window as a muted suffix) where the\n * kit's shows two dates, sits inside a Tooltip, and is squared off on one side so the\n * granularity control can join it flush at the field's 42px.\n *\n * Spread `props.triggerProps` onto a `<button>` whole and put `props.valueProps` on\n * the element that shows the value — see {@link DateTriggerAttributes} for what each\n * part is for. The field around it (label, hidden name twin, clear/calendar glyph,\n * popover or phone sheet) is unchanged.\n */\n renderTrigger?: (props: DateRangeTriggerRenderProps) => ReactNode;\n /**\n * Below {@link PHONE_QUERY} the panel opens as a full-screen sheet (`FullBleedDialog`)\n * with presets in a grid above the calendar and Apply pinned in the footer, instead\n * of the 440px popover that hung off a 360px screen (keksdose's report range field,\n * which hand-rolled exactly this). This is its `backCloses`: on by default, so Back\n * dismisses the sheet; off for a caller whose commit rewrites the URL with\n * `replaceState` in the same tick — keksdose's `?preset/from/to` — where the router\n * would overwrite the sheet's history marker.\n */\n sheetBackCloses?: boolean;\n /**\n * With `commit=\"apply\"`: a line above the calendar saying what Apply would commit —\n * keksdose's report range writes \"Custom · 1 Mar 2026 – …\" there\n * (report-range-field.tsx), the one place a preset armed and then nudged by a day is\n * seen to have become \"Custom\", and a half-made range to still lack its end. Called\n * with the DRAFT on every change, in the popover and the phone sheet alike; `preset`\n * is the preset the column marks for it (none once a day was picked by hand), `to` is\n * `\"\"` until the second click. The line is a polite live region, so a screen reader\n * hears the draft change as the calendar is used.\n *\n * Ignored in `\"immediate\"` mode, where there is no draft: every click is the value.\n */\n renderDraftSummary?: (draft: DateRangeDraftSummary) => ReactNode;\n}\n\n/** What {@link DateRangePickerProps.renderDraftSummary} is called with. */\nexport interface DateRangeDraftSummary {\n from: string;\n to: string;\n /** The preset the column marks for the draft, if any. */\n preset: DateRangePickerPreset | undefined;\n}\n\n/** What {@link DateRangePickerProps.renderTrigger} is called with. */\nexport interface DateRangeTriggerRenderProps {\n /** Spread onto the `<button>`, whole. Merge your class after `triggerProps.className`. */\n triggerProps: DateTriggerAttributes;\n /** Put on the element showing the value: the trigger is named \"label, value\" by\n * reference to this id. Without it the name is the label alone. */\n valueProps: { id: string };\n open: boolean;\n from: string;\n to: string;\n /** The preset the column marks for the committed range, if any — the one to name. */\n preset: DateRangePickerPreset | undefined;\n /** What the kit's own trigger would show: the formatted range, or the placeholder. */\n text: string;\n}\n\ninterface RangeDraft {\n from: string;\n to: string;\n presetId: string | undefined;\n}\n\n/** Which preset (by index) the column marks, or -1. See `DateRangePickerProps.preset`. */\nfunction markedPreset(\n presets: readonly DateRangePickerPreset[],\n from: string,\n to: string,\n activeId: string | null | undefined,\n controlled: boolean,\n): number {\n const matches = (p: DateRangePickerPreset) => p.from === from && p.to === to;\n if (activeId != null) {\n const byId = presets.findIndex((p) => p.id === activeId && matches(p));\n if (byId !== -1 || controlled) return byId;\n } else if (controlled) {\n return -1;\n }\n return presets.findIndex(matches);\n}\n\n/**\n * The preset column: a named group of toggle buttons, `aria-pressed` on the marked one.\n * Pressed-state rather than colour alone, because \"which range is this\" is the one\n * thing a screen-reader user opening the panel needs to hear.\n */\nfunction PresetColumn({\n presets,\n marked,\n label,\n onPick,\n sheet,\n}: {\n presets: readonly DateRangePickerPreset[];\n marked: number;\n label: string;\n onPick: (p: DateRangePickerPreset) => void;\n /** The phone sheet's shape: a two-column grid ABOVE the calendar with 44px rows —\n * a 128px column beside a month grid leaves the grid ~200px on a 360px screen. */\n sheet?: boolean;\n}) {\n return (\n // `border-e`/`pe`, not `-r`: the preset column is on the START side, and the rule\n // between it and the calendar has to follow it in a right-to-left UI.\n <div\n role=\"group\"\n aria-label={label}\n className={\n sheet\n ? \"grid grid-cols-2 gap-0.5\"\n : \"flex w-32 shrink-0 flex-col gap-0.5 border-e border-[var(--border)] pe-2\"\n }\n >\n {presets.map((p, i) => {\n const selected = i === marked;\n return (\n <button\n key={p.id ?? i}\n type=\"button\"\n aria-pressed={selected}\n data-preset={p.id}\n onClick={() => onPick(p)}\n className={cn(\n \"rounded px-2 text-start\",\n // 44px on a phone: the kit's touch-target size (`SHEET_ROW_CLASS`).\n sheet ? \"min-h-11 py-2 text-sm\" : \"py-1.5 text-xs\",\n selected\n ? \"bg-[var(--bg-active)] font-medium text-[var(--text-primary)]\"\n : \"text-[var(--text-secondary)] hover:bg-[var(--bg-hover)]\",\n )}\n >\n {p.label}\n </button>\n );\n })}\n </div>\n );\n}\n\n/**\n * The panel's body. A component of its own so `commit=\"apply\"` can keep its draft in\n * state that is born when the panel opens: `Popover` unmounts its children on close,\n * so the draft is re-seeded from the committed value on every open, and an abandoned\n * half-selection cannot survive into the next one.\n */\nfunction RangePanel({\n from,\n to,\n presets,\n activeId,\n controlled,\n commit,\n close,\n frame,\n sheet,\n commitRange,\n calendarProps,\n labels,\n renderDraftSummary,\n}: {\n from: string;\n to: string;\n presets: readonly DateRangePickerPreset[] | undefined;\n activeId: string | null | undefined;\n controlled: boolean;\n commit: DateRangeCommit;\n close: () => void;\n /** Where the body and the Apply row go — see `PanelFrame`. */\n frame: PanelFrame;\n sheet: boolean;\n commitRange: (from: string, to: string, presetId: string | undefined) => void;\n calendarProps: Pick<PanelCalendarProps, \"locale\" | \"min\" | \"max\" | \"labels\" | \"monthJump\">;\n labels: DatePickerLabels;\n renderDraftSummary?: DateRangePickerProps[\"renderDraftSummary\"];\n}) {\n const drafting = commit === \"apply\";\n const [draft, setDraft] = useState<RangeDraft>({\n from,\n to,\n presetId: activeId ?? undefined,\n });\n // What the panel SHOWS: the draft while drafting, the committed value otherwise.\n const shown = drafting ? draft : { from, to, presetId: activeId ?? undefined };\n const marked = presets\n ? markedPreset(presets, shown.from, shown.to, drafting ? draft.presetId : activeId, drafting || controlled)\n : -1;\n const complete = Boolean(draft.from && draft.to);\n\n const summary =\n drafting && renderDraftSummary\n ? renderDraftSummary({ from: draft.from, to: draft.to, preset: presets?.[marked] })\n : null;\n\n const picker = (\n <PanelCalendar\n {...calendarProps}\n focusOnOpen\n from={shown.from}\n to={shown.to}\n onSelect={(f, t) => {\n if (drafting) {\n setDraft({ from: f, to: t, presetId: undefined });\n return;\n }\n commitRange(f, t, undefined);\n // Two-click range: only dismiss once both ends are chosen.\n if (f && t) close();\n }}\n />\n );\n // The summary heads the CALENDAR's column, not the panel: beside a preset column it\n // reads as the calendar's caption, and in the sheet it sits between the presets and\n // the month it describes. `null` renders no line — a caller may opt out per draft —\n // but keeps the wrapper, so the calendar is not remounted (and its month and focus\n // lost) when a line comes or goes.\n const calendar =\n drafting && renderDraftSummary ? (\n <div className=\"flex flex-col gap-2\">\n {summary != null && (\n <div aria-live=\"polite\" data-draft-summary=\"\" className=\"text-xs text-[var(--text-muted)]\">\n {summary}\n </div>\n )}\n {picker}\n </div>\n ) : (\n picker\n );\n\n const body =\n presets && presets.length > 0 ? (\n <div className={sheet ? \"flex flex-col gap-3\" : \"flex gap-3\"}>\n <PresetColumn\n sheet={sheet}\n presets={presets}\n marked={marked}\n label={labels.presets}\n onPick={(p) => {\n if (drafting) {\n setDraft({ from: p.from, to: p.to, presetId: p.id });\n return;\n }\n commitRange(p.from, p.to, p.id);\n close();\n }}\n />\n {/* `flex-1`: the calendar takes the rest of the panel. Content-sized it was\n seven tiny cells beside a wide preset column. */}\n <div className=\"min-w-0 flex-1\">{calendar}</div>\n </div>\n ) : (\n calendar\n );\n\n if (!drafting) return frame(body);\n return frame(\n body,\n <>\n {/* `flex-1` in the sheet: two thumb-wide halves of the footer, not two small\n buttons in its corner. */}\n <Button type=\"button\" variant=\"ghost\" className={sheet ? \"flex-1\" : undefined} onClick={close}>\n {labels.cancel}\n </Button>\n <Button\n type=\"button\"\n className={sheet ? \"flex-1\" : undefined}\n // A half-made `(from, \"\")` would commit a window with no end — a blank\n // report, one click into a two-click gesture.\n disabled={!complete}\n onClick={() => {\n if (!complete) return;\n commitRange(draft.from, draft.to, draft.presetId);\n close();\n }}\n >\n {labels.apply}\n </Button>\n </>,\n );\n}\n\n/** Two-date range picker: click a start then an end; closes once both are set.\n * With `presets`, a column of named ranges is shown beside the calendar; with\n * `commit=\"apply\"` the panel drafts and commits on Apply (see {@link DateRangeCommit}). */\nexport function DateRangePicker(props: DateRangePickerProps) {\n const {\n from,\n to,\n onChange,\n locale: localeProp,\n placeholder,\n min,\n max,\n formatOptions,\n formatValue,\n separator = \" – \",\n presets,\n preset,\n commit = \"immediate\",\n renderTrigger,\n sheetBackCloses,\n renderDraftSummary,\n calendarLabels,\n monthJump,\n label,\n clearable,\n clearLabel,\n disabled,\n invalid,\n hint,\n error,\n ...rest\n } = props;\n const locale = useKitLocale(localeProp);\n const text = useDatePickerLabels(clearLabel, undefined, undefined);\n const render = useTriggerFormat(\"dateRangePicker\", locale, formatOptions, formatValue);\n const messages = useFieldMessages({\n hint,\n error,\n invalid,\n describedBy: rest[\"aria-describedby\"],\n ariaInvalid: rest[\"aria-invalid\"],\n });\n // The preset this picker committed itself, for a caller that does not control\n // `preset`. Cleared by a hand-picked range or a clear.\n const [ownPreset, setOwnPreset] = useState<string | undefined>(undefined);\n const controlled = preset !== undefined;\n const activeId = controlled ? preset : ownPreset;\n const a = from ? render(from) : \"\";\n const b = to ? render(to) : \"\";\n const triggerText = from\n ? to\n ? `${a}${separator}${b}`\n : `${a}${separator}…`\n : (placeholder ?? \"\");\n const hasPresets = Boolean(presets && presets.length > 0);\n const sheet = useMediaQuery(PHONE_QUERY, false);\n const commitRange = (f: string, t: string, presetId: string | undefined) => {\n setOwnPreset(presetId);\n // Two arguments when no preset is involved — exactly the 0.7 call, so a caller's\n // `toHaveBeenCalledWith(from, to)` and a variadic handler see no difference.\n if (presetId === undefined) onChange(f, t);\n else onChange(f, t, presetId);\n };\n const field = (\n <DateField\n {...rest}\n aria-describedby={messages.describedBy}\n label={label}\n labelHint={messages.labelHint}\n clearable={clearable}\n clearLabel={text.clear}\n panelLabel={text.rangePanel}\n disabled={disabled}\n invalid={messages.isInvalid}\n // Widen so the preset column sits beside the calendar (default otherwise).\n width={hasPresets ? 440 : undefined}\n hasValue={Boolean(from || to)}\n triggerText={triggerText}\n onClear={() => commitRange(\"\", \"\", undefined)}\n sheet={sheet}\n sheetBackCloses={sheetBackCloses}\n renderTrigger={\n renderTrigger &&\n ((triggerProps, valueId) =>\n renderTrigger({\n triggerProps,\n valueProps: { id: valueId },\n open: triggerProps[\"aria-expanded\"],\n from,\n to,\n preset: presets?.[markedPreset(presets, from, to, activeId, controlled)],\n text: triggerText,\n }))\n }\n >\n {(close, frame) => (\n <RangePanel\n from={from}\n to={to}\n presets={presets}\n activeId={activeId}\n controlled={controlled}\n commit={commit}\n close={close}\n frame={frame}\n sheet={sheet}\n commitRange={commitRange}\n calendarProps={{ locale, min, max, labels: calendarLabels, monthJump }}\n labels={text}\n renderDraftSummary={renderDraftSummary}\n />\n )}\n </DateField>\n );\n return (\n <FieldBox below={messages.below} reserve={\"error\" in props || \"hint\" in props}>\n {withEndHint(field, label === undefined ? messages.labelHint : undefined)}\n </FieldBox>\n );\n}\n"],"mappings":"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","names":["_"]}
@@ -34,6 +34,10 @@ interface EntityComboboxProps<V extends string | number, C extends ComboClearVal
34
34
  placeholder?: string;
35
35
  searchPlaceholder?: string;
36
36
  emptyLabel?: string;
37
+ /** Offer a clear "×" in place of the chevron while something is chosen; it emits
38
+ * `clearValue`. A pointer shortcut inside the trigger, out of the tab order — the
39
+ * keyboard's way is Delete or Backspace on the CLOSED trigger (0.23, as
40
+ * {@link CountrySelect}). Not offered while `disabled` or locked. */
37
41
  clearable?: boolean;
38
42
  clearLabel?: string;
39
43
  /** The phone sheet's close button. Its own prop rather than a reuse of
@@ -42,14 +46,51 @@ interface EntityComboboxProps<V extends string | number, C extends ComboClearVal
42
46
  * so it is the one control here that MUST be in the reader's language. */
43
47
  closeLabel?: string;
44
48
  disabled?: boolean;
49
+ /**
50
+ * Why the choice cannot be changed — {@link Button}'s `disabledReason`, for a picker
51
+ * that SAVES on change (keksdose G2: the statement's account, where picking IS the
52
+ * write). The trigger stays focusable and `aria-disabled`, the list does not open,
53
+ * the clear "×" is gone and `onChange` never fires; the reason is in the kit
54
+ * {@link Tooltip} and on the trigger's `aria-describedby`. Wins over `disabled`.
55
+ * {@link CountrySelect}'s lock, which is built on the same core.
56
+ */
57
+ disabledReason?: ReactNode;
58
+ /**
59
+ * This picker COMMITS — choosing saves. Under a locked {@link WriteLockProvider} it is
60
+ * locked the `disabledReason` way with the lock's reason (which wins over its own).
61
+ * A picker inside a form with its own Save stays editable under the lock: leave this
62
+ * off there and put `commit` on the Save. No provider, or an unlocked one: no effect.
63
+ */
64
+ commit?: boolean;
45
65
  /** When set, a "create" row appears for a non-empty query with no exact match. */
46
66
  onCreate?: (query: string) => void;
47
67
  createLabel?: (query: string) => string;
68
+ /**
69
+ * Offer the create row with NOTHING typed too, worded as this — keksdose G9's
70
+ * "Create cash account", which mints a record under a default name and so needs no
71
+ * query. `onCreate` then receives `""`. Left out, the row needs a query, as before.
72
+ * See {@link InlineEntityCombobox}'s `createEmptyLabel`.
73
+ */
74
+ createEmptyLabel?: string;
75
+ /**
76
+ * The create row WRITES (it mints the record on the server) while picking does not.
77
+ * Under a locked {@link WriteLockProvider} the row stays in the list but cannot be
78
+ * taken, with the lock's reason as its second line — the picker itself stays live.
79
+ * keksdose dev#496: on the read-only demo choosing is draft state, minting is not.
80
+ */
81
+ createCommit?: boolean;
48
82
  /** Required and unanswered — {@link FIELD_INVALID}. See {@link Input}'s `invalid`. */
49
83
  invalid?: boolean;
50
84
  /** What is wrong with the value, as {@link Input}'s `error`: rendered under the
51
85
  * field, on the trigger's `aria-describedby`, and implies `invalid`. */
52
86
  error?: ReactNode;
87
+ /**
88
+ * Standing advice — {@link Combobox}'s `hint`, so the pickers read like every other
89
+ * field (keksdose K4): plain TEXT is a caption under the field on the trigger's
90
+ * `aria-describedby`, before any error; a {@link FieldHint} "?" rides the label line,
91
+ * or with no label sits at the trigger's end edge, outside the box.
92
+ */
93
+ hint?: ReactNode;
53
94
  /** Narrow `options` client-side by the query. Default `true`; `false` shows them
54
95
  * as given (a server-ranked list). */
55
96
  filter?: boolean;
@@ -68,6 +109,6 @@ interface EntityComboboxProps<V extends string | number, C extends ComboClearVal
68
109
  * the shared field/anchor/dismiss/search primitives (no cmdk/Radix). For picking
69
110
  * several entities use {@link MultiEntityCombobox}.
70
111
  */
71
- declare function EntityCombobox<V extends string | number, C extends ComboClearValue = null>({ value, onChange, clearValue, options, loadOptions, loading, label, placeholder, searchPlaceholder, emptyLabel, clearable, clearLabel, closeLabel, disabled, onCreate, createLabel, className, invalid, error, filter, minChars, debounceMs, loadErrorLabel, "aria-label": ariaLabel, id, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, "aria-required": ariaRequired, ...rest }: EntityComboboxProps<V, C>): react.JSX.Element;
112
+ declare function EntityCombobox<V extends string | number, C extends ComboClearValue = null>(props: EntityComboboxProps<V, C>): react.JSX.Element;
72
113
 
73
114
  export { ComboClearValue, ComboOption, EntityCombobox, type EntityComboboxProps };