@lerianstudio/sindarian-ui 2.0.0-beta.25 → 2.0.0-beta.27

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 (307) hide show
  1. package/dist/charts/chart.js +4 -231
  2. package/dist/charts/index.js +1 -11
  3. package/dist/components/breadcrumb/get-breadcrumb-paths.js +1 -18
  4. package/dist/components/breadcrumb/index.js +1 -37
  5. package/dist/components/card/account-card/index.js +1 -69
  6. package/dist/components/card/basic-card/index.js +1 -20
  7. package/dist/components/card/dashboard-card/index.js +1 -20
  8. package/dist/components/card/entity-card/index.js +1 -68
  9. package/dist/components/card/index.js +1 -20
  10. package/dist/components/dialog/confirmation-dialog/index.js +1 -96
  11. package/dist/components/dialog/confirmation-dialog/use-confirm-dialog.js +1 -55
  12. package/dist/components/dialog/index.js +1 -19
  13. package/dist/components/dialog/large-dialog/index.js +1 -21
  14. package/dist/components/entity-box/index.js +1 -67
  15. package/dist/components/entity-data-table/index.js +1 -20
  16. package/dist/components/form/combo-box-field/index.js +1 -35
  17. package/dist/components/form/country-field/index.js +1 -11
  18. package/dist/components/form/date-picker-field/index.js +1 -83
  19. package/dist/components/form/date-range-field/index.js +1 -58
  20. package/dist/components/form/date-time-picker-field/index.js +1 -114
  21. package/dist/components/form/file-upload-field/index.js +1 -69
  22. package/dist/components/form/index.js +1 -31
  23. package/dist/components/form/input-field/index.js +1 -96
  24. package/dist/components/form/otp-field/index.js +1 -19
  25. package/dist/components/form/pagination-limit-field/index.js +1 -10
  26. package/dist/components/form/password-field/index.js +1 -16
  27. package/dist/components/form/radio-group-field/index.js +1 -36
  28. package/dist/components/form/renderable-label.js +1 -57
  29. package/dist/components/form/select-field/index.js +1 -68
  30. package/dist/components/form/state-field/index.js +1 -42
  31. package/dist/components/form/switch-field/index.js +1 -16
  32. package/dist/components/form/textarea-field/index.js +1 -23
  33. package/dist/components/page/index.js +1 -20
  34. package/dist/components/page-header/index.js +1 -80
  35. package/dist/components/pagination/index.js +1 -10
  36. package/dist/components/table/id-table-cell.js +1 -109
  37. package/dist/components/table/index.js +1 -19
  38. package/dist/components/table/locked-table-actions.js +1 -41
  39. package/dist/components/table/name-table-cell.js +1 -58
  40. package/dist/components/ui/accordion/index.js +1 -62
  41. package/dist/components/ui/alert/index.js +1 -45
  42. package/dist/components/ui/alert-dialog/index.js +1 -84
  43. package/dist/components/ui/autocomplete/index.js +1 -440
  44. package/dist/components/ui/autosize-textarea/index.js +1 -89
  45. package/dist/components/ui/avatar/index.js +1 -51
  46. package/dist/components/ui/badge/index.js +1 -83
  47. package/dist/components/ui/breadcrumb/index.js +1 -35
  48. package/dist/components/ui/button/index.js +1 -65
  49. package/dist/components/ui/calendar/index.js +1 -118
  50. package/dist/components/ui/card/index.js +1 -32
  51. package/dist/components/ui/checkbox/index.js +1 -44
  52. package/dist/components/ui/collapsible/index.js +1 -50
  53. package/dist/components/ui/command/index.js +1 -95
  54. package/dist/components/ui/copy-field/index.js +1 -163
  55. package/dist/components/ui/dialog/index.js +1 -80
  56. package/dist/components/ui/dialog/styles.css +1 -1
  57. package/dist/components/ui/dropdown-menu/index.js +1 -110
  58. package/dist/components/ui/field/index.js +1 -82
  59. package/dist/components/ui/file-upload/index.js +1 -574
  60. package/dist/components/ui/form.js +1 -162
  61. package/dist/components/ui/hover-card/index.js +1 -51
  62. package/dist/components/ui/icon-button/index.js +1 -35
  63. package/dist/components/ui/input/adornment.js +1 -21
  64. package/dist/components/ui/input/index.js +1 -99
  65. package/dist/components/ui/label/index.js +1 -45
  66. package/dist/components/ui/lerian-wordmark/index.js +1 -7
  67. package/dist/components/ui/loading-button/index.js +1 -9
  68. package/dist/components/ui/multiple-select/index.js +1 -235
  69. package/dist/components/ui/otp-input/index.js +1 -62
  70. package/dist/components/ui/paper/index.js +1 -8
  71. package/dist/components/ui/paper-collapsible/index.js +1 -24
  72. package/dist/components/ui/popover/index.js +1 -55
  73. package/dist/components/ui/progress/index.js +1 -66
  74. package/dist/components/ui/qr-code/index.js +1 -27
  75. package/dist/components/ui/radio-group/index.js +1 -48
  76. package/dist/components/ui/scroll-area/index.js +1 -49
  77. package/dist/components/ui/select/index.js +1 -119
  78. package/dist/components/ui/separator/index.js +1 -43
  79. package/dist/components/ui/sheet/index.js +1 -136
  80. package/dist/components/ui/sidebar/index.js +1 -27
  81. package/dist/components/ui/sidebar/next-router.cjs +1 -119
  82. package/dist/components/ui/sidebar/sidebar-back-button.js +1 -8
  83. package/dist/components/ui/sidebar/sidebar-components.js +1 -23
  84. package/dist/components/ui/sidebar/sidebar-expand-button.js +1 -49
  85. package/dist/components/ui/sidebar/sidebar-item-button.js +1 -21
  86. package/dist/components/ui/sidebar/sidebar-item-collapsible.js +1 -52
  87. package/dist/components/ui/sidebar/sidebar-item-icon-button.js +1 -18
  88. package/dist/components/ui/sidebar/sidebar-item.js +1 -44
  89. package/dist/components/ui/sidebar/sidebar-provider.js +1 -201
  90. package/dist/components/ui/sidebar/sidebar-root.js +1 -130
  91. package/dist/components/ui/sidebar/sidebar-router.js +1 -126
  92. package/dist/components/ui/sidebar/sidebar-trigger.js +1 -57
  93. package/dist/components/ui/skeleton/index.js +1 -8
  94. package/dist/components/ui/stepper/index.js +1 -29
  95. package/dist/components/ui/switch/index.js +1 -43
  96. package/dist/components/ui/table/index.js +1 -56
  97. package/dist/components/ui/tabs/index.js +1 -55
  98. package/dist/components/ui/tabs/styles.css +1 -1
  99. package/dist/components/ui/textarea/index.js +1 -8
  100. package/dist/components/ui/time-picker/index.js +1 -85
  101. package/dist/components/ui/toast/index.js +1 -3
  102. package/dist/components/ui/toast/toaster.js +1 -16
  103. package/dist/components/ui/toggle-group/index.js +1 -156
  104. package/dist/components/ui/tooltip/index.js +1 -81
  105. package/dist/domain/blotter/index.js +1 -20
  106. package/dist/domain/delinquency-aging/aging-buckets.js +1 -120
  107. package/dist/domain/delinquency-aging/index.js +1 -222
  108. package/dist/domain/figure/index.js +1 -19
  109. package/dist/domain/format.js +1 -137
  110. package/dist/domain/index.js +1 -43
  111. package/dist/domain/key-id/index.js +1 -174
  112. package/dist/domain/ledger-sheet/index.js +1 -28
  113. package/dist/domain/money-diff.js +1 -48
  114. package/dist/domain/money-math.js +1 -104
  115. package/dist/domain/money-text/index.js +1 -123
  116. package/dist/domain/section-label/index.js +1 -17
  117. package/dist/domain/status-rail/index.js +1 -76
  118. package/dist/domain/threshold-gauge/index.js +1 -161
  119. package/dist/enterprise/alert-banner/index.js +1 -24
  120. package/dist/enterprise/app-shell/index.js +1 -87
  121. package/dist/enterprise/cursor-pager/index.js +1 -26
  122. package/dist/enterprise/data-table/column-size.js +1 -103
  123. package/dist/enterprise/data-table/index.js +1 -345
  124. package/dist/enterprise/date-range-picker/index.js +1 -115
  125. package/dist/enterprise/detail-panel/index.js +1 -20
  126. package/dist/enterprise/empty-state/index.js +1 -14
  127. package/dist/enterprise/index.js +1 -39
  128. package/dist/enterprise/number-input/index.js +1 -252
  129. package/dist/enterprise/search-input/index.js +1 -121
  130. package/dist/enterprise/stat-card/index.js +1 -75
  131. package/dist/enterprise/status-badge/index.js +1 -120
  132. package/dist/enterprise/use-is-mobile.js +1 -68
  133. package/dist/enterprise/virtualized-table/index.js +1 -141
  134. package/dist/esm/charts/chart.js +4 -190
  135. package/dist/esm/charts/index.js +1 -3
  136. package/dist/esm/components/breadcrumb/get-breadcrumb-paths.js +1 -12
  137. package/dist/esm/components/breadcrumb/index.js +1 -30
  138. package/dist/esm/components/card/account-card/index.js +1 -53
  139. package/dist/esm/components/card/basic-card/index.js +1 -14
  140. package/dist/esm/components/card/dashboard-card/index.js +1 -14
  141. package/dist/esm/components/card/entity-card/index.js +1 -52
  142. package/dist/esm/components/card/index.js +1 -4
  143. package/dist/esm/components/dialog/confirmation-dialog/index.js +1 -60
  144. package/dist/esm/components/dialog/confirmation-dialog/use-confirm-dialog.js +1 -49
  145. package/dist/esm/components/dialog/index.js +1 -3
  146. package/dist/esm/components/dialog/large-dialog/index.js +1 -16
  147. package/dist/esm/components/entity-box/index.js +1 -56
  148. package/dist/esm/components/entity-data-table/index.js +1 -17
  149. package/dist/esm/components/form/combo-box-field/index.js +1 -28
  150. package/dist/esm/components/form/country-field/index.js +1 -7
  151. package/dist/esm/components/form/date-picker-field/index.js +1 -43
  152. package/dist/esm/components/form/date-range-field/index.js +1 -18
  153. package/dist/esm/components/form/date-time-picker-field/index.js +1 -74
  154. package/dist/esm/components/form/file-upload-field/index.js +1 -65
  155. package/dist/esm/components/form/index.js +1 -15
  156. package/dist/esm/components/form/input-field/index.js +1 -92
  157. package/dist/esm/components/form/otp-field/index.js +1 -15
  158. package/dist/esm/components/form/pagination-limit-field/index.js +1 -6
  159. package/dist/esm/components/form/password-field/index.js +1 -10
  160. package/dist/esm/components/form/radio-group-field/index.js +1 -32
  161. package/dist/esm/components/form/renderable-label.js +1 -54
  162. package/dist/esm/components/form/select-field/index.js +1 -64
  163. package/dist/esm/components/form/state-field/index.js +1 -35
  164. package/dist/esm/components/form/switch-field/index.js +1 -12
  165. package/dist/esm/components/form/textarea-field/index.js +1 -19
  166. package/dist/esm/components/page/index.js +1 -15
  167. package/dist/esm/components/page-header/index.js +1 -71
  168. package/dist/esm/components/pagination/index.js +1 -6
  169. package/dist/esm/components/table/id-table-cell.js +1 -105
  170. package/dist/esm/components/table/index.js +1 -3
  171. package/dist/esm/components/table/locked-table-actions.js +1 -37
  172. package/dist/esm/components/table/name-table-cell.js +1 -54
  173. package/dist/esm/components/ui/accordion/index.js +1 -24
  174. package/dist/esm/components/ui/alert/index.js +1 -38
  175. package/dist/esm/components/ui/alert-dialog/index.js +1 -39
  176. package/dist/esm/components/ui/autocomplete/index.js +1 -392
  177. package/dist/esm/components/ui/autosize-textarea/index.js +1 -51
  178. package/dist/esm/components/ui/avatar/index.js +1 -14
  179. package/dist/esm/components/ui/badge/index.js +1 -79
  180. package/dist/esm/components/ui/breadcrumb/index.js +1 -27
  181. package/dist/esm/components/ui/button/index.js +1 -60
  182. package/dist/esm/components/ui/calendar/index.js +1 -82
  183. package/dist/esm/components/ui/card/index.js +1 -24
  184. package/dist/esm/components/ui/checkbox/index.js +1 -9
  185. package/dist/esm/components/ui/collapsible/index.js +1 -13
  186. package/dist/esm/components/ui/command/index.js +1 -51
  187. package/dist/esm/components/ui/copy-field/index.js +1 -127
  188. package/dist/esm/components/ui/dialog/index.js +1 -36
  189. package/dist/esm/components/ui/dropdown-menu/index.js +1 -60
  190. package/dist/esm/components/ui/field/index.js +1 -71
  191. package/dist/esm/components/ui/file-upload/index.js +1 -536
  192. package/dist/esm/components/ui/form.js +1 -118
  193. package/dist/esm/components/ui/hover-card/index.js +1 -14
  194. package/dist/esm/components/ui/icon-button/index.js +1 -31
  195. package/dist/esm/components/ui/input/adornment.js +1 -17
  196. package/dist/esm/components/ui/input/index.js +1 -60
  197. package/dist/esm/components/ui/label/index.js +1 -10
  198. package/dist/esm/components/ui/lerian-wordmark/index.js +1 -4
  199. package/dist/esm/components/ui/loading-button/index.js +1 -7
  200. package/dist/esm/components/ui/multiple-select/index.js +1 -196
  201. package/dist/esm/components/ui/otp-input/index.js +1 -21
  202. package/dist/esm/components/ui/paper/index.js +1 -5
  203. package/dist/esm/components/ui/paper-collapsible/index.js +1 -19
  204. package/dist/esm/components/ui/popover/index.js +1 -17
  205. package/dist/esm/components/ui/progress/index.js +1 -31
  206. package/dist/esm/components/ui/qr-code/index.js +1 -24
  207. package/dist/esm/components/ui/radio-group/index.js +1 -12
  208. package/dist/esm/components/ui/scroll-area/index.js +1 -13
  209. package/dist/esm/components/ui/select/index.js +1 -74
  210. package/dist/esm/components/ui/separator/index.js +1 -8
  211. package/dist/esm/components/ui/sheet/index.js +1 -92
  212. package/dist/esm/components/ui/sidebar/index.js +1 -11
  213. package/dist/esm/components/ui/sidebar/next-router.cjs +1 -119
  214. package/dist/esm/components/ui/sidebar/sidebar-back-button.js +1 -5
  215. package/dist/esm/components/ui/sidebar/sidebar-components.js +1 -15
  216. package/dist/esm/components/ui/sidebar/sidebar-expand-button.js +1 -42
  217. package/dist/esm/components/ui/sidebar/sidebar-item-button.js +1 -18
  218. package/dist/esm/components/ui/sidebar/sidebar-item-collapsible.js +1 -44
  219. package/dist/esm/components/ui/sidebar/sidebar-item-icon-button.js +1 -14
  220. package/dist/esm/components/ui/sidebar/sidebar-item.js +1 -40
  221. package/dist/esm/components/ui/sidebar/sidebar-provider.js +1 -193
  222. package/dist/esm/components/ui/sidebar/sidebar-root.js +1 -126
  223. package/dist/esm/components/ui/sidebar/sidebar-router.js +1 -89
  224. package/dist/esm/components/ui/sidebar/sidebar-trigger.js +1 -53
  225. package/dist/esm/components/ui/skeleton/index.js +1 -6
  226. package/dist/esm/components/ui/stepper/index.js +1 -20
  227. package/dist/esm/components/ui/switch/index.js +1 -8
  228. package/dist/esm/components/ui/table/index.js +1 -44
  229. package/dist/esm/components/ui/tabs/index.js +1 -17
  230. package/dist/esm/components/ui/textarea/index.js +1 -6
  231. package/dist/esm/components/ui/time-picker/index.js +1 -49
  232. package/dist/esm/components/ui/toast/index.js +1 -2
  233. package/dist/esm/components/ui/toast/toaster.js +1 -13
  234. package/dist/esm/components/ui/toggle-group/index.js +1 -120
  235. package/dist/esm/components/ui/tooltip/index.js +1 -43
  236. package/dist/esm/domain/blotter/index.js +1 -16
  237. package/dist/esm/domain/delinquency-aging/aging-buckets.js +1 -115
  238. package/dist/esm/domain/delinquency-aging/index.js +1 -218
  239. package/dist/esm/domain/figure/index.js +1 -15
  240. package/dist/esm/domain/format.js +1 -128
  241. package/dist/esm/domain/index.js +1 -19
  242. package/dist/esm/domain/key-id/index.js +1 -169
  243. package/dist/esm/domain/ledger-sheet/index.js +1 -24
  244. package/dist/esm/domain/money-diff.js +1 -45
  245. package/dist/esm/domain/money-math.js +1 -98
  246. package/dist/esm/domain/money-text/index.js +1 -118
  247. package/dist/esm/domain/section-label/index.js +1 -14
  248. package/dist/esm/domain/status-rail/index.js +1 -71
  249. package/dist/esm/domain/threshold-gauge/index.js +1 -157
  250. package/dist/esm/enterprise/alert-banner/index.js +1 -21
  251. package/dist/esm/enterprise/app-shell/index.js +1 -51
  252. package/dist/esm/enterprise/cursor-pager/index.js +1 -23
  253. package/dist/esm/enterprise/data-table/column-size.js +1 -97
  254. package/dist/esm/enterprise/data-table/index.js +1 -342
  255. package/dist/esm/enterprise/date-range-picker/index.js +1 -112
  256. package/dist/esm/enterprise/detail-panel/index.js +1 -17
  257. package/dist/esm/enterprise/empty-state/index.js +1 -11
  258. package/dist/esm/enterprise/index.js +1 -22
  259. package/dist/esm/enterprise/number-input/index.js +1 -214
  260. package/dist/esm/enterprise/search-input/index.js +1 -85
  261. package/dist/esm/enterprise/stat-card/index.js +1 -72
  262. package/dist/esm/enterprise/status-badge/index.js +1 -116
  263. package/dist/esm/enterprise/use-is-mobile.js +1 -31
  264. package/dist/esm/enterprise/virtualized-table/index.js +1 -138
  265. package/dist/esm/hooks/use-click-away.js +1 -22
  266. package/dist/esm/hooks/use-pagination.js +1 -38
  267. package/dist/esm/hooks/use-stepper.js +1 -22
  268. package/dist/esm/hooks/use-time.js +1 -13
  269. package/dist/esm/hooks/use-toast.js +1 -107
  270. package/dist/esm/index.js +1 -77
  271. package/dist/esm/lib/storage/get-storage-object.js +1 -17
  272. package/dist/esm/lib/storage/get-storage.js +1 -14
  273. package/dist/esm/lib/storage/index.js +1 -2
  274. package/dist/esm/lib/typography.js +1 -31
  275. package/dist/esm/lib/utils.js +1 -41
  276. package/dist/esm/theme/get-theme-script.js +1 -41
  277. package/dist/esm/theme/index.js +1 -4
  278. package/dist/esm/theme/mode-toggle.js +1 -45
  279. package/dist/esm/theme/theme-provider.js +1 -122
  280. package/dist/esm/toast/helpers.js +1 -36
  281. package/dist/esm/toast/index.js +1 -4
  282. package/dist/esm/types/metadata-type.js +1 -1
  283. package/dist/esm/utils/countries.cjs +1 -17
  284. package/dist/esm/utils/country-utils.js +1 -18
  285. package/dist/esm/utils/currency-codes.js +1 -181
  286. package/dist/hooks/use-click-away.js +1 -29
  287. package/dist/hooks/use-pagination.js +1 -41
  288. package/dist/hooks/use-stepper.js +1 -26
  289. package/dist/hooks/use-time.js +1 -20
  290. package/dist/hooks/use-toast.js +1 -111
  291. package/dist/index.js +1 -96
  292. package/dist/lib/storage/get-storage-object.js +1 -23
  293. package/dist/lib/storage/get-storage.js +1 -17
  294. package/dist/lib/storage/index.js +1 -18
  295. package/dist/lib/typography.js +1 -34
  296. package/dist/lib/utils.js +1 -45
  297. package/dist/theme/get-theme-script.js +1 -44
  298. package/dist/theme/index.js +1 -11
  299. package/dist/theme/mode-toggle.js +1 -48
  300. package/dist/theme/theme-provider.js +1 -160
  301. package/dist/toast/helpers.js +1 -41
  302. package/dist/toast/index.js +1 -10
  303. package/dist/types/metadata-type.js +1 -2
  304. package/dist/utils/countries.cjs +1 -17
  305. package/dist/utils/country-utils.js +1 -25
  306. package/dist/utils/currency-codes.js +1 -184
  307. package/package.json +4 -3
