@eifi1/ui-kit 0.52.0 → 0.54.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 (290) hide show
  1. package/README.md +52 -59
  2. package/dist/account/delete-account-setting.d.ts +3 -2
  3. package/dist/account/delete-account-setting.js.map +1 -1
  4. package/dist/auth/auth-errors.d.ts +36 -0
  5. package/dist/auth/auth-errors.js +20 -0
  6. package/dist/auth/auth-errors.js.map +1 -1
  7. package/dist/billing/cancel-subscription.d.ts +5 -14
  8. package/dist/billing/cancel-subscription.js +1 -3
  9. package/dist/billing/cancel-subscription.js.map +1 -1
  10. package/dist/billing/plan-card.d.ts +5 -13
  11. package/dist/billing/plan-card.js +3 -11
  12. package/dist/billing/plan-card.js.map +1 -1
  13. package/dist/billing/plan-switch.d.ts +5 -14
  14. package/dist/billing/plan-switch.js +1 -3
  15. package/dist/billing/plan-switch.js.map +1 -1
  16. package/dist/chart.d.ts +3 -14
  17. package/dist/chart.js +64 -11
  18. package/dist/chart.js.map +1 -1
  19. package/dist/components/amount-input.d.ts +2 -1
  20. package/dist/components/amount-input.js.map +1 -1
  21. package/dist/components/calendar-heatmap.js +4 -2
  22. package/dist/components/calendar-heatmap.js.map +1 -1
  23. package/dist/components/currency-option-list.d.ts +2 -1
  24. package/dist/components/currency-option-list.js +10 -3
  25. package/dist/components/currency-option-list.js.map +1 -1
  26. package/dist/components/currency-select.d.ts +3 -1
  27. package/dist/components/currency-select.js.map +1 -1
  28. package/dist/components/danger-confirm.d.ts +22 -1
  29. package/dist/components/danger-confirm.js +50 -28
  30. package/dist/components/danger-confirm.js.map +1 -1
  31. package/dist/components/data-table-filter-popover.d.ts +2 -1
  32. package/dist/components/data-table-filter-popover.js +3 -2
  33. package/dist/components/data-table-filter-popover.js.map +1 -1
  34. package/dist/components/data-table-filters.d.ts +33 -3
  35. package/dist/components/data-table-filters.js +6 -4
  36. package/dist/components/data-table-filters.js.map +1 -1
  37. package/dist/components/date-field.d.ts +1 -1
  38. package/dist/components/date-field.js.map +1 -1
  39. package/dist/components/date-picker.d.ts +1 -1
  40. package/dist/components/date-picker.js.map +1 -1
  41. package/dist/components/date-range-picker.d.ts +4 -17
  42. package/dist/components/date-range-picker.js +2 -2
  43. package/dist/components/date-range-picker.js.map +1 -1
  44. package/dist/components/dialog-frame.d.ts +3 -1
  45. package/dist/components/dialog-frame.js +6 -2
  46. package/dist/components/dialog-frame.js.map +1 -1
  47. package/dist/components/mini-calendar.js +3 -2
  48. package/dist/components/mini-calendar.js.map +1 -1
  49. package/dist/components/modal.d.ts +27 -0
  50. package/dist/components/modal.js +5 -3
  51. package/dist/components/modal.js.map +1 -1
  52. package/dist/components/nav-pills.d.ts +1 -1
  53. package/dist/components/nav-pills.js.map +1 -1
  54. package/dist/components/number-input.js.map +1 -1
  55. package/dist/components/series-chart-axes.d.ts +1 -1
  56. package/dist/components/series-chart-axes.js.map +1 -1
  57. package/dist/components/series-chart.d.ts +64 -19
  58. package/dist/components/series-chart.js +2 -1
  59. package/dist/components/series-chart.js.map +1 -1
  60. package/dist/components/stat-tile.d.ts +21 -2
  61. package/dist/components/stat-tile.js +4 -2
  62. package/dist/components/stat-tile.js.map +1 -1
  63. package/dist/components/treemap-labels.d.ts +6 -1
  64. package/dist/components/treemap-labels.js +2 -2
  65. package/dist/components/treemap-labels.js.map +1 -1
  66. package/dist/components/treemap.d.ts +29 -4
  67. package/dist/components/treemap.js +22 -9
  68. package/dist/components/treemap.js.map +1 -1
  69. package/dist/currencies.d.ts +16 -2
  70. package/dist/currencies.js +2 -1
  71. package/dist/currencies.js.map +1 -1
  72. package/dist/data-table.d.ts +1 -6
  73. package/dist/data-table.js +1 -9
  74. package/dist/data-table.js.map +1 -1
  75. package/dist/demo/demo-countdown.d.ts +0 -5
  76. package/dist/demo/demo-countdown.js +0 -4
  77. package/dist/demo/demo-countdown.js.map +1 -1
  78. package/dist/eslint/index.js +1 -1
  79. package/dist/feedback/feedback-context-labels.js +2 -1
  80. package/dist/feedback/feedback-context-labels.js.map +1 -1
  81. package/dist/feedback/feedback-context.d.ts +19 -3
  82. package/dist/feedback/feedback-context.js +12 -1
  83. package/dist/feedback/feedback-context.js.map +1 -1
  84. package/dist/feedback/feedback-diagnostics.d.ts +17 -0
  85. package/dist/feedback/feedback-diagnostics.js +27 -0
  86. package/dist/feedback/feedback-diagnostics.js.map +1 -0
  87. package/dist/feedback/feedback-dialog.d.ts +10 -0
  88. package/dist/feedback/feedback-dialog.js +6 -4
  89. package/dist/feedback/feedback-dialog.js.map +1 -1
  90. package/dist/feedback/feedback-mutation.d.ts +11 -23
  91. package/dist/feedback/feedback-mutation.js +1 -9
  92. package/dist/feedback/feedback-mutation.js.map +1 -1
  93. package/dist/feedback/feedback-public-list.d.ts +1 -6
  94. package/dist/feedback/feedback-public-list.js +1 -2
  95. package/dist/feedback/feedback-public-list.js.map +1 -1
  96. package/dist/feedback/feedback-record.d.ts +5 -0
  97. package/dist/feedback/feedback-record.js.map +1 -1
  98. package/dist/feedback/feedback-row-detail-labels.js +1 -8
  99. package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
  100. package/dist/feedback/feedback-row-detail.d.ts +6 -50
  101. package/dist/feedback/feedback-row-detail.js +3 -8
  102. package/dist/feedback/feedback-row-detail.js.map +1 -1
  103. package/dist/feedback/feedback-status-undo.d.ts +3 -19
  104. package/dist/feedback/feedback-status-undo.js.map +1 -1
  105. package/dist/feedback/feedback-submit-dialog.d.ts +5 -1
  106. package/dist/feedback/feedback-submit-dialog.js +15 -3
  107. package/dist/feedback/feedback-submit-dialog.js.map +1 -1
  108. package/dist/feedback/feedback-submit.d.ts +30 -2
  109. package/dist/feedback/feedback-submit.js +5 -2
  110. package/dist/feedback/feedback-submit.js.map +1 -1
  111. package/dist/feedback/feedback-thread.d.ts +0 -9
  112. package/dist/feedback/feedback-thread.js.map +1 -1
  113. package/dist/feedback.d.ts +6 -40
  114. package/dist/feedback.js +88 -46
  115. package/dist/feedback.js.map +1 -1
  116. package/dist/hooks/use-hotkey.js +11 -1
  117. package/dist/hooks/use-hotkey.js.map +1 -1
  118. package/dist/i18n/defaults.js +0 -4
  119. package/dist/i18n/defaults.js.map +1 -1
  120. package/dist/i18n/german.js +5 -20
  121. package/dist/i18n/german.js.map +1 -1
  122. package/dist/i18n/kit-labels.d.ts +5 -14
  123. package/dist/i18n/kit-labels.js.map +1 -1
  124. package/dist/i18n/locales/en.js +5 -31
  125. package/dist/i18n/locales/en.js.map +3 -3
  126. package/dist/i18n/locales/es.js +5 -20
  127. package/dist/i18n/locales/es.js.map +1 -1
  128. package/dist/i18n/locales/fr.js +5 -20
  129. package/dist/i18n/locales/fr.js.map +1 -1
  130. package/dist/i18n/locales/hu.js +5 -20
  131. package/dist/i18n/locales/hu.js.map +1 -1
  132. package/dist/i18n/locales/it.js +5 -21
  133. package/dist/i18n/locales/it.js.map +1 -1
  134. package/dist/i18n/locales/zh.js +5 -20
  135. package/dist/i18n/locales/zh.js.map +1 -1
  136. package/dist/i18n/review.js +9 -4
  137. package/dist/i18n/review.js.map +1 -1
  138. package/dist/i18n/shared-labels.js +0 -1
  139. package/dist/i18n/shared-labels.js.map +1 -1
  140. package/dist/index.d.ts +18 -67
  141. package/dist/index.js +28 -103
  142. package/dist/index.js.map +1 -1
  143. package/dist/lib/dates.d.ts +7 -1
  144. package/dist/lib/dates.js +8 -1
  145. package/dist/lib/dates.js.map +1 -1
  146. package/dist/lib/format.d.ts +37 -2
  147. package/dist/lib/format.js +25 -1
  148. package/dist/lib/format.js.map +1 -1
  149. package/dist/lib/intl-cache.d.ts +4 -2
  150. package/dist/lib/intl-cache.js.map +1 -1
  151. package/dist/lib/translation-review.d.ts +26 -0
  152. package/dist/lib/translation-review.js +14 -0
  153. package/dist/lib/translation-review.js.map +1 -1
  154. package/dist/rhf/fields.d.ts +2 -2
  155. package/dist/rhf/fields.js.map +1 -1
  156. package/dist/search/search-index.d.ts +0 -6
  157. package/dist/search/search-index.js +0 -4
  158. package/dist/search/search-index.js.map +1 -1
  159. package/dist/search.d.ts +1 -8
  160. package/dist/search.js +2 -7
  161. package/dist/search.js.map +1 -1
  162. package/dist/shell/account-menu-language.d.ts +7 -21
  163. package/dist/shell/account-menu-language.js +1 -3
  164. package/dist/shell/account-menu-language.js.map +1 -1
  165. package/dist/shell/account-menu-text-size.d.ts +9 -23
  166. package/dist/shell/account-menu-text-size.js +2 -3
  167. package/dist/shell/account-menu-text-size.js.map +1 -1
  168. package/dist/shell/app-shell.d.ts +0 -10
  169. package/dist/shell/app-shell.js +1 -7
  170. package/dist/shell/app-shell.js.map +1 -1
  171. package/dist/shell/company-switcher.d.ts +1 -1
  172. package/dist/shell/company-switcher.js.map +1 -1
  173. package/dist/shell/dev-menu.d.ts +1 -1
  174. package/dist/shell/dev-menu.js.map +1 -1
  175. package/dist/shell/top-bar-parts-labels.d.ts +1 -1
  176. package/dist/shell/top-bar-parts-labels.js.map +1 -1
  177. package/dist/shell.d.ts +7 -29
  178. package/dist/shell.js +12 -42
  179. package/dist/shell.js.map +1 -1
  180. package/dist/sync.d.ts +2 -6
  181. package/dist/sync.js +2 -15
  182. package/dist/sync.js.map +1 -1
  183. package/dist/theme/chart-palette.d.ts +0 -5
  184. package/dist/theme/chart-palette.js +0 -2
  185. package/dist/theme/chart-palette.js.map +1 -1
  186. package/package.json +1 -1
  187. package/src/account/delete-account-setting.tsx +3 -2
  188. package/src/auth/auth-errors.ts +69 -0
  189. package/src/billing/cancel-subscription.tsx +5 -16
  190. package/src/billing/plan-card.tsx +7 -24
  191. package/src/billing/plan-switch.tsx +5 -16
  192. package/src/chart.ts +62 -13
  193. package/src/components/amount-input.tsx +2 -1
  194. package/src/components/calendar-heatmap.tsx +8 -2
  195. package/src/components/currency-option-list.tsx +12 -4
  196. package/src/components/currency-select.tsx +3 -1
  197. package/src/components/danger-confirm.tsx +80 -27
  198. package/src/components/data-table-filter-popover.tsx +19 -11
  199. package/src/components/data-table-filters.ts +36 -8
  200. package/src/components/date-field.tsx +1 -1
  201. package/src/components/date-picker.tsx +0 -1
  202. package/src/components/date-range-picker.tsx +6 -22
  203. package/src/components/dialog-frame.tsx +12 -4
  204. package/src/components/mini-calendar.tsx +5 -2
  205. package/src/components/modal.tsx +36 -7
  206. package/src/components/nav-pills.tsx +1 -1
  207. package/src/components/number-input.tsx +4 -3
  208. package/src/components/series-chart-axes.ts +6 -1
  209. package/src/components/series-chart.tsx +78 -21
  210. package/src/components/stat-tile.tsx +32 -3
  211. package/src/components/treemap-labels.ts +11 -3
  212. package/src/components/treemap.tsx +65 -12
  213. package/src/currencies.ts +23 -3
  214. package/src/data-table.ts +1 -11
  215. package/src/demo/demo-countdown.ts +3 -8
  216. package/src/feedback/feedback-context-labels.ts +1 -0
  217. package/src/feedback/feedback-context.tsx +34 -2
  218. package/src/feedback/feedback-diagnostics.tsx +41 -0
  219. package/src/feedback/feedback-dialog.tsx +16 -4
  220. package/src/feedback/feedback-mutation.ts +12 -33
  221. package/src/feedback/feedback-public-list.tsx +1 -7
  222. package/src/feedback/feedback-record.ts +5 -0
  223. package/src/feedback/feedback-row-detail-labels.ts +1 -8
  224. package/src/feedback/feedback-row-detail.tsx +21 -58
  225. package/src/feedback/feedback-status-undo.ts +4 -18
  226. package/src/feedback/feedback-submit-dialog.tsx +15 -2
  227. package/src/feedback/feedback-submit.tsx +36 -3
  228. package/src/feedback/feedback-thread.tsx +0 -10
  229. package/src/feedback.ts +89 -48
  230. package/src/hooks/use-hotkey.ts +24 -1
  231. package/src/i18n/defaults.ts +0 -4
  232. package/src/i18n/german.ts +4 -19
  233. package/src/i18n/kit-labels.tsx +5 -14
  234. package/src/i18n/locales/es.ts +4 -19
  235. package/src/i18n/locales/fr.ts +4 -19
  236. package/src/i18n/locales/hu.ts +4 -19
  237. package/src/i18n/locales/it.ts +4 -20
  238. package/src/i18n/locales/zh.ts +4 -19
  239. package/src/i18n/review.ts +9 -4
  240. package/src/i18n/shared-labels.ts +0 -1
  241. package/src/index.ts +44 -114
  242. package/src/lib/dates.ts +24 -2
  243. package/src/lib/format.ts +66 -3
  244. package/src/lib/intl-cache.ts +4 -2
  245. package/src/lib/translation-review.ts +52 -0
  246. package/src/rhf/fields.tsx +2 -2
  247. package/src/search/search-index.ts +0 -15
  248. package/src/search.ts +8 -8
  249. package/src/shell/account-menu-language.tsx +7 -27
  250. package/src/shell/account-menu-text-size.tsx +13 -32
  251. package/src/shell/app-shell.tsx +0 -13
  252. package/src/shell/company-switcher.tsx +1 -1
  253. package/src/shell/dev-menu.tsx +1 -1
  254. package/src/shell/top-bar-parts-labels.ts +1 -1
  255. package/src/shell.ts +17 -44
  256. package/src/sync.ts +2 -15
  257. package/src/theme/chart-palette.ts +0 -2
  258. package/tokens.css +4 -4
  259. package/dist/billing/dialog-close.d.ts +0 -29
  260. package/dist/billing/dialog-close.js +0 -8
  261. package/dist/billing/dialog-close.js.map +0 -1
  262. package/dist/components/field-sync-labels.d.ts +0 -3
  263. package/dist/components/field-sync-labels.js +0 -12
  264. package/dist/components/field-sync-labels.js.map +0 -1
  265. package/dist/components/field-sync.d.ts +0 -186
  266. package/dist/components/field-sync.js +0 -269
  267. package/dist/components/field-sync.js.map +0 -1
  268. package/dist/components/section-nav.d.ts +0 -44
  269. package/dist/components/section-nav.js +0 -33
  270. package/dist/components/section-nav.js.map +0 -1
  271. package/dist/components/sign-chip-labels.d.ts +0 -3
  272. package/dist/components/sign-chip-labels.js +0 -11
  273. package/dist/components/sign-chip-labels.js.map +0 -1
  274. package/dist/components/sign-chip.d.ts +0 -77
  275. package/dist/components/sign-chip.js +0 -47
  276. package/dist/components/sign-chip.js.map +0 -1
  277. package/dist/lib/isin.d.ts +0 -38
  278. package/dist/lib/isin.js +0 -36
  279. package/dist/lib/isin.js.map +0 -1
  280. package/dist/shell/role-switcher.d.ts +0 -28
  281. package/dist/shell/role-switcher.js +0 -31
  282. package/dist/shell/role-switcher.js.map +0 -1
  283. package/src/billing/dialog-close.ts +0 -34
  284. package/src/components/field-sync-labels.ts +0 -14
  285. package/src/components/field-sync.tsx +0 -504
  286. package/src/components/section-nav.tsx +0 -72
  287. package/src/components/sign-chip-labels.ts +0 -13
  288. package/src/components/sign-chip.tsx +0 -133
  289. package/src/lib/isin.ts +0 -72
  290. package/src/shell/role-switcher.tsx +0 -53
