@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,120 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_STATUS_VARIANTS = void 0;
4
- exports.StatusBadge = StatusBadge;
5
- const jsx_runtime_1 = require("react/jsx-runtime");
6
- /**
7
- * StatusBadge maps a status/severity string to a Badge variant and a humanized
8
- * label. Unknown values fall back to a neutral "outline" badge with a
9
- * title-cased label, so new server enum values never render raw or crash.
10
- *
11
- * The variant map is injectable (`variantMap`) and shallow-merges over a
12
- * generic lifecycle default, so apps extend it with their own domain statuses
13
- * without forking the component.
14
- *
15
- * INVARIANT (WCAG 1.4.1): the bare badge is color-only — the variant tint is the
16
- * sole carrier of severity. That is a non-color-cue failure for a severity badge
17
- * (a CRITICAL risk band must read as critical in grayscale and to a color-blind
18
- * reader). `withIcon` opts the badge into the non-color canon: a leading
19
- * severity GLYPH plus an sr-only severity word, with the tint as reinforcement
20
- * only. It is opt-in so existing color-only usage is unaffected.
21
- *
22
- * Pure display: no directive, no interactivity. A leading lucide glyph is
23
- * server-safe, so the component stays RSC-safe for the consuming Next.js app.
24
- */
25
- const lucide_react_1 = require("lucide-react");
26
- const badge_1 = require("../../components/ui/badge");
27
- const utils_1 = require("../../lib/utils");
28
- /**
29
- * Generic lifecycle defaults — extend per-app via the `variantMap` prop.
30
- *
31
- * The KEYS are the frozen legacy contract (apps merge their own domain statuses
32
- * into this map). The VALUES are sindarian-ui Badge variants: sindarian-ui is
33
- * senior, so the legacy `muted`/`warning` variants resolve to sindarian-ui's
34
- * `inactive`/`alert`.
35
- */
36
- exports.DEFAULT_STATUS_VARIANTS = {
37
- DRAFT: 'inactive',
38
- ACTIVE: 'success',
39
- ENABLED: 'success',
40
- INACTIVE: 'inactive',
41
- DISABLED: 'inactive',
42
- PAUSED: 'alert',
43
- ARCHIVED: 'inactive',
44
- QUEUED: 'secondary',
45
- PENDING: 'alert',
46
- PROCESSING: 'alert',
47
- RUNNING: 'alert',
48
- COMPLETE: 'success',
49
- COMPLETED: 'success',
50
- SUCCEEDED: 'success',
51
- SUCCESS: 'success',
52
- FAILED: 'destructive',
53
- ERROR: 'destructive',
54
- CANCELED: 'inactive',
55
- CANCELLED: 'inactive',
56
- EXPIRED: 'inactive',
57
- OPEN: 'alert',
58
- RESOLVED: 'success',
59
- REJECTED: 'destructive',
60
- CONFIRMED: 'success',
61
- LOW: 'inactive',
62
- MEDIUM: 'secondary',
63
- HIGH: 'alert',
64
- CRITICAL: 'destructive'
65
- };
66
- /**
67
- * Variant → non-color severity cue: a distinct glyph + an sr-only severity word.
68
- * This is the load-bearing cue when `withIcon` is set; the Badge's own tint only
69
- * reinforces it, so severity survives grayscale and color-vision deficiency.
70
- *
71
- * A constant lookup keyed on the Badge variant — the variant already encodes the
72
- * severity, so the glyph follows the variant rather than re-deriving severity
73
- * from the raw status. `secondary` is a neutral bucket (e.g. MEDIUM/QUEUED) and,
74
- * like `inactive`, carries the calm dash glyph. The pure color-carrier variants
75
- * and the unknown `outline` fallback have no severity semantics, so they render
76
- * no glyph.
77
- */
78
- const SEVERITY_CUE = {
79
- success: { Icon: lucide_react_1.Check, word: 'OK' },
80
- alert: { Icon: lucide_react_1.TriangleAlert, word: 'Warning' },
81
- destructive: { Icon: lucide_react_1.OctagonX, word: 'Critical' },
82
- inactive: { Icon: lucide_react_1.Minus, word: 'Neutral' },
83
- secondary: { Icon: lucide_react_1.Minus, word: 'Neutral' }
84
- };
85
- /**
86
- * Merge a caller's map over the defaults, upper-casing its KEYS.
87
- *
88
- * Lookup has always upper-cased the incoming `status`, but sindarian-x merged
89
- * `variantMap` verbatim — so `{ reconciled: 'success' }` could never match and
90
- * fell through to the neutral `outline` badge, silently. That was an unstated
91
- * requirement rather than a design choice: lender's own call sites work around
92
- * it by hand (`variantMap={{ [reason.toUpperCase()]: 'destructive' }}`).
93
- * Normalising here is strictly additive — already-uppercase keys are unchanged.
94
- */
95
- function mergeVariants(variantMap) {
96
- if (!variantMap)
97
- return exports.DEFAULT_STATUS_VARIANTS;
98
- const merged = { ...exports.DEFAULT_STATUS_VARIANTS };
99
- for (const [key, variant] of Object.entries(variantMap)) {
100
- merged[key.toUpperCase()] = variant;
101
- }
102
- return merged;
103
- }
104
- function humanize(value) {
105
- return value
106
- .toLowerCase()
107
- .split(/[_\s]+/)
108
- .map((w) => (w[0] ? w[0].toUpperCase() + w.slice(1) : w))
109
- .join(' ');
110
- }
111
- function StatusBadge({ status, label, variantMap, withIcon = false, unknownLabel = 'Unknown', className }) {
112
- if (!status) {
113
- return ((0, jsx_runtime_1.jsx)(badge_1.Badge, { variant: "outline", className: (0, utils_1.cn)('text-muted-foreground', className), children: label ?? unknownLabel }));
114
- }
115
- const variant = mergeVariants(variantMap)[status.toUpperCase()] ?? 'outline';
116
- // The glyph follows the resolved variant; an unknown/non-severity variant has
117
- // no cue, so the badge gracefully renders label-only even with withIcon set.
118
- const cue = withIcon ? SEVERITY_CUE[variant] : undefined;
119
- return ((0, jsx_runtime_1.jsxs)(badge_1.Badge, { variant: variant, className: className, children: [cue ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(cue.Icon, { "aria-hidden": true, className: "size-3.5 shrink-0" }), (0, jsx_runtime_1.jsxs)("span", { className: "sr-only", children: [cue.word, ": "] })] })) : null, label ?? humanize(status)] }));
120
- }
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.DEFAULT_STATUS_VARIANTS=void 0;exports.StatusBadge=StatusBadge;const jsx_runtime_1=require("react/jsx-runtime");const lucide_react_1=require("lucide-react");const badge_1=require("../../components/ui/badge");const utils_1=require("../../lib/utils");exports.DEFAULT_STATUS_VARIANTS={DRAFT:"inactive",ACTIVE:"success",ENABLED:"success",INACTIVE:"inactive",DISABLED:"inactive",PAUSED:"alert",ARCHIVED:"inactive",QUEUED:"secondary",PENDING:"alert",PROCESSING:"alert",RUNNING:"alert",COMPLETE:"success",COMPLETED:"success",SUCCEEDED:"success",SUCCESS:"success",FAILED:"destructive",ERROR:"destructive",CANCELED:"inactive",CANCELLED:"inactive",EXPIRED:"inactive",OPEN:"alert",RESOLVED:"success",REJECTED:"destructive",CONFIRMED:"success",LOW:"inactive",MEDIUM:"secondary",HIGH:"alert",CRITICAL:"destructive"};const SEVERITY_CUE={success:{Icon:lucide_react_1.Check,word:"OK"},alert:{Icon:lucide_react_1.TriangleAlert,word:"Warning"},destructive:{Icon:lucide_react_1.OctagonX,word:"Critical"},inactive:{Icon:lucide_react_1.Minus,word:"Neutral"},secondary:{Icon:lucide_react_1.Minus,word:"Neutral"}};function mergeVariants(s){if(!s)return exports.DEFAULT_STATUS_VARIANTS;const e={...exports.DEFAULT_STATUS_VARIANTS};for(const[t,r]of Object.entries(s)){e[t.toUpperCase()]=r}return e}function humanize(s){return s.toLowerCase().split(/[_\s]+/).map(e=>e[0]?e[0].toUpperCase()+e.slice(1):e).join(" ")}function StatusBadge({status:s,label:e,variantMap:t,withIcon:r=false,unknownLabel:a="Unknown",className:n}){if(!s){return(0,jsx_runtime_1.jsx)(badge_1.Badge,{variant:"outline",className:(0,utils_1.cn)("text-muted-foreground",n),children:e??a})}const i=mergeVariants(t)[s.toUpperCase()]??"outline";const c=r?SEVERITY_CUE[i]:void 0;return(0,jsx_runtime_1.jsxs)(badge_1.Badge,{variant:i,className:n,children:[c?(0,jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment,{children:[(0,jsx_runtime_1.jsx)(c.Icon,{"aria-hidden":true,className:"size-3.5 shrink-0"}),(0,jsx_runtime_1.jsxs)("span",{className:"sr-only",children:[c.word,": "]})]}):null,e??humanize(s)]})}
@@ -1,68 +1 @@
1
- 'use client';
2
- "use strict";
3
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k;
5
- var desc = Object.getOwnPropertyDescriptor(m, k);
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k]; } };
8
- }
9
- Object.defineProperty(o, k2, desc);
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- o[k2] = m[k];
13
- }));
14
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
- Object.defineProperty(o, "default", { enumerable: true, value: v });
16
- }) : function(o, v) {
17
- o["default"] = v;
18
- });
19
- var __importStar = (this && this.__importStar) || (function () {
20
- var ownKeys = function(o) {
21
- ownKeys = Object.getOwnPropertyNames || function (o) {
22
- var ar = [];
23
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
- return ar;
25
- };
26
- return ownKeys(o);
27
- };
28
- return function (mod) {
29
- if (mod && mod.__esModule) return mod;
30
- var result = {};
31
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
- __setModuleDefault(result, mod);
33
- return result;
34
- };
35
- })();
36
- Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.MOBILE_BREAKPOINT = void 0;
38
- exports.useIsMobile = useIsMobile;
39
- const React = __importStar(require("react"));
40
- /** The one breakpoint the hook subscribes at. Exported so its tests can bind to
41
- * it instead of re-stating the number. */
42
- exports.MOBILE_BREAKPOINT = 768;
43
- /**
44
- * True while the viewport is narrower than the 768px mobile breakpoint.
45
- *
46
- * Always false on the FIRST render, server and client alike, so a narrow
47
- * viewport can never produce a hydration mismatch. The correction lands in a
48
- * `useEffect`, which runs AFTER the browser paints: on a narrow viewport the
49
- * first painted frame therefore shows the desktop branch and the mobile branch
50
- * appears on the next frame. That one frame is the deliberate price of
51
- * hydration safety — a layout effect would remove it but would also change the
52
- * render timing every existing consumer is built on.
53
- *
54
- * Every reading comes from the media query itself rather than a mix of
55
- * `matchMedia` and `window.innerWidth`, so the initial value and the change
56
- * events can never disagree at the breakpoint boundary.
57
- */
58
- function useIsMobile() {
59
- const [isMobile, setIsMobile] = React.useState(false);
60
- React.useEffect(() => {
61
- const mql = window.matchMedia(`(max-width: ${exports.MOBILE_BREAKPOINT - 1}px)`);
62
- const onChange = () => setIsMobile(mql.matches);
63
- onChange();
64
- mql.addEventListener('change', onChange);
65
- return () => mql.removeEventListener('change', onChange);
66
- }, []);
67
- return isMobile;
68
- }
1
+ "use client";"use strict";var __createBinding=this&&this.__createBinding||(Object.create?(function(i,e,r,t){if(t===void 0)t=r;var n=Object.getOwnPropertyDescriptor(e,r);if(!n||("get"in n?!e.__esModule:n.writable||n.configurable)){n={enumerable:true,get:function(){return e[r]}}}Object.defineProperty(i,t,n)}):(function(i,e,r,t){if(t===void 0)t=r;i[t]=e[r]}));var __setModuleDefault=this&&this.__setModuleDefault||(Object.create?(function(i,e){Object.defineProperty(i,"default",{enumerable:true,value:e})}):function(i,e){i["default"]=e});var __importStar=this&&this.__importStar||(function(){var i=function(e){i=Object.getOwnPropertyNames||function(r){var t=[];for(var n in r)if(Object.prototype.hasOwnProperty.call(r,n))t[t.length]=n;return t};return i(e)};return function(e){if(e&&e.__esModule)return e;var r={};if(e!=null){for(var t=i(e),n=0;n<t.length;n++)if(t[n]!=="default")__createBinding(r,e,t[n])}__setModuleDefault(r,e);return r}})();Object.defineProperty(exports,"__esModule",{value:true});exports.MOBILE_BREAKPOINT=void 0;exports.useIsMobile=useIsMobile;const React=__importStar(require("react"));exports.MOBILE_BREAKPOINT=768;function useIsMobile(){const[i,e]=React.useState(false);React.useEffect(()=>{const r=window.matchMedia(`(max-width: ${exports.MOBILE_BREAKPOINT-1}px)`);const t=()=>e(r.matches);t();r.addEventListener("change",t);return()=>r.removeEventListener("change",t)},[]);return i}
@@ -1,141 +1 @@
1
- 'use client';
2
- "use strict";
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.VirtualizedTable = VirtualizedTable;
5
- const jsx_runtime_1 = require("react/jsx-runtime");
6
- /**
7
- * VirtualizedTable — a windowed table for very large, flat datasets.
8
- *
9
- * Mirrors DataTable's column/data API (standard TanStack `ColumnDef<T>`, cells
10
- * receive the row's `original`), so screens reuse the exact same column
11
- * definitions. The difference is the rendering strategy: where DataTable
12
- * renders every row, VirtualizedTable renders only the rows inside (and just
13
- * past) the scroll viewport via `@tanstack/react-virtual`. Tens of thousands of
14
- * rows stay scroll-smooth because the DOM only ever holds the visible window.
15
- *
16
- * Layout is div-based (role="table"/"row"/"cell"), not a real `<table>`:
17
- * absolute positioning by `virtualItem.start` is incompatible with table
18
- * layout, so a windowed table cannot use `<tbody>`. The header is a sibling
19
- * above the scroll region: it stays put by layout (it simply sits above an
20
- * overflow box), not via `position: sticky`. Inside the scroll container an
21
- * inner spacer sized to `getTotalSize()` reserves the full scroll height, and
22
- * each virtual row is absolutely positioned within that spacer.
23
- *
24
- * Rows are fixed-height (= `rowHeight`): cell content taller than `rowHeight`
25
- * will clip — cells use `truncate`. This component is for flat, single-line
26
- * datasets only; it does NOT measure rows, so it is not suitable for
27
- * variable-height content.
28
- *
29
- * This is deliberately leaner than DataTable: no pagination, selection,
30
- * keyboard nav, loading or empty affordances — it is the right tool only when
31
- * the whole dataset is already in memory and the row count is the problem.
32
- * For server-paged lists, reach for DataTable + CursorPager instead.
33
- */
34
- const react_1 = require("react");
35
- const react_table_1 = require("@tanstack/react-table");
36
- const react_virtual_1 = require("@tanstack/react-virtual");
37
- const typography_1 = require("../../lib/typography");
38
- const utils_1 = require("../../lib/utils");
39
- const column_size_1 = require("../data-table/column-size");
40
- /** Rows rendered just outside the viewport to keep fast scrolls smooth. */
41
- const OVERSCAN = 8;
42
- function VirtualizedTable({ columns, data, rowHeight = 40, maxHeight = 480, scrollAreaLabel = 'Scrollable table rows', className, headClassName }) {
43
- const scrollRef = (0, react_1.useRef)(null);
44
- const [isScrollable, setIsScrollable] = (0, react_1.useState)(false);
45
- // A number is px; a string is used verbatim (e.g. '60vh').
46
- const viewportMaxHeight = typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;
47
- const table = (0, react_table_1.useReactTable)({
48
- data,
49
- columns,
50
- // Keeps `columnDef.size`/`minSize`/`maxSize` meaning "declared" — see
51
- // ../data-table/column-size. `getSize()` is unaffected; it falls back to
52
- // the library defaults itself.
53
- defaultColumn: column_size_1.UNSIZED_DEFAULT_COLUMN,
54
- getCoreRowModel: (0, react_table_1.getCoreRowModel)()
55
- });
56
- const rows = table.getRowModel().rows;
57
- // Grouped columns (a ColumnDef with nested `columns`) render one header row
58
- // per depth level, so the header is not always a single row.
59
- const headerGroups = table.getHeaderGroups();
60
- const headerRowCount = headerGroups.length;
61
- // The table's real column geometry is its LEAF columns: a grouped ColumnDef
62
- // contributes one spanning header cell but no column of its own, so counting
63
- // top-level defs both under-reports aria-colcount and misplaces every
64
- // aria-colindex to its right.
65
- const leafColumns = table.getVisibleLeafColumns();
66
- const leafPosition = new Map(leafColumns.map((column, i) => [column.id, i]));
67
- const virtualizer = (0, react_virtual_1.useVirtualizer)({
68
- count: rows.length,
69
- getScrollElement: () => scrollRef.current,
70
- estimateSize: () => rowHeight,
71
- overscan: OVERSCAN
72
- });
73
- const virtualRows = virtualizer.getVirtualItems();
74
- const totalSize = virtualizer.getTotalSize();
75
- // `estimateSize` is read through a closure the virtualizer caches per item, so
76
- // changing rowHeight leaves every already-measured row at its old size until
77
- // the cache is dropped explicitly.
78
- (0, react_1.useEffect)(() => {
79
- virtualizer.measure();
80
- }, [rowHeight, virtualizer]);
81
- (0, react_1.useEffect)(() => {
82
- const viewport = scrollRef.current;
83
- if (!viewport)
84
- return;
85
- const updateScrollability = () => {
86
- const nextIsScrollable = viewport.scrollHeight > viewport.clientHeight ||
87
- viewport.scrollWidth > viewport.clientWidth;
88
- setIsScrollable((current) => current === nextIsScrollable ? current : nextIsScrollable);
89
- };
90
- updateScrollability();
91
- if (typeof ResizeObserver === 'undefined')
92
- return;
93
- const observer = new ResizeObserver(updateScrollability);
94
- observer.observe(viewport);
95
- const content = viewport.firstElementChild;
96
- if (content)
97
- observer.observe(content);
98
- return () => observer.disconnect();
99
- }, [totalSize, viewportMaxHeight]);
100
- return ((0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)('border-border bg-card text-card-foreground rounded-lg border', className), children: (0, jsx_runtime_1.jsxs)("div", { role: "table", "aria-rowcount": rows.length + headerRowCount, "aria-colcount": leafColumns.length, children: [(0, jsx_runtime_1.jsx)("div", { role: "rowgroup", className: "bg-card z-10", children: headerGroups.map((headerGroup, groupIndex) => ((0, jsx_runtime_1.jsx)("div", { role: "row", "aria-rowindex": groupIndex + 1, className: "border-border flex border-b", children: headerGroup.headers.map((header) => {
101
- // Anchor the cell on its leftmost leaf column, so a spanning
102
- // group reports the column it actually starts at rather than
103
- // its position among its siblings.
104
- const firstLeaf = header.column.getLeafColumns()[0];
105
- const start = leafPosition.get(firstLeaf?.id ?? header.column.id);
106
- // A group header spans leaf columns whose body cells carry
107
- // their own widths, so its size comes from those leaves, not
108
- // from the group def (which declares nothing). A leaf header
109
- // reads its own column, as before.
110
- const size = header.subHeaders.length > 0
111
- ? (0, column_size_1.readDeclaredGroupSize)(header.column.getLeafColumns())
112
- : (0, column_size_1.readDeclaredColumnSize)(header.column);
113
- return ((0, jsx_runtime_1.jsx)("div", { role: "columnheader", "aria-colindex": (start ?? 0) + 1, "aria-colspan": header.colSpan > 1 ? header.colSpan : undefined,
114
- // A declared width must not be flexed away; without one the
115
- // cell keeps its colSpan share of the row, as before.
116
- style: size?.width === undefined
117
- ? { flex: `${header.colSpan} 1 0%`, ...size }
118
- : { flex: '0 0 auto', ...size },
119
- // Header height is independent of rowHeight: pinning it to a
120
- // small rowHeight (32) clipped the uppercase label against its
121
- // own padding. Padding sizes it; min-h keeps a stable rule.
122
- className: (0, utils_1.cn)('min-h-10 px-4 py-2.5 text-left', typography_1.LABEL_VOICE_CLASS, headClassName), children: header.isPlaceholder
123
- ? null
124
- : (0, react_table_1.flexRender)(header.column.columnDef.header, header.getContext()) }, header.id));
125
- }) }, headerGroup.id))) }), (0, jsx_runtime_1.jsx)("div", { ref: scrollRef, role: "rowgroup", "data-testid": "virtualized-table-viewport", "aria-label": isScrollable ? scrollAreaLabel : undefined, tabIndex: isScrollable ? 0 : undefined, className: "focus-visible:ring-ring relative overflow-auto focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none", style: { maxHeight: viewportMaxHeight }, children: (0, jsx_runtime_1.jsx)("div", { role: "presentation", className: "relative w-full", style: { height: totalSize }, children: virtualRows.map((virtualItem) => {
126
- const row = rows[virtualItem.index];
127
- return ((0, jsx_runtime_1.jsx)("div", { role: "row", "aria-rowindex": virtualItem.index + headerRowCount + 1, "data-index": virtualItem.index, className: "border-border absolute left-0 flex w-full items-center border-b text-sm", style: {
128
- height: virtualItem.size,
129
- transform: `translateY(${virtualItem.start}px)`
130
- }, children: row.getVisibleCells().map((cell, i) => {
131
- const size = (0, column_size_1.readDeclaredColumnSize)(cell.column);
132
- return ((0, jsx_runtime_1.jsx)("div", { role: "cell", "aria-colindex": i + 1,
133
- // An inline `flex` beats the class, so a sized column
134
- // stops flexing while every other column renders
135
- // exactly as it did before.
136
- style: size?.width === undefined
137
- ? size
138
- : { flex: '0 0 auto', ...size }, className: "flex-1 truncate px-4", children: (0, react_table_1.flexRender)(cell.column.columnDef.cell, cell.getContext()) }, cell.id));
139
- }) }, row.id));
140
- }) }) })] }) }));
141
- }
1
+ "use client";"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.VirtualizedTable=VirtualizedTable;const jsx_runtime_1=require("react/jsx-runtime");const react_1=require("react");const react_table_1=require("@tanstack/react-table");const react_virtual_1=require("@tanstack/react-virtual");const typography_1=require("../../lib/typography");const utils_1=require("../../lib/utils");const column_size_1=require("../data-table/column-size");const OVERSCAN=8;function VirtualizedTable({columns:v,data:w,rowHeight:d=40,maxHeight:i=480,scrollAreaLabel:h="Scrollable table rows",className:S,headClassName:z}){const a=(0,react_1.useRef)(null);const[f,C]=(0,react_1.useState)(false);const b=typeof i==="number"?`${i}px`:i;const c=(0,react_table_1.useReactTable)({data:w,columns:v,defaultColumn:column_size_1.UNSIZED_DEFAULT_COLUMN,getCoreRowModel:(0,react_table_1.getCoreRowModel)()});const u=c.getRowModel().rows;const x=c.getHeaderGroups();const m=x.length;const g=c.getVisibleLeafColumns();const _=new Map(g.map((t,r)=>[t.id,r]));const s=(0,react_virtual_1.useVirtualizer)({count:u.length,getScrollElement:()=>a.current,estimateSize:()=>d,overscan:OVERSCAN});const y=s.getVirtualItems();const p=s.getTotalSize();(0,react_1.useEffect)(()=>{s.measure()},[d,s]);(0,react_1.useEffect)(()=>{const t=a.current;if(!t)return;const r=()=>{const l=t.scrollHeight>t.clientHeight||t.scrollWidth>t.clientWidth;C(n=>n===l?n:l)};r();if(typeof ResizeObserver==="undefined")return;const e=new ResizeObserver(r);e.observe(t);const o=t.firstElementChild;if(o)e.observe(o);return()=>e.disconnect()},[p,b]);return(0,jsx_runtime_1.jsx)("div",{className:(0,utils_1.cn)("border-border bg-card text-card-foreground rounded-lg border",S),children:(0,jsx_runtime_1.jsxs)("div",{role:"table","aria-rowcount":u.length+m,"aria-colcount":g.length,children:[(0,jsx_runtime_1.jsx)("div",{role:"rowgroup",className:"bg-card z-10",children:x.map((t,r)=>(0,jsx_runtime_1.jsx)("div",{role:"row","aria-rowindex":r+1,className:"border-border flex border-b",children:t.headers.map(e=>{const o=e.column.getLeafColumns()[0];const l=_.get(o?.id??e.column.id);const n=e.subHeaders.length>0?(0,column_size_1.readDeclaredGroupSize)(e.column.getLeafColumns()):(0,column_size_1.readDeclaredColumnSize)(e.column);return(0,jsx_runtime_1.jsx)("div",{role:"columnheader","aria-colindex":(l??0)+1,"aria-colspan":e.colSpan>1?e.colSpan:void 0,style:n?.width===void 0?{flex:`${e.colSpan} 1 0%`,...n}:{flex:"0 0 auto",...n},className:(0,utils_1.cn)("min-h-10 px-4 py-2.5 text-left",typography_1.LABEL_VOICE_CLASS,z),children:e.isPlaceholder?null:(0,react_table_1.flexRender)(e.column.columnDef.header,e.getContext())},e.id)})},t.id))}),(0,jsx_runtime_1.jsx)("div",{ref:a,role:"rowgroup","data-testid":"virtualized-table-viewport","aria-label":f?h:void 0,tabIndex:f?0:void 0,className:"focus-visible:ring-ring relative overflow-auto focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none",style:{maxHeight:b},children:(0,jsx_runtime_1.jsx)("div",{role:"presentation",className:"relative w-full",style:{height:p},children:y.map(t=>{const r=u[t.index];return(0,jsx_runtime_1.jsx)("div",{role:"row","aria-rowindex":t.index+m+1,"data-index":t.index,className:"border-border absolute left-0 flex w-full items-center border-b text-sm",style:{height:t.size,transform:`translateY(${t.start}px)`},children:r.getVisibleCells().map((e,o)=>{const l=(0,column_size_1.readDeclaredColumnSize)(e.column);return(0,jsx_runtime_1.jsx)("div",{role:"cell","aria-colindex":o+1,style:l?.width===void 0?l:{flex:"0 0 auto",...l},className:"flex-1 truncate px-4",children:(0,react_table_1.flexRender)(e.column.columnDef.cell,e.getContext())},e.id)})},r.id)})})})]})})}
@@ -1,191 +1,5 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- /**
4
- * Recharts wrapper family (shadcn canon).
5
- *
6
- * `ChartContainer` takes a `ChartConfig` keyed by series name and injects one
7
- * `--color-<key>` CSS variable per series, scoped to that chart instance and
8
- * emitted twice — once for light, once under `.dark` — so a chart re-colors
9
- * with the theme without a re-render. Recharts series then reference
10
- * `var(--color-<key>)`.
11
- *
12
- * Series colors come from the design tokens: pass `color: 'var(--color-chart-1)'`
13
- * (the Tailwind `@theme` name, already wrapped in `hsl()`), never the raw
14
- * `var(--chart-1)` triple, which is not a usable color value on its own.
15
- */
16
- import * as React from 'react';
17
- import * as RechartsPrimitive from 'recharts';
18
- import { cn } from '../lib/utils.js';
19
- // Format: { THEME_NAME: CSS_SELECTOR }
20
- const THEMES = { light: '', dark: '.dark' };
21
- /**
22
- * ChartStyle writes a stylesheet through `dangerouslySetInnerHTML`, so every
23
- * interpolated value is an injection sink: `</style>` inside a config key, id,
24
- * or color would close the element and let arbitrary markup follow. Config
25
- * often carries server data (series names from an API), so these are validated
26
- * rather than trusted.
27
- *
28
- * CSS_IDENT — the chart id, which lands in an UNQUOTED attribute selector and
29
- * so may not lead with a digit.
30
- * CONFIG_KEY — series keys, which become `--color-<key>` custom-property names.
31
- * Custom properties DO allow a leading digit, so a numeric series (a year, say)
32
- * must pass; only CSS syntax is excluded.
33
- */
34
- const CSS_IDENT = /^[A-Za-z_][\w-]*$/;
35
- const CONFIG_KEY = /^[\w-]+$/;
36
- /**
37
- * A deliberately conservative character allowlist covering hex, named, `var(…)`,
38
- * `rgb()/hsl()/color-mix()` and slash-alpha forms. It admits no `<`, `>`, `{`,
39
- * `}`, `;`, `:`, quote, backslash, `@`, `*` or `!`, which rules out tag-closing,
40
- * rule termination, comments, at-rules and `!important`.
41
- *
42
- * `url(…)` is rejected separately: the allowlist alone would pass an unquoted
43
- * `url(//evil.test/pixel.png)` — no colon or quote required — which the browser
44
- * would fetch, leaking the viewer's IP and referrer to a third party. The
45
- * whitespace tolerance covers `url (…)` variants.
46
- */
47
- const CSS_COLOR = /^[\w\s#%.,()/-]+$/;
48
- const CSS_URL = /url\s*\(/i;
49
- function isSafeColor(color) {
50
- return CSS_COLOR.test(color) && !CSS_URL.test(color);
1
+ "use client";import{jsx as l,jsxs as g,Fragment as L}from"react/jsx-runtime";import*as x from"react";import*as b from"recharts";import{cn as i}from"../lib/utils.js";const T={light:"",dark:".dark"};const E=/^[A-Za-z_][\w-]*$/;const O=/^[\w-]+$/;const P=/^[\w\s#%.,()/-]+$/;const R=/url\s*\(/i;function M(o){return P.test(o)&&!R.test(o)}const D={width:320,height:200};const y=x.createContext(null);function j(){const o=x.useContext(y);if(!o){throw new Error("useChart must be used within a <ChartContainer />")}return o}function K({id:o,className:e,children:s,config:r,ariaLabel:n,initialDimension:f=D,...c}){const a=x.useId();const d=`chart-${(o??a).replace(/[^\w-]/g,"")}`;const u=typeof n==="string"&&n.length>0?{role:"img","aria-label":n}:n===false?{"aria-hidden":true}:{};return l(y.Provider,{value:{config:r},children:g("div",{"data-slot":"chart","data-chart":d,...u,className:i("[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden",e),...c,children:[l(S,{id:d,config:r}),l(b.ResponsiveContainer,{initialDimension:f,children:s})]})})}const S=({id:o,config:e})=>{if(!E.test(o)){return null}const s=Object.entries(e).filter(([r,n])=>O.test(r)&&(n.theme??n.color));if(!s.length){return null}return l("style",{dangerouslySetInnerHTML:{__html:Object.entries(T).map(([r,n])=>`
2
+ ${n} [data-chart=${o}] {
3
+ ${s.map(([f,c])=>{const a=c.theme?.[r]??c.color;return a&&M(a)?` --color-${f}: ${a};`:null}).join("\n")}
51
4
  }
52
- const INITIAL_DIMENSION = { width: 320, height: 200 };
53
- const ChartContext = React.createContext(null);
54
- function useChart() {
55
- const context = React.useContext(ChartContext);
56
- if (!context) {
57
- throw new Error('useChart must be used within a <ChartContainer />');
58
- }
59
- return context;
60
- }
61
- function ChartContainer({ id, className, children, config, ariaLabel, initialDimension = INITIAL_DIMENSION, ...props }) {
62
- const uniqueId = React.useId();
63
- // chartId lands in BOTH a DOM attribute and a CSS attribute selector, so the
64
- // two must agree and neither may carry syntax — strip to [\w-] once, here.
65
- const chartId = `chart-${(id ?? uniqueId).replace(/[^\w-]/g, '')}`;
66
- // Resolve the accessibility contract for the container element:
67
- // named → expose as an image with that name; false → hide from a11y tree.
68
- const a11yProps = typeof ariaLabel === 'string' && ariaLabel.length > 0
69
- ? { role: 'img', 'aria-label': ariaLabel }
70
- : ariaLabel === false
71
- ? { 'aria-hidden': true }
72
- : {};
73
- return (_jsx(ChartContext.Provider, { value: { config }, children: _jsxs("div", { "data-slot": "chart", "data-chart": chartId, ...a11yProps, className: cn("[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-surface]:outline-hidden", className), ...props, children: [_jsx(ChartStyle, { id: chartId, config: config }), _jsx(RechartsPrimitive.ResponsiveContainer, { initialDimension: initialDimension, children: children })] }) }));
74
- }
75
- const ChartStyle = ({ id, config }) => {
76
- // An id that cannot be written as a CSS identifier cannot be scoped safely,
77
- // and an unscoped rule would leak across every chart on the page.
78
- if (!CSS_IDENT.test(id)) {
79
- return null;
80
- }
81
- const colorConfig = Object.entries(config).filter(([key, config]) => CONFIG_KEY.test(key) && (config.theme ?? config.color));
82
- if (!colorConfig.length) {
83
- return null;
84
- }
85
- return (_jsx("style", { dangerouslySetInnerHTML: {
86
- __html: Object.entries(THEMES)
87
- .map(([theme, prefix]) => `
88
- ${prefix} [data-chart=${id}] {
89
- ${colorConfig
90
- .map(([key, itemConfig]) => {
91
- const color = itemConfig.theme?.[theme] ??
92
- itemConfig.color;
93
- return color && isSafeColor(color) ? ` --color-${key}: ${color};` : null;
94
- })
95
- .join('\n')}
96
- }
97
- `)
98
- .join('\n')
99
- } }));
100
- };
101
- const ChartTooltip = RechartsPrimitive.Tooltip;
102
- function ChartTooltipContent({ active, payload, className, indicator = 'dot', hideLabel = false, hideIndicator = false, label, labelFormatter, labelClassName, formatter, color, nameKey, labelKey }) {
103
- const { config } = useChart();
104
- const tooltipLabel = React.useMemo(() => {
105
- if (hideLabel || !payload?.length) {
106
- return null;
107
- }
108
- const [item] = payload;
109
- const key = `${labelKey ?? item?.dataKey ?? item?.name ?? 'value'}`;
110
- const itemConfig = getPayloadConfigFromPayload(config, item, key);
111
- const value = !labelKey && typeof label === 'string'
112
- ? (config[label]?.label ?? label)
113
- : itemConfig?.label;
114
- if (labelFormatter) {
115
- return (_jsx("div", { className: cn('font-medium', labelClassName), children: labelFormatter(value, payload) }));
116
- }
117
- if (!value) {
118
- return null;
119
- }
120
- return _jsx("div", { className: cn('font-medium', labelClassName), children: value });
121
- }, [
122
- label,
123
- labelFormatter,
124
- payload,
125
- hideLabel,
126
- labelClassName,
127
- config,
128
- labelKey
129
- ]);
130
- if (!active || !payload?.length) {
131
- return null;
132
- }
133
- const nestLabel = payload.length === 1 && indicator !== 'dot';
134
- return (_jsxs("div", { className: cn('border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl', className), children: [!nestLabel ? tooltipLabel : null, _jsx("div", { className: "grid gap-1.5", children: payload
135
- .filter((item) => item.type !== 'none')
136
- .map((item, index) => {
137
- const key = `${nameKey ?? item.name ?? item.dataKey ?? 'value'}`;
138
- const itemConfig = getPayloadConfigFromPayload(config, item, key);
139
- const indicatorColor = color ?? item.payload?.fill ?? item.color;
140
- return (_jsx("div", { className: cn('[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5', indicator === 'dot' && 'items-center'), children: formatter && item?.value !== undefined && item.name ? (formatter(item.value, item.name, item, index, item.payload)) : (_jsxs(_Fragment, { children: [itemConfig?.icon ? (_jsx(itemConfig.icon, {})) : (!hideIndicator && (_jsx("div", { className: cn('shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)', {
141
- 'h-2.5 w-2.5': indicator === 'dot',
142
- 'w-1': indicator === 'line',
143
- 'w-0 border-[1.5px] border-dashed bg-transparent': indicator === 'dashed',
144
- 'my-0.5': nestLabel && indicator === 'dashed'
145
- }), style: {
146
- '--color-bg': indicatorColor,
147
- '--color-border': indicatorColor
148
- } }))), _jsxs("div", { className: cn('flex flex-1 justify-between leading-none', nestLabel ? 'items-end' : 'items-center'), children: [_jsxs("div", { className: "grid gap-1.5", children: [nestLabel ? tooltipLabel : null, _jsx("span", { className: "text-muted-foreground", children: itemConfig?.label ?? item.name })] }), item.value != null && (_jsx("span", { className: "text-foreground font-mono font-medium tabular-nums", children: typeof item.value === 'number'
149
- ? item.value.toLocaleString()
150
- : String(item.value) }))] })] })) }, index));
151
- }) })] }));
152
- }
153
- const ChartLegend = RechartsPrimitive.Legend;
154
- function ChartLegendContent({ className, hideIcon = false, payload, verticalAlign = 'bottom', nameKey }) {
155
- const { config } = useChart();
156
- if (!payload?.length) {
157
- return null;
158
- }
159
- return (_jsx("div", { className: cn('flex items-center justify-center gap-4', verticalAlign === 'top' ? 'pb-3' : 'pt-3', className), children: payload
160
- .filter((item) => item.type !== 'none')
161
- .map((item, index) => {
162
- const key = `${nameKey ?? item.dataKey ?? 'value'}`;
163
- const itemConfig = getPayloadConfigFromPayload(config, item, key);
164
- return (_jsxs("div", { className: cn('[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3'), children: [itemConfig?.icon && !hideIcon ? (_jsx(itemConfig.icon, {})) : (_jsx("div", { className: "h-2 w-2 shrink-0 rounded-[2px]", style: {
165
- backgroundColor: item.color
166
- } })), itemConfig?.label] }, index));
167
- }) }));
168
- }
169
- // Helper to extract item config from a payload.
170
- function getPayloadConfigFromPayload(config, payload, key) {
171
- if (typeof payload !== 'object' || payload === null) {
172
- return undefined;
173
- }
174
- const payloadPayload = 'payload' in payload &&
175
- typeof payload.payload === 'object' &&
176
- payload.payload !== null
177
- ? payload.payload
178
- : undefined;
179
- let configLabelKey = key;
180
- if (key in payload &&
181
- typeof payload[key] === 'string') {
182
- configLabelKey = payload[key];
183
- }
184
- else if (payloadPayload &&
185
- key in payloadPayload &&
186
- typeof payloadPayload[key] === 'string') {
187
- configLabelKey = payloadPayload[key];
188
- }
189
- return configLabelKey in config ? config[configLabelKey] : config[key];
190
- }
191
- export { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle };
5
+ `).join("\n")}})};const A=b.Tooltip;function F({active:o,payload:e,className:s,indicator:r="dot",hideLabel:n=false,hideIndicator:f=false,label:c,labelFormatter:a,labelClassName:d,formatter:u,color:$,nameKey:I,labelKey:C}){const{config:m}=j();const N=x.useMemo(()=>{if(n||!e?.length){return null}const[t]=e;const v=`${C??t?.dataKey??t?.name??"value"}`;const _=w(m,t,v);const h=!C&&typeof c==="string"?m[c]?.label??c:_?.label;if(a){return l("div",{className:i("font-medium",d),children:a(h,e)})}if(!h){return null}return l("div",{className:i("font-medium",d),children:h})},[c,a,e,n,d,m,C]);if(!o||!e?.length){return null}const p=e.length===1&&r!=="dot";return g("div",{className:i("border-border/50 bg-background grid min-w-[8rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",s),children:[!p?N:null,l("div",{className:"grid gap-1.5",children:e.filter(t=>t.type!=="none").map((t,v)=>{const _=`${I??t.name??t.dataKey??"value"}`;const h=w(m,t,_);const k=$??t.payload?.fill??t.color;return l("div",{className:i("[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",r==="dot"&&"items-center"),children:u&&t?.value!==void 0&&t.name?u(t.value,t.name,t,v,t.payload):g(L,{children:[h?.icon?l(h.icon,{}):!f&&l("div",{className:i("shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)",{"h-2.5 w-2.5":r==="dot","w-1":r==="line","w-0 border-[1.5px] border-dashed bg-transparent":r==="dashed","my-0.5":p&&r==="dashed"}),style:{"--color-bg":k,"--color-border":k}}),g("div",{className:i("flex flex-1 justify-between leading-none",p?"items-end":"items-center"),children:[g("div",{className:"grid gap-1.5",children:[p?N:null,l("span",{className:"text-muted-foreground",children:h?.label??t.name})]}),t.value!=null&&l("span",{className:"text-foreground font-mono font-medium tabular-nums",children:typeof t.value==="number"?t.value.toLocaleString():String(t.value)})]})]})},v)})})]})}const H=b.Legend;function q({className:o,hideIcon:e=false,payload:s,verticalAlign:r="bottom",nameKey:n}){const{config:f}=j();if(!s?.length){return null}return l("div",{className:i("flex items-center justify-center gap-4",r==="top"?"pb-3":"pt-3",o),children:s.filter(c=>c.type!=="none").map((c,a)=>{const d=`${n??c.dataKey??"value"}`;const u=w(f,c,d);return g("div",{className:i("[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3"),children:[u?.icon&&!e?l(u.icon,{}):l("div",{className:"h-2 w-2 shrink-0 rounded-[2px]",style:{backgroundColor:c.color}}),u?.label]},a)})})}function w(o,e,s){if(typeof e!=="object"||e===null){return void 0}const r="payload"in e&&typeof e.payload==="object"&&e.payload!==null?e.payload:void 0;let n=s;if(s in e&&typeof e[s]==="string"){n=e[s]}else if(r&&s in r&&typeof r[s]==="string"){n=r[s]}return n in o?o[n]:o[s]}export{K as ChartContainer,A as ChartTooltip,F as ChartTooltipContent,H as ChartLegend,q as ChartLegendContent,S as ChartStyle};
@@ -1,3 +1 @@
1
- // Owned by lane theme-toasts-charts — see docs/plans/2026-08-26-sindarian-x-retirement/
2
- // ChartStyle stays internal: it is an implementation detail of ChartContainer.
3
- export { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent } from './chart.js';
1
+ export{ChartContainer,ChartTooltip,ChartTooltipContent,ChartLegend,ChartLegendContent}from"./chart.js";
@@ -1,12 +1 @@
1
- import isNil from 'lodash/isNil.js';
2
- export function getBreadcrumbPaths(paths) {
3
- return (paths
4
- .filter((path) => {
5
- if (isNil(path.active)) {
6
- return true;
7
- }
8
- return path.active();
9
- })
10
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
- .map(({ active, ...path }) => path));
12
- }
1
+ import i from"lodash/isNil.js";export function getBreadcrumbPaths(t){return t.filter(r=>{if(i(r.active)){return true}return r.active()}).map(({active:r,...e})=>e)}
@@ -1,30 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import React from 'react';
3
- import { Breadcrumb as BaseBreadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from '../../components/ui/breadcrumb/index.js';
4
- export const Breadcrumb = ({ paths }) => {
5
- const last = paths.length - 1;
6
- return (_jsx(BaseBreadcrumb, { children: _jsx(BreadcrumbList, { children: paths.map((path, index) => {
7
- /**
8
- * ⛔ THE LAST CRUMB IS THE PAGE, whatever the caller passed.
9
- *
10
- * The page marker used to be a side effect of the caller omitting
11
- * `href`, and nothing in `getBreadcrumbPaths` makes the last entry
12
- * omit one. A trail whose final entry carried an href therefore had
13
- * no `aria-current="page"` anywhere — a screen reader could not tell
14
- * which of three links was the page being read — and the crumb was a
15
- * live link back to the page the reader is already on.
16
- *
17
- * ⚠️ AND AN HREF-LESS ANCESTOR IS NOT A SWITCHED-OFF LINK. It used
18
- * to reach `BreadcrumbPage` too, which hard-codes `role="link"
19
- * aria-disabled="true"` — the right shape for the page you are ON,
20
- * a destination that exists and simply is not navigable from here.
21
- * An ancestor with no href is a grouping label: there is no page
22
- * behind it, so "Settings, link, dimmed" told the reader a
23
- * destination was unavailable to them rather than that it was never
24
- * a destination, and inflated the link count of the trail. It is
25
- * plain text now, and only the last crumb is the page.
26
- */
27
- const isLast = index === last;
28
- return (_jsxs(React.Fragment, { children: [_jsx(BreadcrumbItem, { children: path.href && !isLast ? (_jsx(BreadcrumbLink, { href: path.href, children: path.name })) : isLast ? (_jsx(BreadcrumbPage, { children: path.name })) : (_jsx("span", { className: "breadcrumb-page", children: path.name })) }), !isLast && _jsx(BreadcrumbSeparator, {})] }, index));
29
- }) }) }));
30
- };
1
+ import{jsx as r,jsxs as n}from"react/jsx-runtime";import d from"react";import{Breadcrumb as i,BreadcrumbItem as t,BreadcrumbLink as o,BreadcrumbList as u,BreadcrumbPage as b,BreadcrumbSeparator as l}from"../../components/ui/breadcrumb/index.js";export const Breadcrumb=({paths:m})=>{const s=m.length-1;return r(i,{children:r(u,{children:m.map((e,c)=>{const a=c===s;return n(d.Fragment,{children:[r(t,{children:e.href&&!a?r(o,{href:e.href,children:e.name}):a?r(b,{children:e.name}):r("span",{className:"breadcrumb-page",children:e.name})}),!a&&r(l,{})]},c)})})})};