@@ -1,6 +1 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cn } from '../../../lib/utils.js';
3
- function Textarea({ className, ...props }) {
4
- return (_jsx("textarea", { className: cn('border-input-border aria-invalid:border-destructive bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex w-full rounded-md border px-3 py-2 text-base focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden', 'read-only:bg-muted read-only:cursor-default read-only:caret-transparent read-only:opacity-50 read-only:select-text read-only:focus:ring-0 read-only:focus:ring-offset-0 read-only:focus:outline-hidden', 'disabled:cursor-not-allowed disabled:opacity-50', className), ...props }));
5
- }
6
- export { Textarea };
1
+ import{jsx as o}from"react/jsx-runtime";import{cn as d}from"../../../lib/utils.js";function n({className:e,...r}){return o("textarea",{className:d("border-input-border aria-invalid:border-destructive bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex w-full rounded-md border px-3 py-2 text-base focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden","read-only:bg-muted read-only:cursor-default read-only:caret-transparent read-only:opacity-50 read-only:select-text read-only:focus:ring-0 read-only:focus:ring-offset-0 read-only:focus:outline-hidden","disabled:cursor-not-allowed disabled:opacity-50",e),...r})}export{n as Textarea};
@@ -1,49 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import * as React from 'react';
4
- import { cn } from '../../../lib/utils.js';
5
- export const HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'));
6
- export const MINUTES = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, '0'));
7
- function TimeScrollColumn({ items, selected, onSelect, label, disabled }) {
8
- const listRef = React.useRef(null);
9
- const hasScrolled = React.useRef(false);
10
- const prevSelected = React.useRef(selected);
11
- React.useEffect(() => {
12
- if (prevSelected.current !== selected) {
13
- hasScrolled.current = false;
14
- prevSelected.current = selected;
15
- }
16
- }, [selected]);
17
- React.useEffect(() => {
18
- if (hasScrolled.current)
19
- return;
20
- if (!listRef.current || !selected)
21
- return;
22
- const selectedIndex = items.indexOf(selected);
23
- if (selectedIndex < 0)
24
- return;
25
- const selectedElement = listRef.current.children[0]?.children[selectedIndex];
26
- if (selectedElement) {
27
- selectedElement.scrollIntoView({ block: 'nearest' });
28
- hasScrolled.current = true;
29
- }
30
- }, [selected, items]);
31
- return (_jsxs("div", { className: "flex min-h-0 min-w-0 flex-1 flex-col items-center", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium select-none", children: label }), _jsx("div", { className: "h-1.5" }), _jsx("div", { ref: listRef, className: "max-h-60 overflow-y-auto", role: "group", "aria-label": label, children: _jsx("div", { className: "flex flex-col gap-1 p-1", children: items.map((item) => {
32
- const isSelected = item === selected;
33
- return (_jsx("button", { type: "button", "aria-pressed": isSelected, disabled: disabled, className: cn('flex h-8 w-11 items-center justify-center rounded-md text-sm tabular-nums transition-colors', isSelected
34
- ? 'bg-accent text-accent-foreground font-bold'
35
- : 'text-muted-foreground hover:bg-muted font-normal', disabled && 'pointer-events-none opacity-50'), onClick: () => onSelect(item), children: item }, item));
36
- }) }) })] }));
37
- }
38
- function TimePicker({ hour, minute, onChange, disabled, className, hourLabel = 'Hour', minuteLabel = 'Minute' }) {
39
- const selectedHour = String(hour ?? 0).padStart(2, '0');
40
- const selectedMinute = String(minute ?? 0).padStart(2, '0');
41
- const handleHourSelect = React.useCallback((value) => {
42
- onChange?.({ hour: parseInt(value, 10), minute: minute ?? 0 });
43
- }, [onChange, minute]);
44
- const handleMinuteSelect = React.useCallback((value) => {
45
- onChange?.({ hour: hour ?? 0, minute: parseInt(value, 10) });
46
- }, [onChange, hour]);
47
- return (_jsxs("div", { "data-slot": "time-picker", className: cn('flex gap-2 p-3', className), children: [_jsx(TimeScrollColumn, { items: HOURS, selected: selectedHour, onSelect: handleHourSelect, label: hourLabel, disabled: disabled }), _jsx(TimeScrollColumn, { items: MINUTES, selected: selectedMinute, onSelect: handleMinuteSelect, label: minuteLabel, disabled: disabled })] }));
48
- }
49
- export { TimePicker };
1
+ "use client";import{jsx as c,jsxs as m}from"react/jsx-runtime";import*as l from"react";import{cn as p}from"../../../lib/utils.js";export const HOURS=Array.from({length:24},(t,e)=>String(e).padStart(2,"0"));export const MINUTES=Array.from({length:60},(t,e)=>String(e).padStart(2,"0"));function x({items:t,selected:e,onSelect:s,label:o,disabled:u}){const a=l.useRef(null);const i=l.useRef(false);const f=l.useRef(e);l.useEffect(()=>{if(f.current!==e){i.current=false;f.current=e}},[e]);l.useEffect(()=>{if(i.current)return;if(!a.current||!e)return;const r=t.indexOf(e);if(r<0)return;const n=a.current.children[0]?.children[r];if(n){n.scrollIntoView({block:"nearest"});i.current=true}},[e,t]);return m("div",{className:"flex min-h-0 min-w-0 flex-1 flex-col items-center",children:[c("span",{className:"text-muted-foreground text-xs font-medium select-none",children:o}),c("div",{className:"h-1.5"}),c("div",{ref:a,className:"max-h-60 overflow-y-auto",role:"group","aria-label":o,children:c("div",{className:"flex flex-col gap-1 p-1",children:t.map(r=>{const n=r===e;return c("button",{type:"button","aria-pressed":n,disabled:u,className:p("flex h-8 w-11 items-center justify-center rounded-md text-sm tabular-nums transition-colors",n?"bg-accent text-accent-foreground font-bold":"text-muted-foreground hover:bg-muted font-normal",u&&"pointer-events-none opacity-50"),onClick:()=>s(r),children:r},r)})})})]})}function h({hour:t,minute:e,onChange:s,disabled:o,className:u,hourLabel:a="Hour",minuteLabel:i="Minute"}){const f=String(t??0).padStart(2,"0");const r=String(e??0).padStart(2,"0");const n=l.useCallback(d=>{s?.({hour:parseInt(d,10),minute:e??0})},[s,e]);const S=l.useCallback(d=>{s?.({hour:t??0,minute:parseInt(d,10)})},[s,t]);return m("div",{"data-slot":"time-picker",className:p("flex gap-2 p-3",u),children:[c(x,{items:HOURS,selected:f,onSelect:n,label:a,disabled:o}),c(x,{items:MINUTES,selected:r,onSelect:S,label:i,disabled:o})]})}export{h as TimePicker};
@@ -1,2 +1 @@
1
- 'use client';
2
- export {};
1
+ "use client";export{};
@@ -1,13 +1 @@
1
- 'use client';
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import { Toaster as SonnerToaster } from 'sonner';
4
- import { DEFAULT_DURATION } from '../../../hooks/use-toast.js';
5
- import { useOptionalTheme } from '../../../theme/theme-provider.js';
6
- export function Toaster({ theme, position = 'bottom-right' }) {
7
- // Optional read: a Toaster mounted outside any ThemeProvider must still
8
- // render, so this cannot go through `useTheme`, which throws.
9
- const themeContext = useOptionalTheme();
10
- return (_jsx(SonnerToaster, { theme: theme ?? themeContext?.resolvedTheme ?? 'system', position: position, visibleToasts: 3, duration: DEFAULT_DURATION, expand: false, closeButton: true, richColors: false, offset: 16, gap: 8, toastOptions: {
11
- className: 'font-sans !text-sm !font-medium'
12
- } }));
13
- }
1
+ "use client";import{jsx as s}from"react/jsx-runtime";import{Toaster as r}from"sonner";import{DEFAULT_DURATION as m}from"../../../hooks/use-toast.js";import{useOptionalTheme as n}from"../../../theme/theme-provider.js";export function Toaster({theme:t,position:o="bottom-right"}){const e=n();return s(r,{theme:t??e?.resolvedTheme??"system",position:o,visibleToasts:3,duration:m,expand:false,closeButton:true,richColors:false,offset:16,gap:8,toastOptions:{className:"font-sans !text-sm !font-medium"}})}
@@ -1,120 +1 @@
1
- 'use client';
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import * as React from 'react';
4
- import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group';
5
- import { cva } from 'class-variance-authority';
6
- import { cn } from '../../../lib/utils.js';
7
- /**
8
- * Item styling for the group. Kept local: sindarian-ui has no standalone
9
- * `Toggle` primitive and the retirement census does not ask for one, so the
10
- * variants live with their only consumer instead of leaking a new export.
11
- */
12
- const toggleVariants = cva("hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
13
- variants: {
14
- variant: {
15
- default: 'bg-transparent',
16
- outline: 'border-input hover:bg-accent hover:text-accent-foreground border bg-transparent shadow-xs'
17
- },
18
- size: {
19
- default: 'h-9 min-w-9 px-2',
20
- sm: 'h-8 min-w-8 px-1.5',
21
- lg: 'h-10 min-w-10 px-2.5'
22
- }
23
- },
24
- defaultVariants: {
25
- variant: 'default',
26
- size: 'default'
27
- }
28
- });
29
- const ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
30
- /**
31
- * Is an arrow key down right now?
32
- *
33
- * This is how an item tells keyboard navigation apart from a pointer press or a
34
- * programmatic focus, and it is the seam Radix uses for the same job in its own
35
- * `RadioGroup`. It works because `RovingFocusGroup` defers the focus move to a
36
- * `setTimeout`: the keydown has already bubbled to `document` and set the flag
37
- * by the time the item receives `focus`.
38
- *
39
- * The distinction is not cosmetic. A pointer press focuses BEFORE the click
40
- * lands, so selecting on every focus would turn one click into
41
- * select-then-deselect and break mouse selection outright.
42
- *
43
- * Returns null when disabled, so `type="multiple"` carries no flag to read.
44
- */
45
- function useArrowKeyPressed(enabled) {
46
- const arrowKeyPressed = React.useRef(false);
47
- React.useEffect(() => {
48
- if (!enabled)
49
- return;
50
- const onKeyDown = (event) => {
51
- if (ARROW_KEYS.includes(event.key))
52
- arrowKeyPressed.current = true;
53
- };
54
- const onKeyUp = () => {
55
- arrowKeyPressed.current = false;
56
- };
57
- document.addEventListener('keydown', onKeyDown);
58
- document.addEventListener('keyup', onKeyUp);
59
- window.addEventListener('blur', onKeyUp);
60
- return () => {
61
- document.removeEventListener('keydown', onKeyDown);
62
- document.removeEventListener('keyup', onKeyUp);
63
- window.removeEventListener('blur', onKeyUp);
64
- // A group unmounted mid-keystroke never sees the keyup.
65
- arrowKeyPressed.current = false;
66
- };
67
- }, [enabled]);
68
- return enabled ? arrowKeyPressed : null;
69
- }
70
- const ToggleGroupContext = React.createContext({
71
- size: 'default',
72
- variant: 'default',
73
- spacing: 0,
74
- arrowKeyPressed: null
75
- });
76
- function ToggleGroup({ className, variant, size, spacing = 0, children, style, ...props }) {
77
- // Read, never destructured: `type` is the discriminant of the Radix props
78
- // union, and pulling it out then spreading it back widens the union so the
79
- // single/multiple props stop resolving.
80
- const arrowKeyPressed = useArrowKeyPressed(props.type === 'single');
81
- return (_jsx(ToggleGroupPrimitive.Root, { "data-slot": "toggle-group", "data-variant": variant, "data-size": size, "data-spacing": spacing,
82
- // Caller style first: `--gap` is ours and must survive, but everything
83
- // else the caller passes has to reach the element.
84
- style: { ...style, '--gap': spacing }, className: cn(
85
- // Legacy carried a `data-[spacing=default]:...:shadow-xs` rule here that
86
- // never matched — `data-spacing` is always a number. Removed rather than
87
- // activated, so the rendered pixels stay identical to sindarian-x.
88
- 'group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md', className), ...props, children: _jsx(ToggleGroupContext.Provider, { value: { variant, size, spacing, arrowKeyPressed }, children: children }) }));
89
- }
90
- function ToggleGroupItem({ className, children, variant, size, onFocus, ...props }) {
91
- const context = React.useContext(ToggleGroupContext);
92
- /**
93
- * Selection follows focus, which is what `role="radio"` promises.
94
- *
95
- * With `type="single"` Radix puts the group on the ARIA radio pattern: the
96
- * root is a `radiogroup`, each item a `radio` with `aria-checked`. Under that
97
- * pattern an arrow key both moves focus and checks the item it lands on.
98
- * Radix only moves focus, so a screen-reader operator used to arrow across
99
- * "radio, not checked, 2 of 3" with nothing ever selected.
100
- *
101
- * `type="multiple"` is a `toolbar` of `aria-pressed` buttons and keeps its
102
- * current behavior, where moving focus without pressing is correct: the flag
103
- * is null there.
104
- *
105
- * Clicking the element rather than writing the value keeps a single
106
- * activation path, so Radix's own toggle and any caller `onClick` both fire
107
- * exactly as they do for a real click.
108
- */
109
- const handleFocus = (event) => {
110
- onFocus?.(event);
111
- if (context.arrowKeyPressed?.current) {
112
- event.currentTarget.click();
113
- }
114
- };
115
- return (_jsx(ToggleGroupPrimitive.Item, { "data-slot": "toggle-group-item", "data-variant": context.variant || variant, "data-size": context.size || size, "data-spacing": context.spacing, className: cn(toggleVariants({
116
- variant: context.variant || variant,
117
- size: context.size || size
118
- }), 'w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10', 'data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l', className), ...props, onFocus: handleFocus, children: children }));
119
- }
120
- export { ToggleGroup, ToggleGroupItem };
1
+ "use client";import{jsx as u}from"react/jsx-runtime";import*as i from"react";import*as l from"@radix-ui/react-toggle-group";import{cva as v}from"class-variance-authority";import{cn as g}from"../../../lib/utils.js";const f=v("hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",{variants:{variant:{default:"bg-transparent",outline:"border-input hover:bg-accent hover:text-accent-foreground border bg-transparent shadow-xs"},size:{default:"h-9 min-w-9 px-2",sm:"h-8 min-w-8 px-1.5",lg:"h-10 min-w-10 px-2.5"}},defaultVariants:{variant:"default",size:"default"}});const m=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"];function w(a){const t=i.useRef(false);i.useEffect(()=>{if(!a)return;const n=o=>{if(m.includes(o.key))t.current=true};const e=()=>{t.current=false};document.addEventListener("keydown",n);document.addEventListener("keyup",e);window.addEventListener("blur",e);return()=>{document.removeEventListener("keydown",n);document.removeEventListener("keyup",e);window.removeEventListener("blur",e);t.current=false}},[a]);return a?t:null}const p=i.createContext({size:"default",variant:"default",spacing:0,arrowKeyPressed:null});function x({className:a,variant:t,size:n,spacing:e=0,children:o,style:s,...r}){const d=w(r.type==="single");return u(l.Root,{"data-slot":"toggle-group","data-variant":t,"data-size":n,"data-spacing":e,style:{...s,"--gap":e},className:g("group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md",a),...r,children:u(p.Provider,{value:{variant:t,size:n,spacing:e,arrowKeyPressed:d},children:o})})}function b({className:a,children:t,variant:n,size:e,onFocus:o,...s}){const r=i.useContext(p);const d=c=>{o?.(c);if(r.arrowKeyPressed?.current){c.currentTarget.click()}};return u(l.Item,{"data-slot":"toggle-group-item","data-variant":r.variant||n,"data-size":r.size||e,"data-spacing":r.spacing,className:g(f({variant:r.variant||n,size:r.size||e}),"w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10","data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l",a),...s,onFocus:d,children:t})}export{x as ToggleGroup,b as ToggleGroupItem};
@@ -1,43 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import * as React from 'react';
4
- import * as TooltipPrimitive from '@radix-ui/react-tooltip';
5
- import { cn } from '../../../lib/utils.js';
6
- function TooltipProvider({ delayDuration = 0, ...props }) {
7
- return (_jsx(TooltipPrimitive.Provider, { "data-slot": "tooltip-provider", delayDuration: delayDuration, ...props }));
8
- }
9
- function Tooltip({ open: _open, onOpenChange, disabled, ...props }) {
10
- const [open, setOpen] = React.useState(_open ?? false);
11
- const handleOpenChange = (open) => {
12
- if (disabled) {
13
- onOpenChange?.(false);
14
- setOpen(false);
15
- return;
16
- }
17
- setOpen(open);
18
- onOpenChange?.(open);
19
- };
20
- return (_jsx(TooltipProvider, { children: _jsx(TooltipPrimitive.Root, { "data-slot": "tooltip", open: open, onOpenChange: handleOpenChange, ...props }) }));
21
- }
22
- function TooltipTrigger({ ...props }) {
23
- return _jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
24
- }
25
- /**
26
- * The fill is a RAW palette step, not a theme token: `--color-shadcn-600` is a
27
- * flat hex, so an inverted tooltip stays #27272A under both themes. The ink has
28
- * to be theme-fixed too, which rules out `--primary-foreground` and
29
- * `--background`: both invert, and both land on 1.00:1 against this fill in
30
- * dark.
31
- *
32
- * base/400 was not unreadable here (5.81:1 on this fill; the 2.56:1 figure that
33
- * class earns elsewhere is against a LIGHT ground, which this is not). It moved
34
- * one step to base/300 because base/400 is banned kit-wide in
35
- * `__tests__/ink-classes.test.ts`: its only safe ground is a fixed-dark fill,
36
- * and that narrow exception had already leaked into eight slots that were not
37
- * one. base/300 reads 10.08:1 here, and keeps this line quieter than the
38
- * `text-white` a consumer puts beside it (`page-header/index.tsx:226`).
39
- */
40
- function TooltipContent({ className, sideOffset = 0, children, ...props }) {
41
- return (_jsx(TooltipPrimitive.Portal, { children: _jsxs(TooltipPrimitive.Content, { "data-slot": "tooltip-content", sideOffset: sideOffset, className: cn('bg-shadcn-600 text-shadcn-300 motion-safe:animate-in fade-in-0 zoom-in-95 motion-safe:data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 rounded-md px-3 py-1.5 text-sm shadow-md', className), ...props, children: [children, _jsx(TooltipPrimitive.Arrow, { className: "bg-shadcn-600 fill-shadcn-600 z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })] }) }));
42
- }
43
- export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
1
+ "use client";import{jsx as e,jsxs as f}from"react/jsx-runtime";import*as p from"react";import*as a from"@radix-ui/react-tooltip";import{cn as c}from"../../../lib/utils.js";function d({delayDuration:t=0,...o}){return e(a.Provider,{"data-slot":"tooltip-provider",delayDuration:t,...o})}function u({open:t,onOpenChange:o,disabled:i,...s}){const[l,n]=p.useState(t??false);const m=r=>{if(i){o?.(false);n(false);return}n(r);o?.(r)};return e(d,{children:e(a.Root,{"data-slot":"tooltip",open:l,onOpenChange:m,...s})})}function h({...t}){return e(a.Trigger,{"data-slot":"tooltip-trigger",...t})}function g({className:t,sideOffset:o=0,children:i,...s}){return e(a.Portal,{children:f(a.Content,{"data-slot":"tooltip-content",sideOffset:o,className:c("bg-shadcn-600 text-shadcn-300 motion-safe:animate-in fade-in-0 zoom-in-95 motion-safe:data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 rounded-md px-3 py-1.5 text-sm shadow-md",t),...s,children:[i,e(a.Arrow,{className:"bg-shadcn-600 fill-shadcn-600 z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]"})]})})}export{u as Tooltip,h as TooltipTrigger,g as TooltipContent,d as TooltipProvider};
@@ -1,16 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Skeleton } from '../../components/ui/skeleton/index.js';
3
- import { LABEL_VOICE_CLASS } from '../../lib/typography.js';
4
- import { cn } from '../../lib/utils.js';
5
- export function Blotter({ children, className }) {
6
- return _jsx("dl", { className: cn('divide-border divide-y', className), children: children });
7
- }
8
- export function BlotterRow({ label, value, valueClassName, className, stacked = false, loading = false }) {
9
- // The skeleton stands in for the value slot's own shape: a full-width prose
10
- // line when stacked, the mono figure's width when inline.
11
- const shownValue = loading ? (_jsx(Skeleton, { className: cn('h-4', stacked ? 'w-full' : 'w-24') })) : (value);
12
- if (stacked) {
13
- return (_jsxs("div", { "aria-busy": loading || undefined, className: cn('hover:bg-body-surface space-y-1 px-2 py-2 transition-colors duration-150 ease-out', className), children: [_jsx("dt", { className: LABEL_VOICE_CLASS, children: label }), _jsx("dd", { className: cn('text-foreground text-sm break-words whitespace-pre-wrap', valueClassName), children: shownValue })] }));
14
- }
15
- return (_jsxs("div", { "aria-busy": loading || undefined, className: cn('hover:bg-body-surface flex items-center justify-between gap-3 px-2 py-2 transition-colors duration-150 ease-out', className), children: [_jsx("dt", { className: LABEL_VOICE_CLASS, children: label }), _jsx("dd", { className: cn('font-mono text-sm font-medium tabular-nums', valueClassName), children: shownValue })] }));
16
- }
1
+ import{jsx as e,jsxs as c}from"react/jsx-runtime";import{Skeleton as m}from"../../components/ui/skeleton/index.js";import{LABEL_VOICE_CLASS as l}from"../../lib/typography.js";import{cn as r}from"../../lib/utils.js";export function Blotter({children:s,className:t}){return e("dl",{className:r("divide-border divide-y",t),children:s})}export function BlotterRow({label:s,value:t,valueClassName:n,className:i,stacked:a=false,loading:o=false}){const d=o?e(m,{className:r("h-4",a?"w-full":"w-24")}):t;if(a){return c("div",{"aria-busy":o||void 0,className:r("hover:bg-body-surface space-y-1 px-2 py-2 transition-colors duration-150 ease-out",i),children:[e("dt",{className:l,children:s}),e("dd",{className:r("text-foreground text-sm break-words whitespace-pre-wrap",n),children:d})]})}return c("div",{"aria-busy":o||void 0,className:r("hover:bg-body-surface flex items-center justify-between gap-3 px-2 py-2 transition-colors duration-150 ease-out",i),children:[e("dt",{className:l,children:s}),e("dd",{className:r("font-mono text-sm font-medium tabular-nums",n),children:d})]})}
@@ -1,115 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /**
3
- * AgingBuckets — an ordered, exhaustive bucketed distribution (the receivables
4
- * aging schedule: current / 1–30 / 31–60 / 61–90 / 90+), each bucket carrying a
5
- * count and a money total.
6
- *
7
- * INTERNAL to `DelinquencyAging`: not re-exported from `src/domain/index.ts`,
8
- * because the port census only lists the composite. It lives here as the
9
- * distribution half of that composite.
10
- *
11
- * INVARIANT: the buckets are ordered current → oldest and are exhaustive. The
12
- * ORDER carries the information — older money is worse money — so the ordinal
13
- * position and its label are the PRIMARY cue; the band glyph + sr-only word are
14
- * the accessible reinforcement (WCAG 1.4.1 — the escalation survives grayscale
15
- * and color-vision deficiency), and the color tint is reinforcement only, never
16
- * the sole carrier of severity. The caller supplies the order; the component
17
- * never reorders, because a sorted-by-amount view would destroy the one fact the
18
- * widget exists to show.
19
- *
20
- * THIRD RAIL (money): a zero bucket renders an explicit `0` — a zero in the 90+
21
- * bucket is information ("nothing slipped that far"), never an omission. When a
22
- * grand total is shown it is summed through `money-math` integer minor units
23
- * (BigInt, at the currency's CLDR scale) — never IEEE-754 — and rendered via
24
- * `MoneyText`. A single unparseable bucket total poisons the sum to NO_VALUE
25
- * rather than silently displaying a wrong number. The SAME currency scale that
26
- * drives the sum is also the print scale handed to `MoneyText`, so the digits on
27
- * screen are the digits that were summed — the printed total can never be a
28
- * re-rounding of the computed one.
29
- *
30
- * Pure display: no directive, no interactivity, server-safe for RSC.
31
- */
32
- import { AlertOctagon, AlertTriangle, CircleDot, Clock } from 'lucide-react';
33
- import { LABEL_VOICE_CLASS } from '../../lib/typography.js';
34
- import { cn } from '../../lib/utils.js';
35
- import { Figure } from '../figure/index.js';
36
- import { formatCount, NO_VALUE } from '../format.js';
37
- import { minorDigitsOf, minorToDecimal, sumMinor } from '../money-math.js';
38
- import { MoneyText } from '../money-text/index.js';
39
- /** Band → glyph + tint + accessible word. The glyph (and the sr-only word) is
40
- * the load-bearing severity cue; the tint only reinforces it, so the
41
- * escalation survives grayscale and color-vision deficiency.
42
- *
43
- * The tint is an INK token (`-h1a`). A load-bearing glyph answers to the 3:1
44
- * floor of SC 1.4.11, and the bare fill token `--system-alert` reads 1.78:1 on
45
- * `--card` and 1.62:1 on `--body-surface` in light — the cue was invisible on
46
- * the row it escalates. */
47
- const BAND = {
48
- current: {
49
- Icon: CircleDot,
50
- tint: 'text-system-success-h1a',
51
- word: 'Em dia'
52
- },
53
- recent: {
54
- Icon: Clock,
55
- tint: 'text-muted-foreground',
56
- word: 'Vencido recentemente'
57
- },
58
- aging: {
59
- Icon: AlertTriangle,
60
- tint: 'text-system-alert-h1a',
61
- word: 'Em atraso'
62
- },
63
- overdue: { Icon: AlertOctagon, tint: 'text-credit', word: 'Vencido' }
64
- };
65
- /**
66
- * Resolve a bucket's severity band from its ORDINAL POSITION, not its amount —
67
- * the order is the information (older = worse). The first bucket is `current`;
68
- * the LAST bucket (the oldest, the 90+ tail) is always `overdue`; the middle
69
- * escalates `recent` → `aging` toward the tail. A single-bucket list reads as
70
- * `current` (there is no escalation to show). Pure: no React, unit-tested.
71
- */
72
- export function bucketBand(index, count) {
73
- if (count <= 1)
74
- return 'current';
75
- if (index === 0)
76
- return 'current';
77
- if (index === count - 1)
78
- return 'overdue';
79
- // Middle buckets: the second-oldest reads `aging`, the rest `recent`.
80
- return index === count - 2 ? 'aging' : 'recent';
81
- }
82
- /**
83
- * Sum the bucket totals through integer minor units (BigInt) at `scale`, then
84
- * render losslessly to a decimal string. Returns null if ANY bucket total is
85
- * unparseable (indeterminate) — the caller renders NO_VALUE rather than a wrong
86
- * grand total. Pure: the money-math third rail, unit-tested.
87
- */
88
- export function sumBucketTotals(buckets, scale) {
89
- const total = sumMinor(buckets.map((b) => b.total), scale);
90
- return total === null ? null : minorToDecimal(total, scale);
91
- }
92
- export function AgingBuckets({ buckets, currency, locale, showTotal = false, labels, healthyTone = 'success', className }) {
93
- const countLabel = labels?.count ?? 'Itens';
94
- const totalLabel = labels?.total ?? 'Total';
95
- const grandTotalLabel = labels?.grandTotal ?? 'Total geral';
96
- // THIRD RAIL: one scale for BOTH the arithmetic and the print. `MoneyText`
97
- // defaults to 2 fraction digits, so leaving it unset printed JPY (scale 0)
98
- // with two decimals the currency does not have, and re-rounded the exact BHD
99
- // (scale 3) grand total `1.005` to `1.01` — a printed total that disagreed
100
- // with the total that was actually summed.
101
- const scale = currency ? minorDigitsOf(currency) : 2;
102
- const grandTotal = showTotal ? sumBucketTotals(buckets, scale) : null;
103
- return (_jsxs("div", { className: cn('flex flex-col', className), children: [_jsx("ul", { className: "flex flex-col", children: buckets.map((bucket, i) => {
104
- const band = bucketBand(i, buckets.length);
105
- const { Icon, word } = BAND[band];
106
- // `ink` retones ONLY the healthy band — the escalated bands keep their
107
- // alert and credit-red tints, which are the whole point of the scale.
108
- const tint = healthyTone === 'ink' && band === 'current'
109
- ? 'text-foreground'
110
- : BAND[band].tint;
111
- // Shallow merge: an unlisted band keeps its pt-BR default.
112
- const bandWord = labels?.bands?.[band] ?? word;
113
- return (_jsxs("li", { className: "border-border flex items-center gap-3 border-b py-2.5 last:border-b-0", children: [_jsx(Icon, { "aria-hidden": true, className: cn('size-4 shrink-0', tint) }), _jsxs("span", { className: "sr-only", children: [bandWord, ": "] }), _jsx("span", { className: cn(LABEL_VOICE_CLASS, 'text-foreground min-w-0 flex-1'), children: bucket.label }), _jsxs("span", { className: "flex shrink-0 flex-col items-end leading-none", children: [_jsx(Figure, { size: "row", className: "text-foreground", children: formatCount(bucket.count, locale) }), _jsx("span", { className: cn(LABEL_VOICE_CLASS, 'mt-1 text-xs'), children: countLabel })] }), _jsxs("span", { className: "flex w-32 shrink-0 flex-col items-end leading-none sm:w-40", children: [_jsx(Figure, { size: "row", className: cn(band === 'overdue' && 'text-credit'), children: _jsx(MoneyText, { amount: bucket.total, currency: currency, fractionDigits: scale, locale: locale, signColor: false }) }), _jsx("span", { className: cn(LABEL_VOICE_CLASS, 'mt-1 text-xs'), children: totalLabel })] })] }, `${bucket.label}-${i}`));
114
- }) }), showTotal ? (_jsxs("div", { className: "border-border flex items-center gap-3 border-t-2 pt-2.5", children: [_jsx("span", { "aria-hidden": true, className: "size-4 shrink-0" }), _jsx("span", { className: cn(LABEL_VOICE_CLASS, 'text-foreground min-w-0 flex-1'), children: grandTotalLabel }), _jsx("span", { className: "w-32 shrink-0 text-right sm:w-40", children: grandTotal === null ? (_jsx(Figure, { size: "row", className: "text-muted-foreground", children: NO_VALUE })) : (_jsx(Figure, { size: "row", className: "text-foreground font-semibold", children: _jsx(MoneyText, { amount: grandTotal, currency: currency, fractionDigits: scale, locale: locale, signColor: false }) })) })] })) : null] }));
115
- }
1
+ import{jsx as e,jsxs as s}from"react/jsx-runtime";import{AlertOctagon as z,AlertTriangle as D,CircleDot as k,Clock as v}from"lucide-react";import{LABEL_VOICE_CLASS as d}from"../../lib/typography.js";import{cn as n}from"../../lib/utils.js";import{Figure as m}from"../figure/index.js";import{formatCount as B,NO_VALUE as E}from"../format.js";import{minorDigitsOf as _,minorToDecimal as j,sumMinor as y}from"../money-math.js";import{MoneyText as g}from"../money-text/index.js";const h={current:{Icon:k,tint:"text-system-success-h1a",word:"Em dia"},recent:{Icon:v,tint:"text-muted-foreground",word:"Vencido recentemente"},aging:{Icon:D,tint:"text-system-alert-h1a",word:"Em atraso"},overdue:{Icon:z,tint:"text-credit",word:"Vencido"}};export function bucketBand(t,r){if(r<=1)return"current";if(t===0)return"current";if(t===r-1)return"overdue";return t===r-2?"aging":"recent"}export function sumBucketTotals(t,r){const o=y(t.map(l=>l.total),r);return o===null?null:j(o,r)}export function AgingBuckets({buckets:t,currency:r,locale:o,showTotal:l=false,labels:i,healthyTone:p="success",className:N}){const w=i?.count??"Itens";const b=i?.total??"Total";const T=i?.grandTotal??"Total geral";const f=r?_(r):2;const u=l?sumBucketTotals(t,f):null;return s("div",{className:n("flex flex-col",N),children:[e("ul",{className:"flex flex-col",children:t.map((c,x)=>{const a=bucketBand(x,t.length);const{Icon:A,word:C}=h[a];const I=p==="ink"&&a==="current"?"text-foreground":h[a].tint;const L=i?.bands?.[a]??C;return s("li",{className:"border-border flex items-center gap-3 border-b py-2.5 last:border-b-0",children:[e(A,{"aria-hidden":true,className:n("size-4 shrink-0",I)}),s("span",{className:"sr-only",children:[L,": "]}),e("span",{className:n(d,"text-foreground min-w-0 flex-1"),children:c.label}),s("span",{className:"flex shrink-0 flex-col items-end leading-none",children:[e(m,{size:"row",className:"text-foreground",children:B(c.count,o)}),e("span",{className:n(d,"mt-1 text-xs"),children:w})]}),s("span",{className:"flex w-32 shrink-0 flex-col items-end leading-none sm:w-40",children:[e(m,{size:"row",className:n(a==="overdue"&&"text-credit"),children:e(g,{amount:c.total,currency:r,fractionDigits:f,locale:o,signColor:false})}),e("span",{className:n(d,"mt-1 text-xs"),children:b})]})]},`${c.label}-${x}`)})}),l?s("div",{className:"border-border flex items-center gap-3 border-t-2 pt-2.5",children:[e("span",{"aria-hidden":true,className:"size-4 shrink-0"}),e("span",{className:n(d,"text-foreground min-w-0 flex-1"),children:T}),e("span",{className:"w-32 shrink-0 text-right sm:w-40",children:u===null?e(m,{size:"row",className:"text-muted-foreground",children:E}):e(m,{size:"row",className:"text-foreground font-semibold",children:e(g,{amount:u,currency:r,fractionDigits:f,locale:o,signColor:false})})})]}):null]})}