@@ -8,19 +8,14 @@ import { DialogFrame } from "../components/dialog-frame";
8
8
  import { COMMIT_EXCEPT_BILLING } from "../components/write-lock";
9
9
  import { useBillingLabels } from "./billing-labels";
10
10
  import type { BillingLabels } from "./billing-labels";
11
- import { dialogClose } from "./dialog-close";
12
- import type { WithDialogClose } from "./dialog-close";
13
11
 
14
- /** {@link CancelSubscriptionConfirmProps} before one of its two close props is required. */
15
- interface CancelSubscriptionConfirmFields {
12
+ export interface CancelSubscriptionConfirmProps {
16
13
  open: boolean;
17
14
  /** The admin dismissed it — Back, Escape, the backdrop. Confirm leaves it open: close it
18
15
  * once `POST /billing/cancel` answers. 0.52: every other kit dialog's `onClose`
19
- * (CONTRIBUTING §4; refactor audit 2026-10-30, API-5). */
20
- onClose?: () => void;
21
- /** @deprecated 0.52 — `onClose`, as every other kit dialog; removed in 0.53. Called with
22
- * `false` when there is no `onClose`. */
23
- onOpenChange?: (open: boolean) => void;
16
+ * (CONTRIBUTING §4; refactor audit 2026-10-30, API-5); required since 0.53 removed
17
+ * `onOpenChange` (deprecated in 0.52). */
18
+ onClose: () => void;
24
19
  /** The overview's `current_period_end`: the day the subscription ends, in a sentence —
25
20
  * the `<UiKitProvider formatDate>` (the app's date preference) when it set one, else the
26
21
  * kit's `formatDate`, `"medium"`, in the provider's locale and `timeZone` (0.52). */
@@ -36,11 +31,6 @@ interface CancelSubscriptionConfirmFields {
36
31
  labels?: LabelOverride<BillingLabels>;
37
32
  }
38
33
 
39
- /** What {@link CancelSubscriptionConfirm} takes: `onClose`, or alone the deprecated
40
- * `onOpenChange`, is required (0.52 code review: with neither it compiled, and nothing
41
- * could dismiss it). */
42
- export type CancelSubscriptionConfirmProps = WithDialogClose<CancelSubscriptionConfirmFields>;
43
-
44
34
  /**
45
35
  * The in-app cancel's confirm (docs/billing-harmonization.md §16.2 decision 2, §16.4,
46
36
  * ui-kit 0.42): "Cancel subscription" for an admin the provider's portal doesn't open for
@@ -68,7 +58,6 @@ export type CancelSubscriptionConfirmProps = WithDialogClose<CancelSubscriptionC
68
58
  export function CancelSubscriptionConfirm({
69
59
  open,
70
60
  onClose,
71
- onOpenChange,
72
61
  periodEnd,
73
62
  onConfirm,
74
63
  pending = false,
@@ -84,7 +73,7 @@ export function CancelSubscriptionConfirm({
84
73
  open={open}
85
74
  title={labels.cancelConfirmTitle}
86
75
  description={labels.cancelConfirmBody(day(periodEnd))}
87
- onClose={dialogClose({ onClose, onOpenChange })}
76
+ onClose={onClose}
88
77
  busy={pending}
89
78
  data-cancel-subscription-confirm=""
90
79
  actions={(close) => (
@@ -14,8 +14,6 @@ import { Button } from "../components/button";
14
14
  import { DialogFrame } from "../components/dialog-frame";
15
15
  import { useBillingLabels } from "./billing-labels";
16
16
  import type { BillingLabels } from "./billing-labels";
17
- import { dialogClose } from "./dialog-close";
18
- import type { WithDialogClose } from "./dialog-close";
19
17
  import { BILLING_INTERVALS, billingCurrencyFor, formatPlanPrice, planPrice } from "./plan-price";
20
18
  import type { BillingCurrency, BillingInterval, PlanPrices } from "./plan-price";
21
19
 
@@ -502,16 +500,13 @@ export function PlanPicker({
502
500
  );
503
501
  }
504
502
 
505
- /** {@link GrantWarningConfirmProps} before one of its two close props is required. */
506
- interface GrantWarningConfirmFields {
503
+ export interface GrantWarningConfirmProps {
507
504
  /** Shown, controlled. */
508
505
  open: boolean;
509
506
  /** "Back", Escape, the backdrop — and after "Buy anyway". 0.52: every other kit
510
- * dialog's `onClose` (CONTRIBUTING §4; refactor audit 2026-10-30, API-5). */
511
- onClose?: () => void;
512
- /** @deprecated 0.52 — `onClose`, as every other kit dialog; removed in 0.53. Called with
513
- * `false` when there is no `onClose`. */
514
- onOpenChange?: (open: boolean) => void;
507
+ * dialog's `onClose` (CONTRIBUTING §4; refactor audit 2026-10-30, API-5); required
508
+ * since 0.53 removed `onOpenChange` (deprecated in 0.52). */
509
+ onClose: () => void;
515
510
  /** "Buy anyway": the app opens its checkout. The dialog closes itself after it. */
516
511
  onConfirm: () => void;
517
512
  /** Prop > `<UiKitProvider labels={{ billing }}>` > English: `grantWarningTitle`,
@@ -519,11 +514,6 @@ interface GrantWarningConfirmFields {
519
514
  labels?: LabelOverride<BillingLabels>;
520
515
  }
521
516
 
522
- /** What {@link GrantWarningConfirm} takes: `onClose`, or alone the deprecated
523
- * `onOpenChange`, is required (0.52 code review: with neither it compiled, and nothing
524
- * could dismiss it). */
525
- export type GrantWarningConfirmProps = WithDialogClose<GrantWarningConfirmFields>;
526
-
527
517
  /**
528
518
  * 0.42 (§16.8 B): the permanent-grant warning before a checkout — `grantWarningTitle`,
529
519
  * `grantWarningBody`, "Back" and "Buy anyway" (`grantWarningAction`). {@link PlanPicker}
@@ -533,22 +523,15 @@ export type GrantWarningConfirmProps = WithDialogClose<GrantWarningConfirmFields
533
523
  * to a checkout — show this before that checkout, so a permanent-grant holder is still
534
524
  * warned. Carries `data-grant-warning`.
535
525
  */
536
- export function GrantWarningConfirm({
537
- open,
538
- onClose,
539
- onOpenChange,
540
- onConfirm,
541
- labels: labelsProp,
542
- }: GrantWarningConfirmProps) {
526
+ export function GrantWarningConfirm({ open, onClose, onConfirm, labels: labelsProp }: GrantWarningConfirmProps) {
543
527
  const labels = useBillingLabels(labelsProp);
544
528
  const common = useKitLabels("common", DEFAULT_COMMON_LABELS);
545
- const close = dialogClose({ onClose, onOpenChange });
546
529
  return (
547
530
  <DialogFrame
548
531
  open={open}
549
532
  title={labels.grantWarningTitle}
550
533
  description={labels.grantWarningBody}
551
- onClose={close}
534
+ onClose={onClose}
552
535
  data-grant-warning=""
553
536
  actions={(lower) => (
554
537
  <>
@@ -567,7 +550,7 @@ export function GrantWarningConfirm({
567
550
  // and the lowering panel is not inert: a double click on "Buy anyway" opened
568
551
  // the checkout twice (0.52 code review, plan-card.tsx). The caller's `open`
569
552
  // turning false still plays the exit.
570
- close();
553
+ onClose();
571
554
  }}
572
555
  >
573
556
  {labels.grantWarningAction}
@@ -13,8 +13,6 @@ import { COMMIT_EXCEPT_BILLING } from "../components/write-lock";
13
13
  import { DEFAULT_PLAN_CHANGE_LABELS } from "./plan-change-labels";
14
14
  import { useBillingLabels } from "./billing-labels";
15
15
  import type { BillingLabels } from "./billing-labels";
16
- import { dialogClose } from "./dialog-close";
17
- import type { WithDialogClose } from "./dialog-close";
18
16
  import type { PlanSwitchPreview } from "./billing-overview";
19
17
  import { minorToMajor } from "./plan-price";
20
18
  import type { BillingInterval } from "./plan-price";
@@ -39,16 +37,13 @@ export interface PlanSwitchSide {
39
37
  interval: BillingInterval;
40
38
  }
41
39
 
42
- /** {@link PlanSwitchConfirmProps} before one of its two close props is required. */
43
- interface PlanSwitchConfirmFields {
40
+ export interface PlanSwitchConfirmProps {
44
41
  open: boolean;
45
42
  /** The payer dismissed it — Cancel, Escape, the backdrop. Confirm leaves it open: close
46
43
  * it once the switch answers. 0.52: every other kit dialog's `onClose` (CONTRIBUTING
47
- * §4; refactor audit 2026-10-30, API-5). */
48
- onClose?: () => void;
49
- /** @deprecated 0.52 — `onClose`, as every other kit dialog; removed in 0.53. Called with
50
- * `false` when there is no `onClose`. */
51
- onOpenChange?: (open: boolean) => void;
44
+ * §4; refactor audit 2026-10-30, API-5); required since 0.53 removed `onOpenChange`
45
+ * (deprecated in 0.52). */
46
+ onClose: () => void;
52
47
  /** What the payer pays now: the overview's `paid`. */
53
48
  from: PlanSwitchSide;
54
49
  /** The pick. */
@@ -88,11 +83,6 @@ interface PlanSwitchConfirmFields {
88
83
  labels?: LabelOverride<BillingLabels>;
89
84
  }
90
85
 
91
- /** What {@link PlanSwitchConfirm} takes: `onClose`, or alone the deprecated
92
- * `onOpenChange`, is required (0.52 code review: with neither it compiled, and nothing
93
- * could dismiss it). */
94
- export type PlanSwitchConfirmProps = WithDialogClose<PlanSwitchConfirmFields>;
95
-
96
86
  /**
97
87
  * The confirm for a plan switch (§15.2.10): from → to, each with its billing period;
98
88
  * Paddle's preview — "Charged now" (an upgrade) or "Credited towards your next charges"
@@ -118,7 +108,6 @@ export type PlanSwitchConfirmProps = WithDialogClose<PlanSwitchConfirmFields>;
118
108
  export function PlanSwitchConfirm({
119
109
  open,
120
110
  onClose,
121
- onOpenChange,
122
111
  from,
123
112
  to,
124
113
  preview,
@@ -165,7 +154,7 @@ export function PlanSwitchConfirm({
165
154
  open={open}
166
155
  title={labels.switchTitle}
167
156
  description={labels.switchFromTo(side(from), side(to))}
168
- onClose={dialogClose({ onClose, onOpenChange })}
157
+ onClose={onClose}
169
158
  busy={pending}
170
159
  // A preview that loads is busy too, but holds nothing: Cancel stays a way out.
171
160
  aria-busy={preview === undefined || undefined}
package/src/chart.ts CHANGED
@@ -10,14 +10,24 @@
10
10
  // because the main barrel re-exports everything here too, so `import { Button } from
11
11
  // "@eifi1/ui-kit"` reaches recharts as well (packaging-contract.test.ts asserts it).
12
12
  export * from "./components/chart";
13
- export * from "./theme/chart-palette";
14
- // 0.52: the slice's share of the 55 names no app reads (refactor audit 2026-10-30, API-4,
15
- // decision 4), tagged on their specifiers as the barrel's are — the modules keep them.
13
+ // Named, not `*`, for chart-palette, series-chart and toggle-legend: 0.53 took the slice's
14
+ // share of the 55 names no app reads off the surface (deprecated in 0.52, refactor audit
15
+ // 2026-10-30, API-4) — `EDGE_MIN_CONTRAST`, `resolveTooltipPlacement`, `STROKE_PATTERNS`,
16
+ // which their modules keep for the kit's own use.
16
17
  export {
17
- /** @deprecated 0.52 — `chartEdge`, which measures a fill against it (API-4); removed in 0.53 */
18
- EDGE_MIN_CONTRAST,
19
- /** @deprecated 0.52 — `--chart-ink` (`CHART_ROLES.outline`), the same ink as a token (API-4); removed in 0.53 */
20
- CHART_INK_HEX,
18
+ PALETTE_SIZE,
19
+ CVD_ORDER,
20
+ paletteFor,
21
+ CHART_COLORS,
22
+ CHART_ROLES,
23
+ CHART_MUTED_HEX,
24
+ chartEdge,
25
+ PALETTE_HEX,
26
+ HEATMAP_HEX,
27
+ lerpHex,
28
+ textOn,
29
+ type PaletteForOptions,
30
+ type HeatStops,
21
31
  } from "./theme/chart-palette";
22
32
 
23
33
  // The tile chart (treemap) the apps share — moved here from keksdose so every consumer
@@ -27,10 +37,42 @@ export * from "./components/treemap-labels";
27
37
 
28
38
  // lenkbank's zoomable series chart, its synced x-zoom, the toggle legend and the
29
39
  // mirrored-pair axis geometry — moved here so the apps derive them from the package.
30
- export * from "./components/series-chart";
31
40
  export {
32
- /** @deprecated 0.52 — `SeriesChart`, which places its tooltip with it (API-4); removed in 0.53 */
33
- resolveTooltipPlacement,
41
+ AXIS_TICK_WIDTH,
42
+ AXIS_TITLE_STRIP,
43
+ anchoredBand,
44
+ axisBandWidth,
45
+ mergeSeries,
46
+ oneAxis,
47
+ padBand,
48
+ paddedDomain,
49
+ seriesKey,
50
+ soleSeriesColor,
51
+ visibleSeries,
52
+ seriesLegendEntries,
53
+ StaticSeriesChart,
54
+ SeriesChart,
55
+ type TimeTickUnit,
56
+ type SeriesChartAxisBudget,
57
+ type SeriesSource,
58
+ type SeriesChartSeries,
59
+ type SeriesChartType,
60
+ type SeriesChartPoint,
61
+ type SeriesChartRow,
62
+ type SeriesChartXValue,
63
+ type SeriesChartTickValues,
64
+ type SeriesChartAxis,
65
+ type SeriesChartXTick,
66
+ type SeriesChartX,
67
+ type SeriesChartTooltipPlacement,
68
+ type SeriesChartTooltip,
69
+ type SeriesChartSpan,
70
+ type SeriesChartTone,
71
+ type SeriesChartReference,
72
+ type SeriesChartMarker,
73
+ type SeriesChartHit,
74
+ type SeriesChartProps,
75
+ type SeriesLegendAxes,
34
76
  } from "./components/series-chart";
35
77
  // 0.32.1 (keksdose's 0.32 report): the height every chart takes, for an app's wrappers.
36
78
  export type { ChartHeight } from "./components/chart-height";
@@ -41,10 +83,17 @@ export * from "./components/chart-zoom";
41
83
  // packaging contract reads this entry's own imports to know which modules may import
42
84
  // recharts, so chart-zoom-core has to be one of them (MOD-6).
43
85
  export { SharedXZoom } from "./components/chart-zoom-core";
44
- export * from "./components/toggle-legend";
45
86
  export {
46
- /** @deprecated 0.52 — `strokeDash`, which picks a series' pattern from it (API-4); removed in 0.53 */
47
- STROKE_PATTERNS,
87
+ strokeDash,
88
+ STEP_DASH,
89
+ toggleHidden,
90
+ ToggleLegend,
91
+ StaticLegend,
92
+ LegendColumn,
93
+ LegendGroup,
94
+ type LegendEntry,
95
+ type ToggleLegendProps,
96
+ type StaticLegendProps,
48
97
  } from "./components/toggle-legend";
49
98
  export * from "./components/facing-pair";
50
99
  export * from "./components/series-chart-labels";
@@ -149,7 +149,8 @@ export interface AmountInputProps extends FieldHelpProps, DataAttributes {
149
149
  currencyNames?: Record<string, string>;
150
150
  /** Offer only these ISO codes in the chip's list, in this order — `CurrencySelect`'s
151
151
  * `options`, since the chip renders CurrencySelect's list (round 0.34 Wave 2, DUP-4).
152
- * Default every currency the kit knows. */
152
+ * Default every currency the kit knows but a `retired` one (the lev), which only the
153
+ * field that holds it lists. */
153
154
  currencyOptions?: string[];
154
155
  /** Focus the field on mount — on mobile this also opens the numpad sheet, so a
155
156
  * new-transaction form can jump straight to amount entry (feedback #70). */
@@ -3,6 +3,7 @@ import type { ComponentPropsWithRef, CSSProperties, KeyboardEvent, ReactNode } f
3
3
  import { cn } from "../lib/cn";
4
4
  import { dirOf, horizontalStep } from "../lib/direction";
5
5
  import { addMonthsClamped, localeWeekStart, parseIsoDate, toLocalIso } from "../lib/dates";
6
+ import { dateFormatter } from "../lib/intl-cache";
6
7
  import { useKitLabels, useKitLocale, useKitWeekStart, type LabelOverride } from "../i18n/kit-labels";
7
8
  import { lerpHex, textOn } from "../theme/chart-palette";
8
9
  import { parseHex } from "../theme/color";
@@ -154,6 +155,8 @@ const DAY_NAME_FORMAT: Intl.DateTimeFormatOptions = {
154
155
  month: "long",
155
156
  year: "numeric",
156
157
  };
158
+ /** A month's name over its first column ("Sep"). */
159
+ const MONTH_LABEL_FORMAT: Intl.DateTimeFormatOptions = { month: "short" };
157
160
 
158
161
  /** The empty day: a wash of the text colour, like `--bg-hover`, so it reads as a cell
159
162
  * on both themes without claiming any value. */
@@ -345,7 +348,10 @@ export function CalendarHeatmap({
345
348
  };
346
349
  }, [layout, firstIso, lastIso]);
347
350
 
348
- const formatDay = (d: Date) => d.toLocaleDateString(locale, DAY_NAME_FORMAT);
351
+ // Every day cell names its date, so through the kit's formatter cache, as the month
352
+ // labels are: `toLocaleDateString` built one per cell and render, 365 for a year's grid
353
+ // (keksdose audit follow-up, 2026-11-01).
354
+ const formatDay = (d: Date) => dateFormatter(locale, DAY_NAME_FORMAT).format(d);
349
355
 
350
356
  const weekdayLabels = useMemo(() => {
351
357
  const ref = new Date(2024, 0, 7 + weekStart); // 7 January 2024 was a Sunday
@@ -503,7 +509,7 @@ export function CalendarHeatmap({
503
509
  for (let r = 0; r < 7; r++) {
504
510
  const d = dayAt(w * 7 + r);
505
511
  if (d && (d.getDate() === 1 || (w === 0 && r === lead && d.getDate() <= 14))) {
506
- return d.toLocaleDateString(locale, { month: "short" });
512
+ return dateFormatter(locale, MONTH_LABEL_FORMAT).format(d);
507
513
  }
508
514
  }
509
515
  return "";
@@ -73,7 +73,8 @@ export interface CurrencyOptionListProps {
73
73
  /** A currency was picked. The host closes the list. */
74
74
  onChoose: (code: string) => void;
75
75
  /** Only these ISO codes, in this order; codes the kit has no definition for are
76
- * dropped. Default every one of {@link CURRENCIES}. */
76
+ * dropped. Default every one of {@link CURRENCIES} that is not `retired` (the chosen
77
+ * one too, if it is). A retired code named here stays: the list is the caller's. */
77
78
  codes?: readonly string[];
78
79
  /** Translated names by code — see {@link currencyName}. */
79
80
  currencyNames?: Record<string, string>;
@@ -112,19 +113,27 @@ export function CurrencyOptionList({
112
113
  searchPlaceholder,
113
114
  sheetTitle,
114
115
  }: CurrencyOptionListProps) {
116
+ const held = getCurrency(value);
117
+ const chosen = held?.code;
118
+ // A retired currency the field holds — the one the default list keeps for it. Its own
119
+ // key, so a change of an ordinary value does not rebuild the rows and their names.
120
+ const heldRetired = held?.retired ? held.code : undefined;
115
121
  // Keyed on the codes' CONTENTS: an inline `options={["EUR", "CHF"]}` is a new array
116
122
  // every render.
117
123
  const codesKey = codes?.join(",");
118
124
  const pool = useMemo(
119
125
  () =>
120
126
  codesKey === undefined
121
- ? CURRENCIES
127
+ ? // No currency that was replaced (the lev, since Bulgaria's euro: keksdose audit
128
+ // follow-up, 2026-11-01) — unless the field holds it, which then opens on its
129
+ // own row, checked, rather than on a list where nothing is chosen.
130
+ CURRENCIES.filter((c) => !c.retired || c.code === heldRetired)
122
131
  : // The caller's order; codes the kit has no definition for are dropped.
123
132
  codesKey
124
133
  .split(",")
125
134
  .map((code) => getCurrency(code))
126
135
  .filter((c): c is CurrencyOption => c !== undefined),
127
- [codesKey],
136
+ [codesKey, heldRetired],
128
137
  );
129
138
  const named = useMemo(
130
139
  () => pool.map((c) => ({ c, name: currencyName(c, currencyNames, locale) })),
@@ -147,7 +156,6 @@ export function CurrencyOptionList({
147
156
  }));
148
157
  }, [named, query, locale]);
149
158
 
150
- const chosen = getCurrency(value)?.code;
151
159
  return (
152
160
  <ComboboxPanel
153
161
  // The core with the currency filter's rows in place of its own.
@@ -41,7 +41,9 @@ export interface CurrencySelectProps extends Omit<ComponentPropsWithoutRef<"div"
41
41
  /**
42
42
  * Restrict the pickable currencies to these ISO codes (e.g. only the
43
43
  * currencies actually present in a budget). Order is preserved. Defaults to
44
- * the full {@link CURRENCIES} list.
44
+ * the full {@link CURRENCIES} list but a `retired` currency (the lev, since
45
+ * Bulgaria's euro), which only a field that holds it lists. A retired code named
46
+ * here is offered: the list is the caller's.
45
47
  */
46
48
  options?: string[];
47
49
  /** Required and unanswered — {@link FIELD_INVALID}, the same rose border the
@@ -6,6 +6,7 @@ import { useKitLabels, type LabelOverride } from "../i18n/kit-labels";
6
6
  import { Button } from "./button";
7
7
  import { Input } from "./input";
8
8
  import { Label } from "./field-base";
9
+ import { FIELD_ERROR_CLASS } from "./field-parts";
9
10
  import { Spinner } from "./spinner";
10
11
  import type { ButtonVariant } from "./button-look";
11
12
  import type { InputProps } from "./input";
@@ -536,6 +537,27 @@ export interface DangerConfirmProps extends Omit<ComponentPropsWithRef<"div">, "
536
537
  * confirm, so a keyboard user can retry or Shift+Tab to the field to correct (0.24).
537
538
  */
538
539
  busy?: boolean;
540
+ /**
541
+ * Why the last confirm failed ("Wrong password"), in the tile beside the field it is
542
+ * about rather than in a toast away from it (keksdose audit follow-up, 2026-11-01:
543
+ * PasswordDangerCard toasted its `password_incorrect` for want of this). The app sets
544
+ * it from the failure and words it — a 400 is a wrong password, a 500 is not.
545
+ *
546
+ * Drawn as the field's own `error`, as {@link ReauthDialog} draws its own: under the
547
+ * password with `requirePassword`, else under the typed phrase — the field painted,
548
+ * `aria-invalid`, the message its `aria-describedby` — and in a tile with neither, as
549
+ * a line above the buttons. Announced as it appears (`role="alert"`): it arrives
550
+ * after the press, with focus on the confirm, where a field's description alone is
551
+ * not heard. Passed at all — even as `undefined` — the field keeps its box, as with
552
+ * Input's `error`, so the message going away does not rebuild the field mid-edit.
553
+ *
554
+ * Hidden while the next attempt runs, once the user edits the tick, the phrase or the
555
+ * password (it is about what was submitted), and when the tile arms or disarms, until
556
+ * the next confirm; shown again when a confirm fails with it still set. So a
557
+ * mutation's error can be handed over as it is:
558
+ * `error={mut.error ? message(mut.error) : undefined}`.
559
+ */
560
+ error?: ReactNode;
539
561
  /** The arm button is disabled. */
540
562
  disabled?: boolean;
541
563
  /**
@@ -628,30 +650,32 @@ function firstSlotField(slot: HTMLElement | null): HTMLElement | null {
628
650
  * destructive button itself), and disarming moves it back to the arm button, so the
629
651
  * keyboard user is never left on an element that just vanished.
630
652
  */
631
- export function DangerConfirm({
632
- onConfirm,
633
- requirePassword,
634
- requireAcknowledge,
635
- consequences,
636
- phrase,
637
- phraseMatch = "trim",
638
- prompt,
639
- tone = "danger",
640
- confirmVariant,
641
- confirmDisabledReason,
642
- children,
643
- armLabel,
644
- confirmLabel,
645
- busy: busyProp,
646
- disabled,
647
- lockedReason: ownLockedReason,
648
- commit,
649
- armed: armedProp,
650
- onArmedChange,
651
- labels: labelsProp,
652
- className,
653
- ...rest
654
- }: DangerConfirmProps) {
653
+ export function DangerConfirm(props: DangerConfirmProps) {
654
+ const {
655
+ onConfirm,
656
+ requirePassword,
657
+ requireAcknowledge,
658
+ consequences,
659
+ phrase,
660
+ phraseMatch = "trim",
661
+ prompt,
662
+ tone = "danger",
663
+ confirmVariant,
664
+ confirmDisabledReason,
665
+ children,
666
+ armLabel,
667
+ confirmLabel,
668
+ busy: busyProp,
669
+ error,
670
+ disabled,
671
+ lockedReason: ownLockedReason,
672
+ commit,
673
+ armed: armedProp,
674
+ onArmedChange,
675
+ labels: labelsProp,
676
+ className,
677
+ ...rest
678
+ } = props;
655
679
  const labels = useKitLabels("dangerConfirm", DEFAULT_DANGER_CONFIRM_LABELS, labelsProp);
656
680
  const lockedReason = useCommitReason(commit, ownLockedReason);
657
681
  const [armedState, setArmedState] = useState(false);
@@ -659,6 +683,9 @@ export function DangerConfirm({
659
683
  const [password, setPassword] = useState("");
660
684
  const [typed, setTyped] = useState("");
661
685
  const [acknowledged, setAcknowledged] = useState(false);
686
+ // The `error` describes what was submitted: an edit, an arm or a disarm leaves it
687
+ // describing nothing on screen. Cleared by the next confirm — ReauthDialog's `edited`.
688
+ const [errorStale, setErrorStale] = useState(false);
662
689
  const { pending, run } = usePromisePending();
663
690
  const busy = Boolean(busyProp) || pending;
664
691
  // `readOnly` rather than `disabled` on the fields while busy: disabling the field
@@ -686,6 +713,8 @@ export function DangerConfirm({
686
713
  const [wasArmed, setWasArmed] = useState(armed);
687
714
  if (wasArmed !== armed) {
688
715
  setWasArmed(armed);
716
+ // An `error` still passed is about the attempt before: not shown until a confirm.
717
+ setErrorStale(true);
689
718
  if (!armed) {
690
719
  setPassword("");
691
720
  setTyped("");
@@ -785,11 +814,22 @@ export function DangerConfirm({
785
814
  ...(asksAcknowledge && { acknowledged: true as const }),
786
815
  };
787
816
  confirmed.current = true;
817
+ setErrorStale(false);
788
818
  // Disarms when it resolves; a rejection leaves it armed, fields kept: the caller
789
819
  // shows why, the user retries.
790
820
  run(onConfirm(values.password, values), () => setArmed(false));
791
821
  };
792
822
 
823
+ // The failure's message, under the field it is about: the password, else the phrase,
824
+ // else (a tile with neither) a line of its own. Not while the next attempt runs.
825
+ const shownError = errorStale || busy ? undefined : error;
826
+ const errorAlert = hasContent(shownError) ? <span role="alert">{shownError}</span> : undefined;
827
+ const errorOn = requirePassword ? "password" : phrase !== undefined ? "phrase" : "line";
828
+ // Passed at all, the carrying field keeps its box (Input's `error` rule): only then.
829
+ const fieldError = (field: "password" | "phrase") =>
830
+ "error" in props && errorOn === field ? { error: errorAlert } : {};
831
+ const edited = () => setErrorStale(true);
832
+
793
833
  // The first field takes focus on arm: the tick, then the phrase, then the password —
794
834
  // the order they are drawn in.
795
835
  const firstField = asksAcknowledge ? "acknowledge" : phrase !== undefined ? "phrase" : "password";
@@ -880,7 +920,9 @@ export function DangerConfirm({
880
920
  // Not `disabled` while busy, for the same reason the fields are readOnly:
881
921
  // the box may hold the focus. A change while busy is simply not taken.
882
922
  onCheckedChange={(next) => {
883
- if (!busy) setAcknowledged(next);
923
+ if (busy) return;
924
+ setAcknowledged(next);
925
+ edited();
884
926
  }}
885
927
  />
886
928
  )}
@@ -890,11 +932,15 @@ export function DangerConfirm({
890
932
  target={phrase}
891
933
  match={phraseMatch}
892
934
  value={typed}
893
- onValueChange={setTyped}
935
+ onValueChange={(next) => {
936
+ setTyped(next);
937
+ edited();
938
+ }}
894
939
  busy={busy}
895
940
  // Already resolved through this tile's own `labels` prop; handed on so a
896
941
  // per-instance phrase wording reaches the field.
897
942
  labels={{ phrase: labels.phrase, phrasePlaceholder: labels.phrasePlaceholder }}
943
+ {...fieldError("phrase")}
898
944
  />
899
945
  )}
900
946
  {requirePassword && (
@@ -903,9 +949,16 @@ export function DangerConfirm({
903
949
  label={labels.password}
904
950
  value={password}
905
951
  busy={busy}
906
- onValueChange={setPassword}
952
+ onValueChange={(next) => {
953
+ setPassword(next);
954
+ edited();
955
+ }}
956
+ {...fieldError("password")}
907
957
  />
908
958
  )}
959
+ {errorOn === "line" && errorAlert !== undefined && (
960
+ <p className={cn(FIELD_ERROR_CLASS, "mt-0")}>{errorAlert}</p>
961
+ )}
909
962
  <div className="flex flex-wrap gap-2">
910
963
  <Button id={cancelId} type="button" variant="ghost" disabled={busy} onClick={() => setArmed(false)}>
911
964
  {labels.cancel}
@@ -40,7 +40,8 @@ interface FilterPopoverProps<T> {
40
40
  * The contents of a column's filter Popover. Renders the right control set for
41
41
  * the column's filter type — text search, select checklist, date range (presets
42
42
  * + {@link MiniCalendar} + from/to inputs), or numeric min/max with an abs
43
- * toggle — driving the shared {@link FilterValue} state.
43
+ * toggle (none for `numberFilter(getValue, { abs: false })`) — driving the shared
44
+ * {@link FilterValue} state.
44
45
  */
45
46
  export function FilterPopover<T>({
46
47
  column,
@@ -219,6 +220,11 @@ export function FilterPopover<T>({
219
220
  }
220
221
 
221
222
  // number
223
+ // `abs: false` on the filter: no "Absolute value" box, and a state that carries `abs`
224
+ // anyway (a view stored before the column lost the box) is handed on without it at the
225
+ // first bound typed — there is no box to untick it with (kastlan reuse audit
226
+ // 2026-11-02, proposal 7).
227
+ const absBox = filter.abs !== false;
222
228
  const v = state.type === "number" ? state : { type: "number" as const, min: "", max: "", abs: false };
223
229
  // The bounds were native `type="number"` inputs, the one numeric entry in the kit that
224
230
  // bypassed decimal-marks (BUG-10, refactor audit 2026-10): where the browser refuses a
@@ -237,7 +243,7 @@ export function FilterPopover<T>({
237
243
  type: "number",
238
244
  min: bound === "min" ? read : v.min,
239
245
  max: bound === "max" ? read : v.max,
240
- abs: v.abs,
246
+ abs: absBox && v.abs,
241
247
  });
242
248
  };
243
249
  return (
@@ -266,15 +272,17 @@ export function FilterPopover<T>({
266
272
  />
267
273
  </label>
268
274
  </div>
269
- <label className="flex items-center gap-2 text-sm text-secondary">
270
- <input
271
- type="checkbox"
272
- checked={v.abs}
273
- onChange={(e) => onChange({ type: "number", min: v.min, max: v.max, abs: e.target.checked })}
274
- className="size-3.5"
275
- />
276
- {labels.numberAbs}
277
- </label>
275
+ {absBox && (
276
+ <label className="flex items-center gap-2 text-sm text-secondary">
277
+ <input
278
+ type="checkbox"
279
+ checked={v.abs}
280
+ onChange={(e) => onChange({ type: "number", min: v.min, max: v.max, abs: e.target.checked })}
281
+ className="size-3.5"
282
+ />
283
+ {labels.numberAbs}
284
+ </label>
285
+ )}
278
286
  {clearRow}
279
287
  </div>
280
288
  );