@tribepad/themis 1.0.1 → 1.0.2

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 (301) hide show
  1. package/dist/elements/Accordion/index.js +1 -335
  2. package/dist/elements/Accordion/index.js.map +1 -1
  3. package/dist/elements/Accordion/index.mjs +1 -317
  4. package/dist/elements/Accordion/index.mjs.map +1 -1
  5. package/dist/elements/AlertDialog/AlertDialog.d.ts +43 -0
  6. package/dist/elements/AlertDialog/AlertDialog.d.ts.map +1 -0
  7. package/dist/elements/AlertDialog/AlertDialog.styles.d.ts +15 -0
  8. package/dist/elements/AlertDialog/AlertDialog.styles.d.ts.map +1 -0
  9. package/dist/elements/AlertDialog/AlertDialog.types.d.ts +72 -0
  10. package/dist/elements/AlertDialog/AlertDialog.types.d.ts.map +1 -0
  11. package/dist/elements/AlertDialog/index.d.ts +25 -0
  12. package/dist/elements/AlertDialog/index.d.ts.map +1 -0
  13. package/dist/elements/AlertDialog/index.js +3 -0
  14. package/dist/elements/AlertDialog/index.js.map +1 -0
  15. package/dist/elements/AlertDialog/index.mjs +3 -0
  16. package/dist/elements/AlertDialog/index.mjs.map +1 -0
  17. package/dist/elements/Avatar/index.js +1 -468
  18. package/dist/elements/Avatar/index.js.map +1 -1
  19. package/dist/elements/Avatar/index.mjs +1 -456
  20. package/dist/elements/Avatar/index.mjs.map +1 -1
  21. package/dist/elements/Badge/index.js +1 -243
  22. package/dist/elements/Badge/index.js.map +1 -1
  23. package/dist/elements/Badge/index.mjs +1 -234
  24. package/dist/elements/Badge/index.mjs.map +1 -1
  25. package/dist/elements/Breadcrumbs/index.js +1 -821
  26. package/dist/elements/Breadcrumbs/index.js.map +1 -1
  27. package/dist/elements/Breadcrumbs/index.mjs +1 -810
  28. package/dist/elements/Breadcrumbs/index.mjs.map +1 -1
  29. package/dist/elements/Button/Button.d.ts +26 -81
  30. package/dist/elements/Button/Button.d.ts.map +1 -1
  31. package/dist/elements/Button/Button.styles.d.ts +35 -0
  32. package/dist/elements/Button/Button.styles.d.ts.map +1 -0
  33. package/dist/elements/Button/Button.types.d.ts +20 -8
  34. package/dist/elements/Button/Button.types.d.ts.map +1 -1
  35. package/dist/elements/Button/index.js +1 -288
  36. package/dist/elements/Button/index.js.map +1 -1
  37. package/dist/elements/Button/index.mjs +1 -283
  38. package/dist/elements/Button/index.mjs.map +1 -1
  39. package/dist/elements/ButtonGroup/index.js +1 -237
  40. package/dist/elements/ButtonGroup/index.js.map +1 -1
  41. package/dist/elements/ButtonGroup/index.mjs +1 -222
  42. package/dist/elements/ButtonGroup/index.mjs.map +1 -1
  43. package/dist/elements/Card/index.js +1 -579
  44. package/dist/elements/Card/index.js.map +1 -1
  45. package/dist/elements/Card/index.mjs +1 -560
  46. package/dist/elements/Card/index.mjs.map +1 -1
  47. package/dist/elements/Carousel/Carousel.d.ts +1 -11
  48. package/dist/elements/Carousel/Carousel.d.ts.map +1 -1
  49. package/dist/elements/Carousel/LazyCarousel.d.ts +1 -1
  50. package/dist/elements/Carousel/LazyCarousel.d.ts.map +1 -1
  51. package/dist/elements/Carousel/index.js +1 -789
  52. package/dist/elements/Carousel/index.js.map +1 -1
  53. package/dist/elements/Carousel/index.mjs +1 -786
  54. package/dist/elements/Carousel/index.mjs.map +1 -1
  55. package/dist/elements/Chart/ChartContext.d.ts.map +1 -1
  56. package/dist/elements/Chart/index.js +1 -1842
  57. package/dist/elements/Chart/index.js.map +1 -1
  58. package/dist/elements/Chart/index.mjs +1 -1832
  59. package/dist/elements/Chart/index.mjs.map +1 -1
  60. package/dist/elements/Checkbox/index.js +1 -316
  61. package/dist/elements/Checkbox/index.js.map +1 -1
  62. package/dist/elements/Checkbox/index.mjs +1 -306
  63. package/dist/elements/Checkbox/index.mjs.map +1 -1
  64. package/dist/elements/CheckboxGroup/index.js +1 -455
  65. package/dist/elements/CheckboxGroup/index.js.map +1 -1
  66. package/dist/elements/CheckboxGroup/index.mjs +1 -439
  67. package/dist/elements/CheckboxGroup/index.mjs.map +1 -1
  68. package/dist/elements/Combobox/Combobox.d.ts +56 -0
  69. package/dist/elements/Combobox/Combobox.d.ts.map +1 -0
  70. package/dist/elements/Combobox/Combobox.styles.d.ts +29 -0
  71. package/dist/elements/Combobox/Combobox.styles.d.ts.map +1 -0
  72. package/dist/elements/Combobox/Combobox.types.d.ts +67 -0
  73. package/dist/elements/Combobox/Combobox.types.d.ts.map +1 -0
  74. package/dist/elements/Combobox/index.d.ts +20 -0
  75. package/dist/elements/Combobox/index.d.ts.map +1 -0
  76. package/dist/elements/Combobox/index.js +3 -0
  77. package/dist/elements/Combobox/index.js.map +1 -0
  78. package/dist/elements/Combobox/index.mjs +3 -0
  79. package/dist/elements/Combobox/index.mjs.map +1 -0
  80. package/dist/elements/DatePicker/DatePicker.d.ts +1 -1
  81. package/dist/elements/DatePicker/DatePicker.d.ts.map +1 -1
  82. package/dist/elements/DatePicker/index.js +1 -903
  83. package/dist/elements/DatePicker/index.js.map +1 -1
  84. package/dist/elements/DatePicker/index.mjs +1 -853
  85. package/dist/elements/DatePicker/index.mjs.map +1 -1
  86. package/dist/elements/Dropdown/Dropdown.d.ts +7 -15
  87. package/dist/elements/Dropdown/Dropdown.d.ts.map +1 -1
  88. package/dist/elements/Dropdown/Dropdown.styles.d.ts +22 -0
  89. package/dist/elements/Dropdown/Dropdown.styles.d.ts.map +1 -0
  90. package/dist/elements/Dropdown/index.d.ts +1 -0
  91. package/dist/elements/Dropdown/index.d.ts.map +1 -1
  92. package/dist/elements/Dropdown/index.js +1 -193
  93. package/dist/elements/Dropdown/index.js.map +1 -1
  94. package/dist/elements/Dropdown/index.mjs +1 -184
  95. package/dist/elements/Dropdown/index.mjs.map +1 -1
  96. package/dist/elements/FileField/index.js +1 -1539
  97. package/dist/elements/FileField/index.js.map +1 -1
  98. package/dist/elements/FileField/index.mjs +1 -1507
  99. package/dist/elements/FileField/index.mjs.map +1 -1
  100. package/dist/elements/FormLayout/index.js +1 -170
  101. package/dist/elements/FormLayout/index.js.map +1 -1
  102. package/dist/elements/FormLayout/index.mjs +1 -167
  103. package/dist/elements/FormLayout/index.mjs.map +1 -1
  104. package/dist/elements/Modal/Modal.d.ts +9 -14
  105. package/dist/elements/Modal/Modal.d.ts.map +1 -1
  106. package/dist/elements/Modal/Modal.styles.d.ts +29 -0
  107. package/dist/elements/Modal/Modal.styles.d.ts.map +1 -0
  108. package/dist/elements/Modal/index.d.ts +1 -0
  109. package/dist/elements/Modal/index.d.ts.map +1 -1
  110. package/dist/elements/Modal/index.js +1 -232
  111. package/dist/elements/Modal/index.js.map +1 -1
  112. package/dist/elements/Modal/index.mjs +1 -220
  113. package/dist/elements/Modal/index.mjs.map +1 -1
  114. package/dist/elements/NumberField/index.js +1 -666
  115. package/dist/elements/NumberField/index.js.map +1 -1
  116. package/dist/elements/NumberField/index.mjs +1 -654
  117. package/dist/elements/NumberField/index.mjs.map +1 -1
  118. package/dist/elements/OTPInput/OTPInput.d.ts.map +1 -1
  119. package/dist/elements/OTPInput/index.js +1 -734
  120. package/dist/elements/OTPInput/index.js.map +1 -1
  121. package/dist/elements/OTPInput/index.mjs +1 -732
  122. package/dist/elements/OTPInput/index.mjs.map +1 -1
  123. package/dist/elements/Pagination/Pagination.d.ts +45 -0
  124. package/dist/elements/Pagination/Pagination.d.ts.map +1 -0
  125. package/dist/elements/Pagination/Pagination.styles.d.ts +10 -0
  126. package/dist/elements/Pagination/Pagination.styles.d.ts.map +1 -0
  127. package/dist/elements/Pagination/Pagination.types.d.ts +55 -0
  128. package/dist/elements/Pagination/Pagination.types.d.ts.map +1 -0
  129. package/dist/elements/Pagination/index.d.ts +21 -0
  130. package/dist/elements/Pagination/index.d.ts.map +1 -0
  131. package/dist/elements/Pagination/index.js +3 -0
  132. package/dist/elements/Pagination/index.js.map +1 -0
  133. package/dist/elements/Pagination/index.mjs +3 -0
  134. package/dist/elements/Pagination/index.mjs.map +1 -0
  135. package/dist/elements/Panel/index.js +1 -330
  136. package/dist/elements/Panel/index.js.map +1 -1
  137. package/dist/elements/Panel/index.mjs +1 -323
  138. package/dist/elements/Panel/index.mjs.map +1 -1
  139. package/dist/elements/PasswordField/PasswordField.d.ts +27 -0
  140. package/dist/elements/PasswordField/PasswordField.d.ts.map +1 -0
  141. package/dist/elements/PasswordField/PasswordField.styles.d.ts +32 -0
  142. package/dist/elements/PasswordField/PasswordField.styles.d.ts.map +1 -0
  143. package/dist/elements/PasswordField/PasswordField.types.d.ts +100 -0
  144. package/dist/elements/PasswordField/PasswordField.types.d.ts.map +1 -0
  145. package/dist/elements/PasswordField/index.css +2 -0
  146. package/dist/elements/PasswordField/index.css.map +1 -0
  147. package/dist/elements/PasswordField/index.d.ts +20 -0
  148. package/dist/elements/PasswordField/index.d.ts.map +1 -0
  149. package/dist/elements/PasswordField/index.js +3 -0
  150. package/dist/elements/PasswordField/index.js.map +1 -0
  151. package/dist/elements/PasswordField/index.mjs +3 -0
  152. package/dist/elements/PasswordField/index.mjs.map +1 -0
  153. package/dist/elements/Progress/index.js +1 -187
  154. package/dist/elements/Progress/index.js.map +1 -1
  155. package/dist/elements/Progress/index.mjs +1 -181
  156. package/dist/elements/Progress/index.mjs.map +1 -1
  157. package/dist/elements/RadioGroup/index.js +1 -369
  158. package/dist/elements/RadioGroup/index.js.map +1 -1
  159. package/dist/elements/RadioGroup/index.mjs +1 -359
  160. package/dist/elements/RadioGroup/index.mjs.map +1 -1
  161. package/dist/elements/Resizable/index.js +1 -1580
  162. package/dist/elements/Resizable/index.js.map +1 -1
  163. package/dist/elements/Resizable/index.mjs +1 -1566
  164. package/dist/elements/Resizable/index.mjs.map +1 -1
  165. package/dist/elements/SearchField/SearchField.d.ts +27 -0
  166. package/dist/elements/SearchField/SearchField.d.ts.map +1 -0
  167. package/dist/elements/SearchField/SearchField.styles.d.ts +32 -0
  168. package/dist/elements/SearchField/SearchField.styles.d.ts.map +1 -0
  169. package/dist/elements/SearchField/SearchField.types.d.ts +45 -0
  170. package/dist/elements/SearchField/SearchField.types.d.ts.map +1 -0
  171. package/dist/elements/SearchField/index.css +2 -0
  172. package/dist/elements/SearchField/index.css.map +1 -0
  173. package/dist/elements/SearchField/index.d.ts +21 -0
  174. package/dist/elements/SearchField/index.d.ts.map +1 -0
  175. package/dist/elements/SearchField/index.js +3 -0
  176. package/dist/elements/SearchField/index.js.map +1 -0
  177. package/dist/elements/SearchField/index.mjs +3 -0
  178. package/dist/elements/SearchField/index.mjs.map +1 -0
  179. package/dist/elements/Select/Select.d.ts +19 -48
  180. package/dist/elements/Select/Select.d.ts.map +1 -1
  181. package/dist/elements/Select/Select.styles.d.ts +55 -0
  182. package/dist/elements/Select/Select.styles.d.ts.map +1 -0
  183. package/dist/elements/Select/index.js +1 -589
  184. package/dist/elements/Select/index.js.map +1 -1
  185. package/dist/elements/Select/index.mjs +1 -582
  186. package/dist/elements/Select/index.mjs.map +1 -1
  187. package/dist/elements/Skeleton/index.js +1 -82
  188. package/dist/elements/Skeleton/index.js.map +1 -1
  189. package/dist/elements/Skeleton/index.mjs +1 -78
  190. package/dist/elements/Skeleton/index.mjs.map +1 -1
  191. package/dist/elements/Switch/index.js +1 -179
  192. package/dist/elements/Switch/index.js.map +1 -1
  193. package/dist/elements/Switch/index.mjs +1 -173
  194. package/dist/elements/Switch/index.mjs.map +1 -1
  195. package/dist/elements/Table/Table.d.ts +3 -24
  196. package/dist/elements/Table/Table.d.ts.map +1 -1
  197. package/dist/elements/Table/Table.styles.d.ts +24 -0
  198. package/dist/elements/Table/Table.styles.d.ts.map +1 -0
  199. package/dist/elements/Table/index.js +1 -595
  200. package/dist/elements/Table/index.js.map +1 -1
  201. package/dist/elements/Table/index.mjs +1 -578
  202. package/dist/elements/Table/index.mjs.map +1 -1
  203. package/dist/elements/Tabs/index.js +1 -337
  204. package/dist/elements/Tabs/index.js.map +1 -1
  205. package/dist/elements/Tabs/index.mjs +1 -320
  206. package/dist/elements/Tabs/index.mjs.map +1 -1
  207. package/dist/elements/TextField/TextField.d.ts +6 -42
  208. package/dist/elements/TextField/TextField.d.ts.map +1 -1
  209. package/dist/elements/TextField/TextField.hooks.d.ts +63 -0
  210. package/dist/elements/TextField/TextField.hooks.d.ts.map +1 -0
  211. package/dist/elements/TextField/TextField.icons.d.ts +19 -0
  212. package/dist/elements/TextField/TextField.icons.d.ts.map +1 -0
  213. package/dist/elements/TextField/TextField.styles.d.ts +37 -0
  214. package/dist/elements/TextField/TextField.styles.d.ts.map +1 -0
  215. package/dist/elements/TextField/TextField.types.d.ts +3 -0
  216. package/dist/elements/TextField/TextField.types.d.ts.map +1 -1
  217. package/dist/elements/TextField/index.css +1 -22
  218. package/dist/elements/TextField/index.css.map +1 -1
  219. package/dist/elements/TextField/index.js +1 -902
  220. package/dist/elements/TextField/index.js.map +1 -1
  221. package/dist/elements/TextField/index.mjs +1 -882
  222. package/dist/elements/TextField/index.mjs.map +1 -1
  223. package/dist/elements/TimeField/index.js +1 -254
  224. package/dist/elements/TimeField/index.js.map +1 -1
  225. package/dist/elements/TimeField/index.mjs +1 -238
  226. package/dist/elements/TimeField/index.mjs.map +1 -1
  227. package/dist/elements/Toast/Toast.d.ts +0 -22
  228. package/dist/elements/Toast/Toast.d.ts.map +1 -1
  229. package/dist/elements/Toast/index.js +1 -737
  230. package/dist/elements/Toast/index.js.map +1 -1
  231. package/dist/elements/Toast/index.mjs +1 -724
  232. package/dist/elements/Toast/index.mjs.map +1 -1
  233. package/dist/elements/Tooltip/index.js +1 -323
  234. package/dist/elements/Tooltip/index.js.map +1 -1
  235. package/dist/elements/Tooltip/index.mjs +1 -310
  236. package/dist/elements/Tooltip/index.mjs.map +1 -1
  237. package/dist/elements/index.css +1 -22
  238. package/dist/elements/index.css.map +1 -1
  239. package/dist/elements/index.d.ts +13 -1
  240. package/dist/elements/index.d.ts.map +1 -1
  241. package/dist/elements/index.js +1 -12455
  242. package/dist/elements/index.js.map +1 -1
  243. package/dist/elements/index.mjs +1 -12233
  244. package/dist/elements/index.mjs.map +1 -1
  245. package/dist/index.css +1 -22
  246. package/dist/index.css.map +1 -1
  247. package/dist/index.js +2 -12490
  248. package/dist/index.js.map +1 -1
  249. package/dist/index.mjs +2 -12262
  250. package/dist/index.mjs.map +1 -1
  251. package/dist/schemas/index.js +2 -54
  252. package/dist/schemas/index.js.map +1 -1
  253. package/dist/schemas/index.mjs +2 -48
  254. package/dist/schemas/index.mjs.map +1 -1
  255. package/dist/styles/defaults.css +151 -0
  256. package/dist/styles/index.js +1 -166
  257. package/dist/styles/index.js.map +1 -1
  258. package/dist/styles/index.mjs +1 -129
  259. package/dist/styles/index.mjs.map +1 -1
  260. package/dist/utils/index.js +1 -12
  261. package/dist/utils/index.js.map +1 -1
  262. package/dist/utils/index.mjs +1 -10
  263. package/dist/utils/index.mjs.map +1 -1
  264. package/package.json +9 -7
  265. package/src/elements/Accordion/Accordion.stories.tsx +1 -1
  266. package/src/elements/AlertDialog/AlertDialog.stories.tsx +124 -0
  267. package/src/elements/Avatar/Avatar.stories.tsx +1 -1
  268. package/src/elements/Badge/Badge.stories.tsx +1 -1
  269. package/src/elements/Breadcrumbs/Breadcrumbs.stories.tsx +1 -1
  270. package/src/elements/Button/Button.stories.tsx +1 -1
  271. package/src/elements/ButtonGroup/ButtonGroup.stories.tsx +1 -1
  272. package/src/elements/Card/Card.stories.tsx +1 -1
  273. package/src/elements/Carousel/Carousel.stories.tsx +1 -1
  274. package/src/elements/Chart/Chart.stories.tsx +1 -1
  275. package/src/elements/Checkbox/Checkbox.stories.tsx +1 -1
  276. package/src/elements/CheckboxGroup/CheckboxGroup.stories.tsx +1 -1
  277. package/src/elements/Combobox/Combobox.stories.tsx +133 -0
  278. package/src/elements/DatePicker/DatePicker.stories.tsx +1 -1
  279. package/src/elements/Dropdown/Dropdown.stories.tsx +1 -1
  280. package/src/elements/FileField/FileField.stories.tsx +1 -1
  281. package/src/elements/FileField/FileProgress.stories.tsx +1 -1
  282. package/src/elements/FormLayout/FormLayout.stories.tsx +1 -1
  283. package/src/elements/Modal/Modal.stories.tsx +1 -1
  284. package/src/elements/NumberField/NumberField.stories.tsx +1 -1
  285. package/src/elements/OTPInput/OTPInput.stories.tsx +1 -1
  286. package/src/elements/Pagination/Pagination.stories.tsx +203 -0
  287. package/src/elements/Panel/Panel.stories.tsx +1 -1
  288. package/src/elements/PasswordField/PasswordField.stories.tsx +167 -0
  289. package/src/elements/Progress/Progress.stories.tsx +1 -1
  290. package/src/elements/RadioGroup/RadioGroup.stories.tsx +1 -1
  291. package/src/elements/Resizable/Resizable.stories.tsx +1 -1
  292. package/src/elements/SearchField/SearchField.stories.tsx +146 -0
  293. package/src/elements/Select/Select.stories.tsx +1 -1
  294. package/src/elements/Skeleton/Skeleton.stories.tsx +1 -1
  295. package/src/elements/Switch/Switch.stories.tsx +1 -1
  296. package/src/elements/Table/Table.stories.tsx +1 -1
  297. package/src/elements/Tabs/Tabs.stories.tsx +1 -1
  298. package/src/elements/TextField/TextField.stories.tsx +1 -1
  299. package/src/elements/TimeField/TimeField.stories.tsx +1 -1
  300. package/src/elements/Toast/Toast.stories.tsx +1 -1
  301. package/src/elements/Tooltip/Tooltip.stories.tsx +1 -1
@@ -1,1844 +1,3 @@
1
1
  "use client";
2
- 'use strict';
3
-
4
- var react = require('react');
5
- var clsx = require('clsx');
6
- var tailwindMerge = require('tailwind-merge');
7
- var classVarianceAuthority = require('class-variance-authority');
8
- var jsxRuntime = require('react/jsx-runtime');
9
- var reactAria = require('react-aria');
10
- require('react-aria-components');
11
- var zod = require('zod');
12
-
13
- // src/elements/Chart/Chart.tsx
14
- function cn(...inputs) {
15
- return tailwindMerge.twMerge(clsx.clsx(inputs));
16
- }
17
- var chartVariants = classVarianceAuthority.cva(
18
- // Base classes
19
- "relative w-full overflow-hidden rounded-lg border",
20
- {
21
- variants: {
22
- variant: {
23
- default: "border-[var(--border)] bg-[var(--content-background)]",
24
- muted: "border-[var(--accent-background)] bg-[var(--accent-background)]/50"
25
- }
26
- },
27
- defaultVariants: {
28
- variant: "default"
29
- }
30
- }
31
- );
32
- var dataPointVariants = classVarianceAuthority.cva(
33
- // Base classes
34
- "cursor-pointer focus-visible:outline-none",
35
- {
36
- variants: {
37
- type: {
38
- bar: "",
39
- // Fill color set dynamically per series
40
- line: "stroke-2"
41
- // Stroke color set dynamically per series
42
- },
43
- state: {
44
- default: "",
45
- focused: "",
46
- // Focus ring provides visual feedback, no scale needed for data points
47
- selected: "opacity-80"
48
- }
49
- },
50
- defaultVariants: {
51
- type: "bar",
52
- state: "default"
53
- }
54
- }
55
- );
56
- classVarianceAuthority.cva(
57
- // Base classes
58
- "pointer-events-none",
59
- {
60
- variants: {
61
- visible: {
62
- true: "opacity-100",
63
- false: "opacity-0"
64
- }
65
- },
66
- defaultVariants: {
67
- visible: false
68
- }
69
- }
70
- );
71
- var legendVariants = classVarianceAuthority.cva(
72
- // Base classes - horizontal flex layout below chart
73
- "flex flex-wrap items-center justify-center gap-4 px-4 py-2",
74
- {
75
- variants: {
76
- visible: {
77
- true: "",
78
- false: "hidden"
79
- }
80
- },
81
- defaultVariants: {
82
- visible: true
83
- }
84
- }
85
- );
86
-
87
- // src/elements/Chart/chart.constants.ts
88
- var CHART_ASPECT_RATIO = 9 / 16;
89
- var CHART_PADDING = {
90
- top: 20,
91
- // Space for title overflow
92
- right: 20,
93
- // Space for y-axis label
94
- bottom: 60,
95
- // Space for x-axis labels + legend
96
- left: 60
97
- // Space for y-axis labels
98
- };
99
- var MIN_CHART_WIDTH = 300;
100
- var MIN_CHART_HEIGHT = 169;
101
- var BAR_GROUP_PADDING = 0.2;
102
- var BAR_PADDING = 0.1;
103
- var MIN_BAR_WIDTH = 20;
104
- var BAR_RADIUS = 4;
105
- var LINE_STROKE_WIDTH = 2;
106
- var POINT_RADIUS = 6;
107
- var LINE_PATTERNS = {
108
- solid: "none",
109
- dashed: "8 4",
110
- // 8px dash, 4px gap
111
- dotted: "2 4"
112
- // 2px dot, 4px gap
113
- };
114
- var GRID_COLOR = "var(--border)";
115
- var GRID_STROKE_WIDTH = 1;
116
- var GRID_STROKE_OPACITY = 0.7;
117
- var AXIS_COLOR = "var(--border)";
118
- var AXIS_STROKE_WIDTH = 1;
119
- var AXIS_TICK_SIZE = 6;
120
- var AXIS_LABEL_FONT_SIZE = 12;
121
- var MIN_TOUCH_TARGET = 44;
122
- var FOCUS_RING_OFFSET = 4;
123
- var FOCUS_RING_WIDTH = 2;
124
- var DEFAULT_SERIES_COLORS = [
125
- "var(--chart-1, hsl(221 83% 53%))",
126
- // Series 0 - Blue
127
- "var(--chart-2, hsl(142 71% 45%))",
128
- // Series 1 - Green
129
- "var(--chart-3, hsl(38 92% 50%))",
130
- // Series 2 - Orange
131
- "var(--chart-4, hsl(262 83% 58%))",
132
- // Series 3 - Purple
133
- "var(--chart-5, hsl(0 84% 60%))"
134
- // Series 4 - Red
135
- ];
136
- function niceNum(range, round) {
137
- const exponent = Math.floor(Math.log10(range));
138
- const fraction = range / Math.pow(10, exponent);
139
- let niceFraction;
140
- if (round) {
141
- if (fraction < 1.5) niceFraction = 1;
142
- else if (fraction < 3) niceFraction = 2;
143
- else if (fraction < 7) niceFraction = 5;
144
- else niceFraction = 10;
145
- } else {
146
- if (fraction <= 1) niceFraction = 1;
147
- else if (fraction <= 2) niceFraction = 2;
148
- else if (fraction <= 5) niceFraction = 5;
149
- else niceFraction = 10;
150
- }
151
- return niceFraction * Math.pow(10, exponent);
152
- }
153
- function calculateYAxisTicks(minValue, maxValue, tickCount = 5) {
154
- if (minValue === maxValue) {
155
- if (minValue === 0) {
156
- return [0, 25, 50, 75, 100];
157
- }
158
- minValue = minValue * 0.9;
159
- maxValue = maxValue * 1.1;
160
- }
161
- const range = niceNum(maxValue - minValue, false);
162
- const tickSpacing = niceNum(range / (tickCount - 1), true);
163
- const niceMin = Math.floor(minValue / tickSpacing) * tickSpacing;
164
- const niceMax = Math.ceil(maxValue / tickSpacing) * tickSpacing;
165
- const ticks = [];
166
- for (let tick = niceMin; tick <= niceMax; tick += tickSpacing) {
167
- ticks.push(tick);
168
- }
169
- return ticks;
170
- }
171
- function calculateBarLayout(plotWidth, labelCount, seriesCount, plotX = 0) {
172
- const groupWidth = plotWidth / labelCount;
173
- const availableWidth = groupWidth * (1 - BAR_GROUP_PADDING);
174
- const barWidth = Math.max(
175
- MIN_BAR_WIDTH,
176
- availableWidth / seriesCount * (1 - BAR_PADDING)
177
- );
178
- const getBarX = (labelIdx, seriesIdx) => {
179
- const groupCenter = plotX + groupWidth * labelIdx + groupWidth / 2;
180
- const totalBarsWidth = seriesCount * barWidth + (seriesCount - 1) * (BAR_PADDING * barWidth);
181
- const barsStart = groupCenter - totalBarsWidth / 2;
182
- return barsStart + seriesIdx * (barWidth + BAR_PADDING * barWidth);
183
- };
184
- return { barWidth, groupWidth, getBarX };
185
- }
186
-
187
- // src/elements/Chart/useChartDimensions.ts
188
- function useChartDimensions(containerRef) {
189
- const [dimensions, setDimensions] = react.useState({
190
- width: MIN_CHART_WIDTH,
191
- height: MIN_CHART_HEIGHT,
192
- padding: CHART_PADDING,
193
- plotArea: {
194
- x: CHART_PADDING.left,
195
- y: CHART_PADDING.top,
196
- width: MIN_CHART_WIDTH - CHART_PADDING.left - CHART_PADDING.right,
197
- height: MIN_CHART_HEIGHT - CHART_PADDING.top - CHART_PADDING.bottom
198
- }
199
- });
200
- react.useEffect(() => {
201
- const container = containerRef.current;
202
- if (!container) return;
203
- const updateDimensions = (width) => {
204
- const constrainedWidth = Math.max(width, MIN_CHART_WIDTH);
205
- const height = Math.max(
206
- constrainedWidth * CHART_ASPECT_RATIO,
207
- MIN_CHART_HEIGHT
208
- );
209
- const plotArea = {
210
- x: CHART_PADDING.left,
211
- y: CHART_PADDING.top,
212
- width: constrainedWidth - CHART_PADDING.left - CHART_PADDING.right,
213
- height: height - CHART_PADDING.top - CHART_PADDING.bottom
214
- };
215
- setDimensions({
216
- width: constrainedWidth,
217
- height,
218
- padding: CHART_PADDING,
219
- plotArea
220
- });
221
- };
222
- const observer = new ResizeObserver((entries) => {
223
- const entry = entries[0];
224
- if (!entry) return;
225
- updateDimensions(entry.contentRect.width);
226
- });
227
- updateDimensions(container.getBoundingClientRect().width);
228
- observer.observe(container);
229
- return () => {
230
- observer.disconnect();
231
- };
232
- }, [containerRef]);
233
- return dimensions;
234
- }
235
- function useRovingTabIndex2D(options) {
236
- const {
237
- rows,
238
- cols,
239
- initialRow = 0,
240
- initialCol = 0,
241
- wrap = false,
242
- onFocusChange
243
- } = options;
244
- const [focusedRow, setFocusedRow] = react.useState(initialRow);
245
- const [focusedCol, setFocusedCol] = react.useState(initialCol);
246
- const getColCount = react.useCallback(
247
- (row) => {
248
- return typeof cols === "function" ? cols(row) : cols;
249
- },
250
- [cols]
251
- );
252
- const notifyFocusChange = react.useCallback(
253
- (row, col) => {
254
- onFocusChange?.(row, col);
255
- },
256
- [onFocusChange]
257
- );
258
- const moveRight = react.useCallback(() => {
259
- setFocusedCol((currentCol) => {
260
- const maxCol = getColCount(focusedRow) - 1;
261
- let newCol = currentCol + 1;
262
- if (newCol > maxCol) {
263
- newCol = wrap ? 0 : maxCol;
264
- }
265
- if (newCol !== currentCol) {
266
- notifyFocusChange(focusedRow, newCol);
267
- }
268
- return newCol;
269
- });
270
- }, [focusedRow, getColCount, wrap, notifyFocusChange]);
271
- const moveLeft = react.useCallback(() => {
272
- setFocusedCol((currentCol) => {
273
- let newCol = currentCol - 1;
274
- const maxCol = getColCount(focusedRow) - 1;
275
- if (newCol < 0) {
276
- newCol = wrap ? maxCol : 0;
277
- }
278
- if (newCol !== currentCol) {
279
- notifyFocusChange(focusedRow, newCol);
280
- }
281
- return newCol;
282
- });
283
- }, [focusedRow, getColCount, wrap, notifyFocusChange]);
284
- const moveDown = react.useCallback(() => {
285
- setFocusedRow((currentRow) => {
286
- let newRow = currentRow + 1;
287
- if (newRow >= rows) {
288
- newRow = wrap ? 0 : rows - 1;
289
- }
290
- if (newRow !== currentRow) {
291
- const newMaxCol = getColCount(newRow) - 1;
292
- setFocusedCol((col) => {
293
- const constrainedCol = Math.min(col, newMaxCol);
294
- notifyFocusChange(newRow, constrainedCol);
295
- return constrainedCol;
296
- });
297
- }
298
- return newRow;
299
- });
300
- }, [rows, getColCount, wrap, notifyFocusChange]);
301
- const moveUp = react.useCallback(() => {
302
- setFocusedRow((currentRow) => {
303
- let newRow = currentRow - 1;
304
- if (newRow < 0) {
305
- newRow = wrap ? rows - 1 : 0;
306
- }
307
- if (newRow !== currentRow) {
308
- const newMaxCol = getColCount(newRow) - 1;
309
- setFocusedCol((col) => {
310
- const constrainedCol = Math.min(col, newMaxCol);
311
- notifyFocusChange(newRow, constrainedCol);
312
- return constrainedCol;
313
- });
314
- }
315
- return newRow;
316
- });
317
- }, [rows, getColCount, wrap, notifyFocusChange]);
318
- const moveToStart = react.useCallback(() => {
319
- setFocusedCol(0);
320
- notifyFocusChange(focusedRow, 0);
321
- }, [focusedRow, notifyFocusChange]);
322
- const moveToEnd = react.useCallback(() => {
323
- const maxCol = getColCount(focusedRow) - 1;
324
- setFocusedCol(maxCol);
325
- notifyFocusChange(focusedRow, maxCol);
326
- }, [focusedRow, getColCount, notifyFocusChange]);
327
- const setFocus = react.useCallback(
328
- (row, col) => {
329
- const constrainedRow = Math.max(0, Math.min(row, rows - 1));
330
- const maxCol = getColCount(constrainedRow) - 1;
331
- const constrainedCol = Math.max(0, Math.min(col, maxCol));
332
- setFocusedRow(constrainedRow);
333
- setFocusedCol(constrainedCol);
334
- notifyFocusChange(constrainedRow, constrainedCol);
335
- },
336
- [rows, getColCount, notifyFocusChange]
337
- );
338
- const getTabIndex = react.useCallback(
339
- (row, col) => {
340
- return row === focusedRow && col === focusedCol ? 0 : -1;
341
- },
342
- [focusedRow, focusedCol]
343
- );
344
- return react.useMemo(
345
- () => ({
346
- focusedRow,
347
- focusedCol,
348
- moveRight,
349
- moveLeft,
350
- moveDown,
351
- moveUp,
352
- moveToStart,
353
- moveToEnd,
354
- setFocus,
355
- getTabIndex
356
- }),
357
- [
358
- focusedRow,
359
- focusedCol,
360
- moveRight,
361
- moveLeft,
362
- moveDown,
363
- moveUp,
364
- moveToStart,
365
- moveToEnd,
366
- setFocus,
367
- getTabIndex
368
- ]
369
- );
370
- }
371
- var ChartContext = react.createContext(null);
372
- function useChartContext() {
373
- const context = react.useContext(ChartContext);
374
- if (!context) {
375
- throw new Error("useChartContext must be used within a ChartProvider");
376
- }
377
- return context;
378
- }
379
- function getAllLabels(data) {
380
- const labelSet = /* @__PURE__ */ new Set();
381
- data.forEach((series) => {
382
- series.data.forEach((point) => {
383
- labelSet.add(point.label);
384
- });
385
- });
386
- return Array.from(labelSet);
387
- }
388
- function calculateScales(data, dimensions, startAtZero) {
389
- const allLabels = getAllLabels(data);
390
- const { plotArea } = dimensions;
391
- let dataMin = Infinity;
392
- let dataMax = -Infinity;
393
- data.forEach((series) => {
394
- series.data.forEach((point) => {
395
- dataMin = Math.min(dataMin, point.value);
396
- dataMax = Math.max(dataMax, point.value);
397
- });
398
- });
399
- if (!isFinite(dataMin) || !isFinite(dataMax)) {
400
- dataMin = 0;
401
- dataMax = 100;
402
- }
403
- const yMin = startAtZero ? Math.min(0, dataMin) : dataMin;
404
- const yTicks = calculateYAxisTicks(yMin, dataMax);
405
- const yMax = yTicks[yTicks.length - 1] || dataMax;
406
- const adjustedYMin = yTicks[0] || yMin;
407
- const xScale = (labelIndex) => {
408
- if (allLabels.length <= 1) return plotArea.x + plotArea.width / 2;
409
- const step = plotArea.width / allLabels.length;
410
- return plotArea.x + step * labelIndex + step / 2;
411
- };
412
- const yRange = yMax - adjustedYMin || 1;
413
- const yScale = (value) => {
414
- const normalized = (value - adjustedYMin) / yRange;
415
- return plotArea.y + plotArea.height * (1 - normalized);
416
- };
417
- return {
418
- allLabels,
419
- xScale,
420
- yScale,
421
- yMin: adjustedYMin,
422
- yMax,
423
- yTicks
424
- };
425
- }
426
- function ChartProvider({
427
- children,
428
- containerRef,
429
- data,
430
- type,
431
- title,
432
- description,
433
- xAxisLabel,
434
- yAxisLabel,
435
- showTooltip,
436
- showGrid,
437
- showLegend,
438
- announceTrends,
439
- trendUpFormat,
440
- trendDownFormat,
441
- startAtZero,
442
- reducedMotion = true,
443
- onPointFocus: onPointFocusProp,
444
- onPointSelect: onPointSelectProp
445
- }) {
446
- const [selectedPoint, setSelectedPoint] = react.useState(null);
447
- const [announcement, setAnnouncement] = react.useState("");
448
- const [isTooltipVisible, setIsTooltipVisible] = react.useState(false);
449
- const [tooltipPoint, setTooltipPoint] = react.useState(null);
450
- const [currentTrend, setCurrentTrend] = react.useState(null);
451
- const [currentTrendDirection, setCurrentTrendDirection] = react.useState(null);
452
- const dimensions = useChartDimensions(containerRef);
453
- const scales = react.useMemo(
454
- () => calculateScales(data, dimensions, startAtZero),
455
- [data, dimensions, startAtZero]
456
- );
457
- void react.useMemo(
458
- () => Math.max(...data.map((s) => s.data.length)),
459
- [data]
460
- );
461
- const formatTrendText = react.useCallback(
462
- (percent, isUp) => {
463
- const format = isUp ? trendUpFormat : trendDownFormat;
464
- return format.replace("{percent}", String(percent));
465
- },
466
- [trendUpFormat, trendDownFormat]
467
- );
468
- const getTrendInfo = (seriesIndex, pointIndex) => {
469
- if (!announceTrends || pointIndex <= 0) return null;
470
- const series = data[seriesIndex];
471
- if (!series) return null;
472
- const currentPoint = series.data[pointIndex];
473
- const previousPoint = series.data[pointIndex - 1];
474
- if (!currentPoint || !previousPoint || previousPoint.value === 0) {
475
- return null;
476
- }
477
- const change = (currentPoint.value - previousPoint.value) / Math.abs(previousPoint.value) * 100;
478
- const isUp = change >= 0;
479
- const percent = Math.abs(Math.round(change));
480
- return {
481
- text: formatTrendText(percent, isUp),
482
- direction: isUp ? "up" : "down"
483
- };
484
- };
485
- const roving = useRovingTabIndex2D({
486
- rows: data.length,
487
- cols: (row) => data[row]?.data.length || 0,
488
- onFocusChange: (seriesIndex, pointIndex) => {
489
- const series = data[seriesIndex];
490
- const point = series?.data[pointIndex];
491
- if (!series || !point) return;
492
- const trendInfo = getTrendInfo(seriesIndex, pointIndex);
493
- setCurrentTrend(trendInfo?.text ?? null);
494
- setCurrentTrendDirection(trendInfo?.direction ?? null);
495
- const position = `${pointIndex + 1} of ${series.data.length}`;
496
- const seriesInfo = data.length > 1 ? `${series.name}: ` : "";
497
- const formattedValue = formatValue(point.value);
498
- const announcementTrend = trendInfo ? `, ${trendInfo.text}` : "";
499
- setAnnouncement(
500
- `${seriesInfo}${point.label}, ${formattedValue}${announcementTrend}. ${position}.`
501
- );
502
- if (showTooltip) {
503
- setIsTooltipVisible(true);
504
- setTooltipPoint({ series: seriesIndex, point: pointIndex });
505
- }
506
- onPointFocusProp?.(point, seriesIndex, pointIndex);
507
- }
508
- });
509
- const formatValue = react.useCallback((value2) => {
510
- const locale = typeof navigator !== "undefined" ? navigator.language : "en-US";
511
- return new Intl.NumberFormat(locale).format(value2);
512
- }, []);
513
- const calculateTrendForPoint = react.useCallback(
514
- (seriesIndex, pointIndex) => {
515
- if (!announceTrends) return null;
516
- if (pointIndex <= 0) return null;
517
- const series = data[seriesIndex];
518
- if (!series) return null;
519
- const currentPoint = series.data[pointIndex];
520
- const previousPoint = series.data[pointIndex - 1];
521
- if (!currentPoint || !previousPoint || previousPoint.value === 0) {
522
- return null;
523
- }
524
- const change = (currentPoint.value - previousPoint.value) / Math.abs(previousPoint.value) * 100;
525
- const isUp = change >= 0;
526
- const percent = Math.abs(Math.round(change));
527
- return {
528
- text: formatTrendText(percent, isUp),
529
- direction: isUp ? "up" : "down"
530
- };
531
- },
532
- [data, announceTrends, formatTrendText]
533
- );
534
- const getSeriesColor = react.useCallback((seriesIndex) => {
535
- return DEFAULT_SERIES_COLORS[seriesIndex % DEFAULT_SERIES_COLORS.length] ?? "var(--chart-1)";
536
- }, []);
537
- const getPointCoordinates = react.useCallback(
538
- (seriesIndex, pointIndex) => {
539
- const series = data[seriesIndex];
540
- const point = series?.data[pointIndex];
541
- if (!series || !point) return null;
542
- const labelIndex = scales.allLabels.indexOf(point.label);
543
- if (labelIndex === -1) return null;
544
- return {
545
- x: scales.xScale(labelIndex),
546
- y: scales.yScale(point.value)
547
- };
548
- },
549
- [data, scales]
550
- );
551
- const setFocus = react.useCallback(
552
- (series, point) => {
553
- roving.setFocus(series, point);
554
- },
555
- [roving]
556
- );
557
- const selectPoint = react.useCallback(
558
- (series, point) => {
559
- setSelectedPoint({ series, point });
560
- const seriesData = data[series];
561
- const pointData = seriesData?.data[point];
562
- if (seriesData && pointData) {
563
- onPointSelectProp?.(pointData, series, point);
564
- }
565
- },
566
- [data, onPointSelectProp]
567
- );
568
- const clearSelection = react.useCallback(() => {
569
- setSelectedPoint(null);
570
- }, []);
571
- const announce = react.useCallback((message) => {
572
- setAnnouncement(message);
573
- }, []);
574
- const showTooltipAt = react.useCallback(
575
- (series, point) => {
576
- if (showTooltip) {
577
- setIsTooltipVisible(true);
578
- setTooltipPoint({ series, point });
579
- const trendResult = calculateTrendForPoint(series, point);
580
- setCurrentTrend(trendResult?.text ?? null);
581
- setCurrentTrendDirection(trendResult?.direction ?? null);
582
- }
583
- },
584
- [showTooltip, calculateTrendForPoint]
585
- );
586
- const hideTooltip = react.useCallback(() => {
587
- setIsTooltipVisible(false);
588
- setTooltipPoint(null);
589
- }, []);
590
- const value = react.useMemo(
591
- () => ({
592
- // State
593
- focusedSeries: roving.focusedRow,
594
- focusedPoint: roving.focusedCol,
595
- selectedPoint,
596
- announcement,
597
- isTooltipVisible,
598
- tooltipPoint,
599
- currentTrend,
600
- currentTrendDirection,
601
- // Computed
602
- dimensions,
603
- scales,
604
- // Props passthrough
605
- data,
606
- type,
607
- title,
608
- description,
609
- xAxisLabel,
610
- yAxisLabel,
611
- showTooltip,
612
- showGrid,
613
- showLegend,
614
- announceTrends,
615
- trendUpFormat,
616
- trendDownFormat,
617
- startAtZero,
618
- reducedMotion,
619
- // Actions
620
- setFocus,
621
- selectPoint,
622
- clearSelection,
623
- announce,
624
- showTooltipAt,
625
- hideTooltip,
626
- // Helpers
627
- getTabIndex: roving.getTabIndex,
628
- formatValue,
629
- getSeriesColor,
630
- getPointCoordinates
631
- }),
632
- [
633
- roving.focusedRow,
634
- roving.focusedCol,
635
- roving.getTabIndex,
636
- selectedPoint,
637
- announcement,
638
- isTooltipVisible,
639
- tooltipPoint,
640
- currentTrend,
641
- currentTrendDirection,
642
- dimensions,
643
- scales,
644
- data,
645
- type,
646
- title,
647
- description,
648
- xAxisLabel,
649
- yAxisLabel,
650
- showTooltip,
651
- showGrid,
652
- showLegend,
653
- announceTrends,
654
- trendUpFormat,
655
- trendDownFormat,
656
- startAtZero,
657
- reducedMotion,
658
- setFocus,
659
- selectPoint,
660
- clearSelection,
661
- announce,
662
- showTooltipAt,
663
- hideTooltip,
664
- formatValue,
665
- getSeriesColor,
666
- getPointCoordinates
667
- ]
668
- );
669
- return /* @__PURE__ */ jsxRuntime.jsx(ChartContext.Provider, { value, children });
670
- }
671
- function ChartGrid({
672
- yTicks,
673
- yScale,
674
- plotX,
675
- plotWidth,
676
- visible
677
- }) {
678
- if (!visible) {
679
- return null;
680
- }
681
- return /* @__PURE__ */ jsxRuntime.jsx("g", { className: "chart-grid", "aria-hidden": "true", "data-testid": "chart-grid", children: yTicks.map((tick) => {
682
- const y = yScale(tick);
683
- return /* @__PURE__ */ jsxRuntime.jsx(
684
- "line",
685
- {
686
- x1: plotX,
687
- y1: y,
688
- x2: plotX + plotWidth,
689
- y2: y,
690
- stroke: GRID_COLOR,
691
- strokeWidth: GRID_STROKE_WIDTH,
692
- strokeOpacity: GRID_STROKE_OPACITY
693
- },
694
- tick
695
- );
696
- }) });
697
- }
698
- ChartGrid.displayName = "ChartGrid";
699
- function ChartAxis({
700
- allLabels,
701
- yTicks,
702
- xScale,
703
- yScale,
704
- plotArea,
705
- xAxisLabel,
706
- yAxisLabel,
707
- formatValue
708
- }) {
709
- const { x: plotX, y: plotY, width: plotWidth, height: plotHeight } = plotArea;
710
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { className: "chart-axis", "aria-hidden": "true", "data-testid": "chart-axis", children: [
711
- /* @__PURE__ */ jsxRuntime.jsx(
712
- "line",
713
- {
714
- x1: plotX,
715
- y1: plotY + plotHeight,
716
- x2: plotX + plotWidth,
717
- y2: plotY + plotHeight,
718
- stroke: AXIS_COLOR,
719
- strokeWidth: AXIS_STROKE_WIDTH
720
- }
721
- ),
722
- /* @__PURE__ */ jsxRuntime.jsx(
723
- "line",
724
- {
725
- x1: plotX,
726
- y1: plotY,
727
- x2: plotX,
728
- y2: plotY + plotHeight,
729
- stroke: AXIS_COLOR,
730
- strokeWidth: AXIS_STROKE_WIDTH
731
- }
732
- ),
733
- allLabels.map((label, index) => {
734
- const x = xScale(index);
735
- const y = plotY + plotHeight;
736
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
737
- /* @__PURE__ */ jsxRuntime.jsx(
738
- "line",
739
- {
740
- x1: x,
741
- y1: y,
742
- x2: x,
743
- y2: y + AXIS_TICK_SIZE,
744
- stroke: AXIS_COLOR,
745
- strokeWidth: AXIS_STROKE_WIDTH
746
- }
747
- ),
748
- /* @__PURE__ */ jsxRuntime.jsx(
749
- "text",
750
- {
751
- x,
752
- y: y + AXIS_TICK_SIZE + AXIS_LABEL_FONT_SIZE,
753
- textAnchor: "middle",
754
- fontSize: AXIS_LABEL_FONT_SIZE,
755
- fill: "currentColor",
756
- className: "text-[var(--content-foreground)]",
757
- children: label
758
- }
759
- )
760
- ] }, label);
761
- }),
762
- yTicks.map((tick) => {
763
- const x = plotX;
764
- const y = yScale(tick);
765
- return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
766
- /* @__PURE__ */ jsxRuntime.jsx(
767
- "line",
768
- {
769
- x1: x - AXIS_TICK_SIZE,
770
- y1: y,
771
- x2: x,
772
- y2: y,
773
- stroke: AXIS_COLOR,
774
- strokeWidth: AXIS_STROKE_WIDTH
775
- }
776
- ),
777
- /* @__PURE__ */ jsxRuntime.jsx(
778
- "text",
779
- {
780
- x: x - AXIS_TICK_SIZE - 4,
781
- y,
782
- textAnchor: "end",
783
- dominantBaseline: "middle",
784
- fontSize: AXIS_LABEL_FONT_SIZE,
785
- fill: "currentColor",
786
- className: "text-[var(--content-foreground)]",
787
- children: formatValue(tick)
788
- }
789
- )
790
- ] }, tick);
791
- }),
792
- /* @__PURE__ */ jsxRuntime.jsx(
793
- "text",
794
- {
795
- x: plotX + plotWidth / 2,
796
- y: plotY + plotHeight + 45,
797
- textAnchor: "middle",
798
- fontSize: AXIS_LABEL_FONT_SIZE,
799
- fill: "currentColor",
800
- className: "text-[var(--content-foreground)] font-medium",
801
- children: xAxisLabel
802
- }
803
- ),
804
- /* @__PURE__ */ jsxRuntime.jsx(
805
- "text",
806
- {
807
- x: 15,
808
- y: plotY + plotHeight / 2,
809
- textAnchor: "middle",
810
- fontSize: AXIS_LABEL_FONT_SIZE,
811
- fill: "currentColor",
812
- className: "text-[var(--content-foreground)] font-medium",
813
- transform: `rotate(-90, 15, ${plotY + plotHeight / 2})`,
814
- children: yAxisLabel
815
- }
816
- )
817
- ] });
818
- }
819
- ChartAxis.displayName = "ChartAxis";
820
- function ChartDataPoint({
821
- type,
822
- x,
823
- y,
824
- width = POINT_RADIUS * 2,
825
- height = POINT_RADIUS * 2,
826
- color,
827
- seriesIndex,
828
- pointIndex,
829
- tabIndex,
830
- label,
831
- isSelected = false,
832
- onFocus,
833
- onBlur,
834
- onHover,
835
- onHoverEnd,
836
- onKeyDown
837
- }) {
838
- const ref = react.useRef(null);
839
- const { isFocusVisible, focusProps } = reactAria.useFocusRing();
840
- react.useEffect(() => {
841
- if (tabIndex === 0 && ref.current) {
842
- const activeElement = document.activeElement;
843
- const isChartFocused = activeElement?.closest('[role="graphics-document"]') !== null;
844
- if (isChartFocused) {
845
- ref.current.focus();
846
- }
847
- }
848
- }, [tabIndex]);
849
- const hitAreaSize = Math.max(MIN_TOUCH_TARGET, width, height);
850
- const hitX = type === "bar" ? x - (hitAreaSize - width) / 2 : x - hitAreaSize / 2;
851
- const hitY = type === "bar" ? y - (hitAreaSize - height) / 2 : y - hitAreaSize / 2;
852
- const handleFocus = (event) => {
853
- focusProps.onFocus?.(event);
854
- onFocus?.(seriesIndex, pointIndex);
855
- };
856
- const handleBlur = (event) => {
857
- focusProps.onBlur?.(event);
858
- onBlur?.();
859
- };
860
- const handleMouseEnter = (_event) => {
861
- onHover?.(seriesIndex, pointIndex);
862
- };
863
- const handleMouseLeave = (_event) => {
864
- onHoverEnd?.();
865
- };
866
- const state = isSelected ? "selected" : isFocusVisible ? "focused" : "default";
867
- return /* @__PURE__ */ jsxRuntime.jsxs(
868
- "g",
869
- {
870
- ref,
871
- role: "listitem",
872
- tabIndex,
873
- "aria-label": label,
874
- className: dataPointVariants({ type, state }),
875
- onFocus: handleFocus,
876
- onBlur: handleBlur,
877
- onMouseEnter: handleMouseEnter,
878
- onMouseLeave: handleMouseLeave,
879
- onKeyDown,
880
- "data-testid": `chart-point-${seriesIndex}-${pointIndex}`,
881
- children: [
882
- /* @__PURE__ */ jsxRuntime.jsx(
883
- "rect",
884
- {
885
- x: hitX,
886
- y: hitY,
887
- width: hitAreaSize,
888
- height: hitAreaSize,
889
- fill: "transparent",
890
- className: "cursor-pointer"
891
- }
892
- ),
893
- type === "bar" ? /* @__PURE__ */ jsxRuntime.jsx(
894
- "rect",
895
- {
896
- x,
897
- y,
898
- width,
899
- height,
900
- rx: BAR_RADIUS,
901
- ry: BAR_RADIUS,
902
- fill: color,
903
- className: "chart-bar"
904
- }
905
- ) : /* @__PURE__ */ jsxRuntime.jsx(
906
- "circle",
907
- {
908
- cx: x,
909
- cy: y,
910
- r: POINT_RADIUS,
911
- fill: color,
912
- className: "chart-point"
913
- }
914
- ),
915
- isFocusVisible && (type === "bar" ? /* @__PURE__ */ jsxRuntime.jsx(
916
- "rect",
917
- {
918
- x: x - FOCUS_RING_OFFSET,
919
- y: y - FOCUS_RING_OFFSET,
920
- width: width + FOCUS_RING_OFFSET * 2,
921
- height: height + FOCUS_RING_OFFSET * 2,
922
- rx: BAR_RADIUS + FOCUS_RING_OFFSET,
923
- ry: BAR_RADIUS + FOCUS_RING_OFFSET,
924
- fill: "none",
925
- stroke: "var(--ring)",
926
- strokeWidth: FOCUS_RING_WIDTH,
927
- className: "chart-focus-ring"
928
- }
929
- ) : /* @__PURE__ */ jsxRuntime.jsx(
930
- "circle",
931
- {
932
- cx: x,
933
- cy: y,
934
- r: POINT_RADIUS + FOCUS_RING_OFFSET,
935
- fill: "none",
936
- stroke: "var(--ring)",
937
- strokeWidth: FOCUS_RING_WIDTH,
938
- className: "chart-focus-ring"
939
- }
940
- ))
941
- ]
942
- }
943
- );
944
- }
945
- ChartDataPoint.displayName = "ChartDataPoint";
946
- function ChartBarSeries({
947
- series,
948
- seriesIndex,
949
- totalSeries,
950
- dimensions,
951
- scales,
952
- color,
953
- getTabIndex,
954
- selectedPoint,
955
- onPointFocus,
956
- onPointBlur,
957
- onPointHover,
958
- onPointHoverEnd,
959
- onKeyDown,
960
- formatValue
961
- }) {
962
- const { plotArea } = dimensions;
963
- const { allLabels, xScale: _xScale, yScale, yMin } = scales;
964
- const { barWidth, getBarX } = calculateBarLayout(
965
- plotArea.width,
966
- allLabels.length,
967
- totalSeries,
968
- plotArea.x
969
- );
970
- const baselineY = yScale(Math.max(0, yMin));
971
- return /* @__PURE__ */ jsxRuntime.jsx(
972
- "g",
973
- {
974
- className: "chart-bar-series",
975
- role: "list",
976
- "aria-label": `${series.name} series`,
977
- "data-testid": `chart-series-${seriesIndex}`,
978
- children: series.data.map((point, pointIndex) => {
979
- const labelIndex = allLabels.indexOf(point.label);
980
- if (labelIndex === -1) return null;
981
- const barX = getBarX(labelIndex, seriesIndex);
982
- const barY = yScale(point.value);
983
- const barHeight = Math.abs(baselineY - barY);
984
- const finalBarY = point.value >= 0 ? barY : baselineY;
985
- const isSelected = selectedPoint?.series === seriesIndex && selectedPoint?.point === pointIndex;
986
- const accessibleLabel = `${series.name}: ${point.label}, ${formatValue(point.value)}`;
987
- return /* @__PURE__ */ jsxRuntime.jsx(
988
- ChartDataPoint,
989
- {
990
- type: "bar",
991
- x: barX,
992
- y: finalBarY,
993
- width: barWidth,
994
- height: barHeight,
995
- color,
996
- seriesIndex,
997
- pointIndex,
998
- tabIndex: getTabIndex(seriesIndex, pointIndex),
999
- label: accessibleLabel,
1000
- isSelected,
1001
- onFocus: onPointFocus,
1002
- onBlur: onPointBlur,
1003
- onHover: onPointHover,
1004
- onHoverEnd: onPointHoverEnd,
1005
- onKeyDown
1006
- },
1007
- `${point.label}-${pointIndex}`
1008
- );
1009
- })
1010
- }
1011
- );
1012
- }
1013
- ChartBarSeries.displayName = "ChartBarSeries";
1014
- function ChartLineSeries({
1015
- series,
1016
- seriesIndex,
1017
- scales,
1018
- color,
1019
- pattern,
1020
- getTabIndex,
1021
- selectedPoint,
1022
- onPointFocus,
1023
- onPointBlur,
1024
- onPointHover,
1025
- onPointHoverEnd,
1026
- onKeyDown,
1027
- formatValue
1028
- }) {
1029
- const { allLabels, xScale, yScale } = scales;
1030
- const points = react.useMemo(() => {
1031
- return series.data.map((point, pointIndex) => {
1032
- const labelIndex = allLabels.indexOf(point.label);
1033
- if (labelIndex === -1) return null;
1034
- return {
1035
- x: xScale(labelIndex),
1036
- y: yScale(point.value),
1037
- point,
1038
- pointIndex,
1039
- labelIndex
1040
- };
1041
- }).filter((p) => p !== null).sort((a, b) => a.labelIndex - b.labelIndex);
1042
- }, [series.data, allLabels, xScale, yScale]);
1043
- const pathD = react.useMemo(() => {
1044
- if (points.length === 0) return "";
1045
- return points.map((p, i) => `${i === 0 ? "M" : "L"} ${p.x} ${p.y}`).join(" ");
1046
- }, [points]);
1047
- const strokeDasharray = LINE_PATTERNS[pattern] === "none" ? void 0 : LINE_PATTERNS[pattern];
1048
- return /* @__PURE__ */ jsxRuntime.jsxs(
1049
- "g",
1050
- {
1051
- className: "chart-line-series",
1052
- role: "list",
1053
- "aria-label": `${series.name} series`,
1054
- "data-testid": `chart-series-${seriesIndex}`,
1055
- children: [
1056
- /* @__PURE__ */ jsxRuntime.jsx(
1057
- "path",
1058
- {
1059
- d: pathD,
1060
- fill: "none",
1061
- stroke: color,
1062
- strokeWidth: LINE_STROKE_WIDTH,
1063
- strokeDasharray,
1064
- strokeLinecap: "round",
1065
- strokeLinejoin: "round",
1066
- className: "chart-line",
1067
- "aria-hidden": "true"
1068
- }
1069
- ),
1070
- points.map(({ x, y, point, pointIndex }) => {
1071
- const isSelected = selectedPoint?.series === seriesIndex && selectedPoint?.point === pointIndex;
1072
- const accessibleLabel = `${series.name}: ${point.label}, ${formatValue(point.value)}`;
1073
- return /* @__PURE__ */ jsxRuntime.jsx(
1074
- ChartDataPoint,
1075
- {
1076
- type: "line",
1077
- x,
1078
- y,
1079
- color,
1080
- seriesIndex,
1081
- pointIndex,
1082
- tabIndex: getTabIndex(seriesIndex, pointIndex),
1083
- label: accessibleLabel,
1084
- isSelected,
1085
- onFocus: onPointFocus,
1086
- onBlur: onPointBlur,
1087
- onHover: onPointHover,
1088
- onHoverEnd: onPointHoverEnd,
1089
- onKeyDown
1090
- },
1091
- `${point.label}-${pointIndex}`
1092
- );
1093
- })
1094
- ]
1095
- }
1096
- );
1097
- }
1098
- ChartLineSeries.displayName = "ChartLineSeries";
1099
- function ChartSVG({
1100
- type,
1101
- data,
1102
- dimensions,
1103
- scales,
1104
- titleId,
1105
- descId,
1106
- showGrid,
1107
- xAxisLabel,
1108
- yAxisLabel,
1109
- getSeriesColor,
1110
- getTabIndex,
1111
- selectedPoint,
1112
- formatValue,
1113
- onKeyDown,
1114
- onPointFocus,
1115
- onPointBlur,
1116
- onPointHover,
1117
- onPointHoverEnd
1118
- }) {
1119
- const { width, height, plotArea } = dimensions;
1120
- const { allLabels, xScale, yScale, yTicks } = scales;
1121
- return /* @__PURE__ */ jsxRuntime.jsxs(
1122
- "svg",
1123
- {
1124
- role: "graphics-document",
1125
- "aria-roledescription": `${type} chart`,
1126
- "aria-labelledby": titleId,
1127
- "aria-describedby": descId,
1128
- viewBox: `0 0 ${width} ${height}`,
1129
- className: "h-full w-full",
1130
- style: { maxHeight: height },
1131
- "data-testid": "chart-svg",
1132
- children: [
1133
- /* @__PURE__ */ jsxRuntime.jsx(
1134
- ChartGrid,
1135
- {
1136
- yTicks,
1137
- yScale,
1138
- plotX: plotArea.x,
1139
- plotWidth: plotArea.width,
1140
- visible: showGrid
1141
- }
1142
- ),
1143
- /* @__PURE__ */ jsxRuntime.jsx(
1144
- ChartAxis,
1145
- {
1146
- allLabels,
1147
- yTicks,
1148
- xScale,
1149
- yScale,
1150
- plotArea,
1151
- xAxisLabel,
1152
- yAxisLabel,
1153
- formatValue
1154
- }
1155
- ),
1156
- data.map((series, seriesIndex) => {
1157
- const color = series.color || getSeriesColor(seriesIndex);
1158
- const pattern = series.pattern || "solid";
1159
- if (type === "bar") {
1160
- return /* @__PURE__ */ jsxRuntime.jsx(
1161
- ChartBarSeries,
1162
- {
1163
- series,
1164
- seriesIndex,
1165
- totalSeries: data.length,
1166
- dimensions,
1167
- scales,
1168
- color,
1169
- getTabIndex,
1170
- selectedPoint,
1171
- onPointFocus,
1172
- onPointBlur,
1173
- onPointHover,
1174
- onPointHoverEnd,
1175
- onKeyDown,
1176
- formatValue
1177
- },
1178
- series.name
1179
- );
1180
- }
1181
- return /* @__PURE__ */ jsxRuntime.jsx(
1182
- ChartLineSeries,
1183
- {
1184
- series,
1185
- seriesIndex,
1186
- scales,
1187
- color,
1188
- pattern,
1189
- getTabIndex,
1190
- selectedPoint,
1191
- onPointFocus,
1192
- onPointBlur,
1193
- onPointHover,
1194
- onPointHoverEnd,
1195
- onKeyDown,
1196
- formatValue
1197
- },
1198
- series.name
1199
- );
1200
- })
1201
- ]
1202
- }
1203
- );
1204
- }
1205
- ChartSVG.displayName = "ChartSVG";
1206
- var tooltipContentVariants = classVarianceAuthority.cva(
1207
- // Base styles
1208
- [
1209
- "z-50 overflow-hidden rounded-md px-3 py-1.5 text-sm shadow-md",
1210
- // Inverted colors for high contrast (7:1 ratio)
1211
- "bg-[var(--content-foreground)] text-[var(--content-background)]",
1212
- // Entry animation base
1213
- "animate-in fade-in-0",
1214
- // Exit animation
1215
- "data-[exiting]:animate-out data-[exiting]:fade-out-0"
1216
- ],
1217
- {
1218
- variants: {
1219
- side: {
1220
- top: [
1221
- "motion-safe:slide-in-from-bottom-2",
1222
- "data-[exiting]:motion-safe:slide-out-to-bottom-2"
1223
- ],
1224
- bottom: [
1225
- "motion-safe:slide-in-from-top-2",
1226
- "data-[exiting]:motion-safe:slide-out-to-top-2"
1227
- ],
1228
- left: [
1229
- "motion-safe:slide-in-from-right-2",
1230
- "data-[exiting]:motion-safe:slide-out-to-right-2"
1231
- ],
1232
- right: [
1233
- "motion-safe:slide-in-from-left-2",
1234
- "data-[exiting]:motion-safe:slide-out-to-left-2"
1235
- ]
1236
- }
1237
- },
1238
- defaultVariants: {
1239
- side: "top"
1240
- }
1241
- }
1242
- );
1243
- classVarianceAuthority.cva(
1244
- // Base arrow styles
1245
- "fill-[var(--content-foreground)]",
1246
- {
1247
- variants: {
1248
- side: {
1249
- top: "rotate-180",
1250
- bottom: "rotate-0",
1251
- left: "rotate-90",
1252
- right: "-rotate-90"
1253
- }
1254
- },
1255
- defaultVariants: {
1256
- side: "top"
1257
- }
1258
- }
1259
- );
1260
- var TOOLTIP_GAP = 8;
1261
- function ChartTooltip({
1262
- visible,
1263
- x,
1264
- y,
1265
- seriesName,
1266
- label,
1267
- value,
1268
- chartWidth,
1269
- chartHeight,
1270
- trend,
1271
- trendDirection
1272
- }) {
1273
- const tooltipRef = react.useRef(null);
1274
- const [position, setPosition] = react.useState({ left: 0, top: 0 });
1275
- const [side, setSide] = react.useState("top");
1276
- react.useEffect(() => {
1277
- if (!visible || !tooltipRef.current) return;
1278
- const tooltip = tooltipRef.current;
1279
- const tooltipRect = tooltip.getBoundingClientRect();
1280
- const tooltipWidth = tooltipRect.width || 120;
1281
- const tooltipHeight = tooltipRect.height || 50;
1282
- let left = x - tooltipWidth / 2;
1283
- let top = y - tooltipHeight - TOOLTIP_GAP;
1284
- let newSide = "top";
1285
- if (top < 0) {
1286
- top = y + TOOLTIP_GAP + 16;
1287
- newSide = "bottom";
1288
- }
1289
- if (left < 0) {
1290
- left = 0;
1291
- }
1292
- if (left + tooltipWidth > chartWidth) {
1293
- left = chartWidth - tooltipWidth;
1294
- }
1295
- setPosition({ left, top });
1296
- setSide(newSide);
1297
- }, [visible, x, y, chartWidth]);
1298
- if (!visible) {
1299
- return null;
1300
- }
1301
- return /* @__PURE__ */ jsxRuntime.jsxs(
1302
- "div",
1303
- {
1304
- ref: tooltipRef,
1305
- "aria-hidden": "true",
1306
- className: tooltipContentVariants({ side }),
1307
- style: {
1308
- position: "absolute",
1309
- left: position.left,
1310
- top: position.top,
1311
- pointerEvents: "none"
1312
- },
1313
- "data-testid": "chart-tooltip",
1314
- children: [
1315
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: seriesName }),
1316
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "opacity-80", children: [
1317
- label,
1318
- ": ",
1319
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: value })
1320
- ] }),
1321
- trend && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs opacity-70 mt-1 flex items-center gap-1", children: [
1322
- trendDirection === "up" ? /* @__PURE__ */ jsxRuntime.jsx(
1323
- "svg",
1324
- {
1325
- "aria-hidden": "true",
1326
- className: "h-3 w-3 text-green-500",
1327
- viewBox: "0 0 20 20",
1328
- fill: "currentColor",
1329
- children: /* @__PURE__ */ jsxRuntime.jsx(
1330
- "path",
1331
- {
1332
- fillRule: "evenodd",
1333
- d: "M10 17a.75.75 0 01-.75-.75V5.612L5.29 9.77a.75.75 0 01-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0110 17z",
1334
- clipRule: "evenodd"
1335
- }
1336
- )
1337
- }
1338
- ) : trendDirection === "down" ? /* @__PURE__ */ jsxRuntime.jsx(
1339
- "svg",
1340
- {
1341
- "aria-hidden": "true",
1342
- className: "h-3 w-3 text-red-500",
1343
- viewBox: "0 0 20 20",
1344
- fill: "currentColor",
1345
- children: /* @__PURE__ */ jsxRuntime.jsx(
1346
- "path",
1347
- {
1348
- fillRule: "evenodd",
1349
- d: "M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z",
1350
- clipRule: "evenodd"
1351
- }
1352
- )
1353
- }
1354
- ) : null,
1355
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: trend })
1356
- ] })
1357
- ]
1358
- }
1359
- );
1360
- }
1361
- ChartTooltip.displayName = "ChartTooltip";
1362
- function ChartLegend({
1363
- data,
1364
- visible,
1365
- type,
1366
- getSeriesColor
1367
- }) {
1368
- if (!visible) {
1369
- return null;
1370
- }
1371
- return /* @__PURE__ */ jsxRuntime.jsx(
1372
- "div",
1373
- {
1374
- className: legendVariants({ visible }),
1375
- role: "list",
1376
- "aria-label": "Chart legend",
1377
- "data-testid": "chart-legend",
1378
- children: data.map((series, index) => {
1379
- const color = series.color || getSeriesColor(index);
1380
- const pattern = series.pattern || "solid";
1381
- return /* @__PURE__ */ jsxRuntime.jsxs(
1382
- "div",
1383
- {
1384
- role: "listitem",
1385
- className: "flex items-center gap-2",
1386
- children: [
1387
- type === "bar" ? /* @__PURE__ */ jsxRuntime.jsx(
1388
- "div",
1389
- {
1390
- className: "h-3 w-3 rounded-sm",
1391
- style: { backgroundColor: color },
1392
- "data-testid": `legend-color-${index}`,
1393
- "aria-hidden": "true"
1394
- }
1395
- ) : /* @__PURE__ */ jsxRuntime.jsx(
1396
- "svg",
1397
- {
1398
- width: "24",
1399
- height: "12",
1400
- "aria-hidden": "true",
1401
- "data-testid": `legend-color-${index}`,
1402
- children: /* @__PURE__ */ jsxRuntime.jsx(
1403
- "line",
1404
- {
1405
- x1: "0",
1406
- y1: "6",
1407
- x2: "24",
1408
- y2: "6",
1409
- stroke: color,
1410
- strokeWidth: "2",
1411
- strokeDasharray: LINE_PATTERNS[pattern] === "none" ? void 0 : LINE_PATTERNS[pattern]
1412
- }
1413
- )
1414
- }
1415
- ),
1416
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-[var(--content-foreground)]", children: series.name })
1417
- ]
1418
- },
1419
- series.name
1420
- );
1421
- })
1422
- }
1423
- );
1424
- }
1425
- ChartLegend.displayName = "ChartLegend";
1426
- function ChartAnnouncer({
1427
- announcement
1428
- }) {
1429
- return /* @__PURE__ */ jsxRuntime.jsx(reactAria.VisuallyHidden, { children: /* @__PURE__ */ jsxRuntime.jsx(
1430
- "div",
1431
- {
1432
- role: "status",
1433
- "aria-live": "polite",
1434
- "aria-atomic": "true",
1435
- "data-testid": "chart-announcer",
1436
- children: announcement
1437
- }
1438
- ) });
1439
- }
1440
- ChartAnnouncer.displayName = "ChartAnnouncer";
1441
- function ChartDataTable({
1442
- data,
1443
- title,
1444
- xAxisLabel,
1445
- yAxisLabel,
1446
- formatValue,
1447
- allLabels
1448
- }) {
1449
- const isSingleSeries = data.length === 1;
1450
- return /* @__PURE__ */ jsxRuntime.jsx(reactAria.VisuallyHidden, { children: /* @__PURE__ */ jsxRuntime.jsxs("table", { "data-testid": "chart-data-table", children: [
1451
- /* @__PURE__ */ jsxRuntime.jsxs("caption", { children: [
1452
- title,
1453
- " data table"
1454
- ] }),
1455
- /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1456
- /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: xAxisLabel }),
1457
- isSingleSeries ? /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", children: yAxisLabel }) : data.map((series) => /* @__PURE__ */ jsxRuntime.jsxs("th", { scope: "col", children: [
1458
- series.name,
1459
- " (",
1460
- yAxisLabel,
1461
- ")"
1462
- ] }, series.name))
1463
- ] }) }),
1464
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: allLabels.map((label) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1465
- /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "row", children: label }),
1466
- data.map((series) => {
1467
- const point = series.data.find((p) => p.label === label);
1468
- return /* @__PURE__ */ jsxRuntime.jsx("td", { children: point ? formatValue(point.value) : "\u2014" }, series.name);
1469
- })
1470
- ] }, label)) })
1471
- ] }) });
1472
- }
1473
- ChartDataTable.displayName = "ChartDataTable";
1474
- function useChartKeyboard(options) {
1475
- const { roving, onSelect, onClear, disabled = false } = options;
1476
- const onKeyDown = react.useCallback(
1477
- (event) => {
1478
- if (disabled) return;
1479
- switch (event.key) {
1480
- case "ArrowRight":
1481
- event.preventDefault();
1482
- roving.moveRight();
1483
- break;
1484
- case "ArrowLeft":
1485
- event.preventDefault();
1486
- roving.moveLeft();
1487
- break;
1488
- case "ArrowDown":
1489
- event.preventDefault();
1490
- roving.moveDown();
1491
- break;
1492
- case "ArrowUp":
1493
- event.preventDefault();
1494
- roving.moveUp();
1495
- break;
1496
- case "Home":
1497
- event.preventDefault();
1498
- roving.moveToStart();
1499
- break;
1500
- case "End":
1501
- event.preventDefault();
1502
- roving.moveToEnd();
1503
- break;
1504
- case "Enter":
1505
- case " ":
1506
- event.preventDefault();
1507
- onSelect?.();
1508
- break;
1509
- case "Escape":
1510
- event.preventDefault();
1511
- onClear?.();
1512
- break;
1513
- }
1514
- },
1515
- [roving, onSelect, onClear, disabled]
1516
- );
1517
- return { onKeyDown };
1518
- }
1519
- function ChartInner({ chartId }) {
1520
- const ctx = useChartContext();
1521
- const {
1522
- data,
1523
- type,
1524
- title,
1525
- xAxisLabel,
1526
- yAxisLabel,
1527
- showTooltip,
1528
- showGrid,
1529
- showLegend,
1530
- dimensions,
1531
- scales,
1532
- announcement,
1533
- isTooltipVisible,
1534
- tooltipPoint,
1535
- currentTrend,
1536
- currentTrendDirection,
1537
- selectedPoint,
1538
- getTabIndex,
1539
- formatValue,
1540
- getSeriesColor,
1541
- getPointCoordinates,
1542
- setFocus,
1543
- selectPoint,
1544
- clearSelection,
1545
- showTooltipAt,
1546
- hideTooltip
1547
- } = ctx;
1548
- const titleId = `${chartId}-title`;
1549
- const descId = `${chartId}-desc`;
1550
- const roving = useRovingTabIndex2D({
1551
- rows: data.length,
1552
- cols: (row) => data[row]?.data.length || 0,
1553
- onFocusChange: (seriesIndex, pointIndex) => {
1554
- setFocus(seriesIndex, pointIndex);
1555
- }
1556
- });
1557
- const { onKeyDown } = useChartKeyboard({
1558
- roving,
1559
- onSelect: () => {
1560
- selectPoint(roving.focusedRow, roving.focusedCol);
1561
- },
1562
- onClear: () => {
1563
- clearSelection();
1564
- }
1565
- });
1566
- const handlePointFocus = react.useCallback(
1567
- (seriesIndex, pointIndex) => {
1568
- roving.setFocus(seriesIndex, pointIndex);
1569
- showTooltipAt(seriesIndex, pointIndex);
1570
- },
1571
- [roving, showTooltipAt]
1572
- );
1573
- const handlePointBlur = react.useCallback(() => {
1574
- }, []);
1575
- const handlePointHover = react.useCallback(
1576
- (seriesIndex, pointIndex) => {
1577
- showTooltipAt(seriesIndex, pointIndex);
1578
- },
1579
- [showTooltipAt]
1580
- );
1581
- const handlePointHoverEnd = react.useCallback(() => {
1582
- hideTooltip();
1583
- }, [hideTooltip]);
1584
- const tooltipPosition = react.useMemo(() => {
1585
- if (!tooltipPoint) return { x: 0, y: 0 };
1586
- const coords = getPointCoordinates(tooltipPoint.series, tooltipPoint.point);
1587
- return coords || { x: 0, y: 0 };
1588
- }, [tooltipPoint, getPointCoordinates]);
1589
- const tooltipSeries = react.useMemo(
1590
- () => tooltipPoint ? data[tooltipPoint.series] : null,
1591
- [tooltipPoint, data]
1592
- );
1593
- const tooltipPointData = react.useMemo(
1594
- () => tooltipPoint ? tooltipSeries?.data[tooltipPoint.point] : null,
1595
- [tooltipPoint, tooltipSeries]
1596
- );
1597
- const shouldShowLegend = react.useMemo(
1598
- () => showLegend ?? data.length > 1,
1599
- [showLegend, data.length]
1600
- );
1601
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1602
- /* @__PURE__ */ jsxRuntime.jsx(ChartAnnouncer, { announcement }),
1603
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1604
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative z-0", children: /* @__PURE__ */ jsxRuntime.jsx(
1605
- ChartSVG,
1606
- {
1607
- type,
1608
- data,
1609
- dimensions,
1610
- scales,
1611
- titleId,
1612
- descId,
1613
- showGrid,
1614
- xAxisLabel,
1615
- yAxisLabel,
1616
- getSeriesColor,
1617
- getTabIndex,
1618
- selectedPoint,
1619
- formatValue,
1620
- onKeyDown,
1621
- onPointFocus: handlePointFocus,
1622
- onPointBlur: handlePointBlur,
1623
- onPointHover: handlePointHover,
1624
- onPointHoverEnd: handlePointHoverEnd
1625
- }
1626
- ) }),
1627
- showTooltip && tooltipPointData && tooltipSeries && /* @__PURE__ */ jsxRuntime.jsx(
1628
- ChartTooltip,
1629
- {
1630
- visible: isTooltipVisible,
1631
- x: tooltipPosition.x,
1632
- y: tooltipPosition.y,
1633
- seriesName: tooltipSeries.name,
1634
- label: tooltipPointData.label,
1635
- value: formatValue(tooltipPointData.value),
1636
- chartWidth: dimensions.width,
1637
- chartHeight: dimensions.height,
1638
- trend: currentTrend,
1639
- trendDirection: currentTrendDirection
1640
- }
1641
- )
1642
- ] }),
1643
- /* @__PURE__ */ jsxRuntime.jsx(
1644
- ChartLegend,
1645
- {
1646
- data,
1647
- visible: shouldShowLegend,
1648
- type,
1649
- getSeriesColor
1650
- }
1651
- ),
1652
- /* @__PURE__ */ jsxRuntime.jsx(
1653
- ChartDataTable,
1654
- {
1655
- data,
1656
- title,
1657
- xAxisLabel,
1658
- yAxisLabel,
1659
- formatValue,
1660
- allLabels: scales.allLabels
1661
- }
1662
- )
1663
- ] });
1664
- }
1665
- var ChartComponent = react.forwardRef(
1666
- ({
1667
- data,
1668
- type,
1669
- title,
1670
- description,
1671
- xAxisLabel,
1672
- yAxisLabel,
1673
- variant = "default",
1674
- className,
1675
- showTooltip = true,
1676
- showGrid = true,
1677
- showLegend,
1678
- announceTrends = false,
1679
- trendUpFormat = "up {percent}% from previous",
1680
- trendDownFormat = "down {percent}% from previous",
1681
- startAtZero = true,
1682
- reducedMotion,
1683
- onPointFocus,
1684
- onPointSelect,
1685
- id,
1686
- "aria-label": ariaLabel,
1687
- "aria-labelledby": ariaLabelledby,
1688
- "aria-describedby": ariaDescribedby,
1689
- "data-testid": dataTestId,
1690
- ...rest
1691
- }, ref) => {
1692
- const containerRef = react.useRef(null);
1693
- const generatedId = react.useId();
1694
- const chartId = id || `chart-${generatedId}`;
1695
- const titleId = `${chartId}-title`;
1696
- const descId = `${chartId}-desc`;
1697
- const shouldShowLegend = showLegend ?? data.length > 1;
1698
- return /* @__PURE__ */ jsxRuntime.jsxs(
1699
- "figure",
1700
- {
1701
- ref: (node) => {
1702
- if (typeof ref === "function") {
1703
- ref(node);
1704
- } else if (ref) {
1705
- ref.current = node;
1706
- }
1707
- containerRef.current = node;
1708
- },
1709
- id: chartId,
1710
- role: "figure",
1711
- "aria-label": ariaLabel,
1712
- "aria-labelledby": ariaLabelledby || titleId,
1713
- "aria-describedby": ariaDescribedby || descId,
1714
- "data-testid": dataTestId,
1715
- className: cn(chartVariants({ variant }), "p-4", className),
1716
- ...rest,
1717
- children: [
1718
- /* @__PURE__ */ jsxRuntime.jsxs("figcaption", { className: "sr-only", children: [
1719
- /* @__PURE__ */ jsxRuntime.jsx("span", { id: titleId, children: title }),
1720
- /* @__PURE__ */ jsxRuntime.jsx("span", { id: descId, children: description })
1721
- ] }),
1722
- /* @__PURE__ */ jsxRuntime.jsx(
1723
- ChartProvider,
1724
- {
1725
- containerRef,
1726
- data,
1727
- type,
1728
- title,
1729
- description,
1730
- xAxisLabel,
1731
- yAxisLabel,
1732
- showTooltip,
1733
- showGrid,
1734
- showLegend: shouldShowLegend,
1735
- announceTrends,
1736
- trendUpFormat,
1737
- trendDownFormat,
1738
- startAtZero,
1739
- reducedMotion,
1740
- onPointFocus,
1741
- onPointSelect,
1742
- children: /* @__PURE__ */ jsxRuntime.jsx(ChartInner, { chartId })
1743
- }
1744
- )
1745
- ]
1746
- }
1747
- );
1748
- }
1749
- );
1750
- ChartComponent.displayName = "Chart";
1751
- var Chart = react.memo(ChartComponent);
1752
- Chart.displayName = "Chart";
1753
- var BaseComponentPropsSchema = zod.z.object({
1754
- // Styling
1755
- className: zod.z.string().optional(),
1756
- // React
1757
- children: zod.z.any().optional(),
1758
- // ReactNode not directly supported by Zod
1759
- id: zod.z.string().optional(),
1760
- // Accessibility (WCAG 2.2 AA requirements)
1761
- "aria-label": zod.z.string().optional(),
1762
- "aria-labelledby": zod.z.string().optional(),
1763
- "aria-describedby": zod.z.string().optional(),
1764
- "aria-live": zod.z.enum(["off", "polite", "assertive"]).optional(),
1765
- "aria-hidden": zod.z.boolean().optional(),
1766
- // Testing & Development
1767
- "data-testid": zod.z.string().optional()
1768
- });
1769
-
1770
- // src/elements/Chart/Chart.types.ts
1771
- var DataPointSchema = zod.z.object({
1772
- /** X-axis label (e.g., "January", "Q1 2024") */
1773
- label: zod.z.string().min(1, "Label is required"),
1774
- /** Y-axis value */
1775
- value: zod.z.number(),
1776
- /** Extended description for screen readers */
1777
- description: zod.z.string().optional()
1778
- });
1779
- var LinePatternSchema = zod.z.enum(["solid", "dashed", "dotted"]);
1780
- var DataSeriesSchema = zod.z.object({
1781
- /** Series name for legend and announcements */
1782
- name: zod.z.string().min(1, "Series name is required"),
1783
- /** CSS color (defaults to --chart-1 through --chart-5) */
1784
- color: zod.z.string().optional(),
1785
- /** Line pattern for accessibility (defaults to solid) */
1786
- pattern: LinePatternSchema.optional().default("solid"),
1787
- /** Data points in this series */
1788
- data: zod.z.array(DataPointSchema).min(1, "At least one data point required")
1789
- });
1790
- var ChartTypeSchema = zod.z.enum(["bar", "line"]);
1791
- var ChartVariantSchema = zod.z.enum(["default", "muted"]);
1792
- var ChartPropsSchema = BaseComponentPropsSchema.extend({
1793
- // Required props
1794
- /** Chart data - maximum 5 series (Decision 11) */
1795
- data: zod.z.array(DataSeriesSchema).min(1, "At least one data series required").max(5, "Maximum 5 series allowed"),
1796
- /** Chart type: bar or line */
1797
- type: ChartTypeSchema,
1798
- /** Chart title (displayed and used for accessibility) */
1799
- title: zod.z.string().min(1, "Title is required"),
1800
- /** Chart description (displayed and used for accessibility) */
1801
- description: zod.z.string().min(1, "Description is required"),
1802
- /** X-axis label */
1803
- xAxisLabel: zod.z.string().min(1, "X-axis label is required"),
1804
- /** Y-axis label */
1805
- yAxisLabel: zod.z.string().min(1, "Y-axis label is required"),
1806
- // Optional - Styling
1807
- /** Visual variant */
1808
- variant: ChartVariantSchema.optional().default("default"),
1809
- // Optional - Features
1810
- /** Show tooltip on focus/hover (default: true) */
1811
- showTooltip: zod.z.boolean().optional().default(true),
1812
- /** Show grid lines (default: true) */
1813
- showGrid: zod.z.boolean().optional().default(true),
1814
- /** Show legend (auto: shown when >1 series) */
1815
- showLegend: zod.z.boolean().optional(),
1816
- /** Announce trend percentages (default: false) */
1817
- announceTrends: zod.z.boolean().optional().default(false),
1818
- /** ICU format string for upward trend (use {percent} placeholder) */
1819
- trendUpFormat: zod.z.string().optional().default("up {percent}% from previous"),
1820
- /** ICU format string for downward trend (use {percent} placeholder) */
1821
- trendDownFormat: zod.z.string().optional().default("down {percent}% from previous"),
1822
- /** Y-axis starts at zero (default: true) - Decision 6 */
1823
- startAtZero: zod.z.boolean().optional().default(true),
1824
- // Optional - Accessibility
1825
- /** Override reduced motion preference */
1826
- reducedMotion: zod.z.boolean().optional(),
1827
- // Optional - Events (use z.unknown() since Zod can't validate function types at runtime)
1828
- /** Called when a data point receives focus */
1829
- onPointFocus: zod.z.unknown().optional(),
1830
- /** Called when a data point is selected (Enter/Space) */
1831
- onPointSelect: zod.z.unknown().optional()
1832
- });
1833
-
1834
- exports.Chart = Chart;
1835
- exports.ChartPropsSchema = ChartPropsSchema;
1836
- exports.ChartTypeSchema = ChartTypeSchema;
1837
- exports.ChartVariantSchema = ChartVariantSchema;
1838
- exports.DataPointSchema = DataPointSchema;
1839
- exports.DataSeriesSchema = DataSeriesSchema;
1840
- exports.LinePatternSchema = LinePatternSchema;
1841
- exports.chartVariants = chartVariants;
1842
- exports.dataPointVariants = dataPointVariants;
1843
- //# sourceMappingURL=index.js.map
2
+ 'use strict';var react=require('react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge'),classVarianceAuthority=require('class-variance-authority'),jsxRuntime=require('react/jsx-runtime'),reactAria=require('react-aria');require('react-aria-components');var zod=require('zod');function se(...o){return tailwindMerge.twMerge(clsx.clsx(o))}var we=classVarianceAuthority.cva("relative w-full overflow-hidden rounded-lg border",{variants:{variant:{default:"border-[var(--border)] bg-[var(--content-background)]",muted:"border-[var(--accent-background)] bg-[var(--accent-background)]/50"}},defaultVariants:{variant:"default"}}),Ee=classVarianceAuthority.cva("cursor-pointer focus-visible:outline-none",{variants:{type:{bar:"",line:"stroke-2"},state:{default:"",focused:"",selected:"opacity-80"}},defaultVariants:{type:"bar",state:"default"}});classVarianceAuthority.cva("pointer-events-none",{variants:{visible:{true:"opacity-100",false:"opacity-0"}},defaultVariants:{visible:false}});var Je=classVarianceAuthority.cva("flex flex-wrap items-center justify-center gap-4 px-4 py-2",{variants:{visible:{true:"",false:"hidden"}},defaultVariants:{visible:true}});var M={top:20,right:20,bottom:60,left:60},ve=300,ye=169,Mt=.2,Ae=.1,Vt=20,le=4,je=2,ce=6;var te={solid:"none",dashed:"8 4",dotted:"2 4"};var et="var(--border)",tt=1,ot=.7,ue="var(--border)",de=1,me=6,oe=12,nt=44,X=4,Le=2,Ne=["var(--chart-1, hsl(221 83% 53%))","var(--chart-2, hsl(142 71% 45%))","var(--chart-3, hsl(38 92% 50%))","var(--chart-4, hsl(262 83% 58%))","var(--chart-5, hsl(0 84% 60%))"];function Qe(o,t){let e=Math.floor(Math.log10(o)),a=o/Math.pow(10,e),n;return t?a<1.5?n=1:a<3?n=2:a<7?n=5:n=10:a<=1?n=1:a<=2?n=2:a<=5?n=5:n=10,n*Math.pow(10,e)}function rt(o,t,e=5){if(o===t){if(o===0)return [0,25,50,75,100];o=o*.9,t=t*1.1;}let a=Qe(t-o,false),n=Qe(a/(e-1),true),s=Math.floor(o/n)*n,r=Math.ceil(t/n)*n,i=[];for(let l=s;l<=r;l+=n)i.push(l);return i}function at(o,t,e,a=0){let n=o/t,s=n*(1-Mt),r=Math.max(Vt,s/e*(1-Ae));return {barWidth:r,groupWidth:n,getBarX:(l,p)=>{let h=a+n*l+n/2,u=e*r+(e-1)*(Ae*r);return h-u/2+p*(r+Ae*r)}}}function it(o){let[t,e]=react.useState({width:ve,height:ye,padding:M,plotArea:{x:M.left,y:M.top,width:ve-M.left-M.right,height:ye-M.top-M.bottom}});return react.useEffect(()=>{let a=o.current;if(!a)return;let n=r=>{let i=Math.max(r,ve),l=Math.max(i*.5625,ye),p={x:M.left,y:M.top,width:i-M.left-M.right,height:l-M.top-M.bottom};e({width:i,height:l,padding:M,plotArea:p});},s=new ResizeObserver(r=>{let i=r[0];i&&n(i.contentRect.width);});return n(a.getBoundingClientRect().width),s.observe(a),()=>{s.disconnect();}},[o]),t}function Ce(o){let{rows:t,cols:e,initialRow:a=0,initialCol:n=0,wrap:s=false,onFocusChange:r}=o,[i,l]=react.useState(a),[p,h]=react.useState(n),u=react.useCallback(d=>typeof e=="function"?e(d):e,[e]),b=react.useCallback((d,c)=>{r?.(d,c);},[r]),C=react.useCallback(()=>{h(d=>{let c=u(i)-1,g=d+1;return g>c&&(g=s?0:c),g!==d&&b(i,g),g});},[i,u,s,b]),m=react.useCallback(()=>{h(d=>{let c=d-1,g=u(i)-1;return c<0&&(c=s?g:0),c!==d&&b(i,c),c});},[i,u,s,b]),x=react.useCallback(()=>{l(d=>{let c=d+1;if(c>=t&&(c=s?0:t-1),c!==d){let g=u(c)-1;h(I=>{let y=Math.min(I,g);return b(c,y),y});}return c});},[t,u,s,b]),R=react.useCallback(()=>{l(d=>{let c=d-1;if(c<0&&(c=s?t-1:0),c!==d){let g=u(c)-1;h(I=>{let y=Math.min(I,g);return b(c,y),y});}return c});},[t,u,s,b]),D=react.useCallback(()=>{h(0),b(i,0);},[i,b]),A=react.useCallback(()=>{let d=u(i)-1;h(d),b(i,d);},[i,u,b]),f=react.useCallback((d,c)=>{let g=Math.max(0,Math.min(d,t-1)),I=u(g)-1,y=Math.max(0,Math.min(c,I));l(g),h(y),b(g,y);},[t,u,b]),S=react.useCallback((d,c)=>d===i&&c===p?0:-1,[i,p]);return react.useMemo(()=>({focusedRow:i,focusedCol:p,moveRight:C,moveLeft:m,moveDown:x,moveUp:R,moveToStart:D,moveToEnd:A,setFocus:f,getTabIndex:S}),[i,p,C,m,x,R,D,A,f,S])}var lt=react.createContext(null);function ct(){let o=react.useContext(lt);if(!o)throw new Error("useChartContext must be used within a ChartProvider");return o}function Kt(o){let t=new Set;return o.forEach(e=>{e.data.forEach(a=>{t.add(a.label);});}),Array.from(t)}function $t(o,t,e){let a=Kt(o),{plotArea:n}=t,s=1/0,r=-1/0;o.forEach(m=>{m.data.forEach(x=>{s=Math.min(s,x.value),r=Math.max(r,x.value);});}),(!isFinite(s)||!isFinite(r))&&(s=0,r=100);let i=e?Math.min(0,s):s,l=rt(i,r),p=l[l.length-1]||r,h=l[0]||i,u=m=>{if(a.length<=1)return n.x+n.width/2;let x=n.width/a.length;return n.x+x*m+x/2},b=p-h||1;return {allLabels:a,xScale:u,yScale:m=>{let x=(m-h)/b;return n.y+n.height*(1-x)},yMin:h,yMax:p,yTicks:l}}function ut({children:o,containerRef:t,data:e,type:a,title:n,description:s,xAxisLabel:r,yAxisLabel:i,showTooltip:l,showGrid:p,showLegend:h,announceTrends:u,trendUpFormat:b,trendDownFormat:C,startAtZero:m,reducedMotion:x=true,onPointFocus:R,onPointSelect:D}){let[A,f]=react.useState(null),[S,d]=react.useState(""),[c,g]=react.useState(false),[I,y]=react.useState(null),[_,E]=react.useState(null),[k,V]=react.useState(null),G=it(t),O=react.useMemo(()=>$t(e,G,m),[e,G,m]);react.useMemo(()=>Math.max(...e.map(v=>v.data.length)),[e]);let re=react.useCallback((v,T)=>(T?b:C).replace("{percent}",String(v)),[b,C]),De=(v,T)=>{if(!u||T<=0)return null;let w=e[v];if(!w)return null;let L=w.data[T],N=w.data[T-1];if(!L||!N||N.value===0)return null;let Z=(L.value-N.value)/Math.abs(N.value)*100,J=Z>=0,ie=Math.abs(Math.round(Z));return {text:re(ie,J),direction:J?"up":"down"}},B=Ce({rows:e.length,cols:v=>e[v]?.data.length||0,onFocusChange:(v,T)=>{let w=e[v],L=w?.data[T];if(!w||!L)return;let N=De(v,T);E(N?.text??null),V(N?.direction??null);let Z=`${T+1} of ${w.data.length}`,J=e.length>1?`${w.name}: `:"",ie=z(L.value),Lt=N?`, ${N.text}`:"";d(`${J}${L.label}, ${ie}${Lt}. ${Z}.`),l&&(g(true),y({series:v,point:T})),R?.(L,v,T);}}),ae=react.useMemo(()=>{let v=typeof navigator<"u"?navigator.language:"en-US";return new Intl.NumberFormat(v)},[]),z=react.useCallback(v=>ae.format(v),[ae]),ee=react.useCallback((v,T)=>{if(!u||T<=0)return null;let w=e[v];if(!w)return null;let L=w.data[T],N=w.data[T-1];if(!L||!N||N.value===0)return null;let Z=(L.value-N.value)/Math.abs(N.value)*100,J=Z>=0,ie=Math.abs(Math.round(Z));return {text:re(ie,J),direction:J?"up":"down"}},[e,u,re]),ge=react.useCallback(v=>Ne[v%Ne.length]??"var(--chart-1)",[]),F=react.useCallback((v,T)=>{let w=e[v],L=w?.data[T];if(!w||!L)return null;let N=O.allLabels.indexOf(L.label);return N===-1?null:{x:O.xScale(N),y:O.yScale(L.value)}},[e,O]),U=react.useCallback((v,T)=>{B.setFocus(v,T);},[B]),ze=react.useCallback((v,T)=>{f({series:v,point:T});let w=e[v],L=w?.data[T];w&&L&&D?.(L,v,T);},[e,D]),Xe=react.useCallback(()=>{f(null);},[]),Ye=react.useCallback(v=>{d(v);},[]),qe=react.useCallback((v,T)=>{if(l){g(true),y({series:v,point:T});let w=ee(v,T);E(w?.text??null),V(w?.direction??null);}},[l,ee]),Ze=react.useCallback(()=>{g(false),y(null);},[]),At=react.useMemo(()=>({focusedSeries:B.focusedRow,focusedPoint:B.focusedCol,selectedPoint:A,announcement:S,isTooltipVisible:c,tooltipPoint:I,currentTrend:_,currentTrendDirection:k,dimensions:G,scales:O,data:e,type:a,title:n,description:s,xAxisLabel:r,yAxisLabel:i,showTooltip:l,showGrid:p,showLegend:h,announceTrends:u,trendUpFormat:b,trendDownFormat:C,startAtZero:m,reducedMotion:x,setFocus:U,selectPoint:ze,clearSelection:Xe,announce:Ye,showTooltipAt:qe,hideTooltip:Ze,getTabIndex:B.getTabIndex,formatValue:z,getSeriesColor:ge,getPointCoordinates:F}),[B.focusedRow,B.focusedCol,B.getTabIndex,A,S,c,I,_,k,G,O,e,a,n,s,r,i,l,p,h,u,b,C,m,x,U,ze,Xe,Ye,qe,Ze,z,ge,F]);return jsxRuntime.jsx(lt.Provider,{value:At,children:o})}function _e({yTicks:o,yScale:t,plotX:e,plotWidth:a,visible:n}){return n?jsxRuntime.jsx("g",{className:"chart-grid","aria-hidden":"true","data-testid":"chart-grid",children:o.map(s=>{let r=t(s);return jsxRuntime.jsx("line",{x1:e,y1:r,x2:e+a,y2:r,stroke:et,strokeWidth:tt,strokeOpacity:ot},s)})}):null}_e.displayName="ChartGrid";function Ve({allLabels:o,yTicks:t,xScale:e,yScale:a,plotArea:n,xAxisLabel:s,yAxisLabel:r,formatValue:i}){let{x:l,y:p,width:h,height:u}=n;return jsxRuntime.jsxs("g",{className:"chart-axis","aria-hidden":"true","data-testid":"chart-axis",children:[jsxRuntime.jsx("line",{x1:l,y1:p+u,x2:l+h,y2:p+u,stroke:ue,strokeWidth:de}),jsxRuntime.jsx("line",{x1:l,y1:p,x2:l,y2:p+u,stroke:ue,strokeWidth:de}),o.map((b,C)=>{let m=e(C),x=p+u;return jsxRuntime.jsxs("g",{children:[jsxRuntime.jsx("line",{x1:m,y1:x,x2:m,y2:x+me,stroke:ue,strokeWidth:de}),jsxRuntime.jsx("text",{x:m,y:x+me+oe,textAnchor:"middle",fontSize:oe,fill:"currentColor",className:"text-[var(--content-foreground)]",children:b})]},b)}),t.map(b=>{let C=l,m=a(b);return jsxRuntime.jsxs("g",{children:[jsxRuntime.jsx("line",{x1:C-me,y1:m,x2:C,y2:m,stroke:ue,strokeWidth:de}),jsxRuntime.jsx("text",{x:C-me-4,y:m,textAnchor:"end",dominantBaseline:"middle",fontSize:oe,fill:"currentColor",className:"text-[var(--content-foreground)]",children:i(b)})]},b)}),jsxRuntime.jsx("text",{x:l+h/2,y:p+u+45,textAnchor:"middle",fontSize:oe,fill:"currentColor",className:"text-[var(--content-foreground)] font-medium",children:s}),jsxRuntime.jsx("text",{x:15,y:p+u/2,textAnchor:"middle",fontSize:oe,fill:"currentColor",className:"text-[var(--content-foreground)] font-medium",transform:`rotate(-90, 15, ${p+u/2})`,children:r})]})}Ve.displayName="ChartAxis";function be({type:o,x:t,y:e,width:a=ce*2,height:n=ce*2,color:s,seriesIndex:r,pointIndex:i,tabIndex:l,label:p,isSelected:h=false,onFocus:u,onBlur:b,onHover:C,onHoverEnd:m,onKeyDown:x}){let R=react.useRef(null),{isFocusVisible:D,focusProps:A}=reactAria.useFocusRing();react.useEffect(()=>{l===0&&R.current&&document.activeElement?.closest('[role="graphics-document"]')!==null&&R.current.focus();},[l]);let f=Math.max(nt,a,n),S=o==="bar"?t-(f-a)/2:t-f/2,d=o==="bar"?e-(f-n)/2:e-f/2,c=E=>{A.onFocus?.(E),u?.(r,i);},g=E=>{A.onBlur?.(E),b?.();},I=E=>{C?.(r,i);},y=E=>{m?.();};return jsxRuntime.jsxs("g",{ref:R,role:"listitem",tabIndex:l,"aria-label":p,className:Ee({type:o,state:h?"selected":D?"focused":"default"}),onFocus:c,onBlur:g,onMouseEnter:I,onMouseLeave:y,onKeyDown:x,"data-testid":`chart-point-${r}-${i}`,children:[jsxRuntime.jsx("rect",{x:S,y:d,width:f,height:f,fill:"transparent",className:"cursor-pointer"}),o==="bar"?jsxRuntime.jsx("rect",{x:t,y:e,width:a,height:n,rx:le,ry:le,fill:s,className:"chart-bar"}):jsxRuntime.jsx("circle",{cx:t,cy:e,r:ce,fill:s,className:"chart-point"}),D&&(o==="bar"?jsxRuntime.jsx("rect",{x:t-X,y:e-X,width:a+X*2,height:n+X*2,rx:le+X,ry:le+X,fill:"none",stroke:"var(--ring)",strokeWidth:Le,className:"chart-focus-ring"}):jsxRuntime.jsx("circle",{cx:t,cy:e,r:ce+X,fill:"none",stroke:"var(--ring)",strokeWidth:Le,className:"chart-focus-ring"}))]})}be.displayName="ChartDataPoint";function ke({series:o,seriesIndex:t,totalSeries:e,dimensions:a,scales:n,color:s,getTabIndex:r,selectedPoint:i,onPointFocus:l,onPointBlur:p,onPointHover:h,onPointHoverEnd:u,onKeyDown:b,formatValue:C}){let{plotArea:m}=a,{allLabels:x,xScale:R,yScale:D,yMin:A}=n,{barWidth:f,getBarX:S}=at(m.width,x.length,e,m.x),d=D(Math.max(0,A));return jsxRuntime.jsx("g",{className:"chart-bar-series",role:"list","aria-label":`${o.name} series`,"data-testid":`chart-series-${t}`,children:o.data.map((c,g)=>{let I=x.indexOf(c.label);if(I===-1)return null;let y=S(I,t),_=D(c.value),E=Math.abs(d-_),k=c.value>=0?_:d,V=i?.series===t&&i?.point===g,G=`${o.name}: ${c.label}, ${C(c.value)}`;return jsxRuntime.jsx(be,{type:"bar",x:y,y:k,width:f,height:E,color:s,seriesIndex:t,pointIndex:g,tabIndex:r(t,g),label:G,isSelected:V,onFocus:l,onBlur:p,onHover:h,onHoverEnd:u,onKeyDown:b},`${c.label}-${g}`)})})}ke.displayName="ChartBarSeries";function Oe({series:o,seriesIndex:t,scales:e,color:a,pattern:n,getTabIndex:s,selectedPoint:r,onPointFocus:i,onPointBlur:l,onPointHover:p,onPointHoverEnd:h,onKeyDown:u,formatValue:b}){let{allLabels:C,xScale:m,yScale:x}=e,R=react.useMemo(()=>o.data.map((f,S)=>{let d=C.indexOf(f.label);return d===-1?null:{x:m(d),y:x(f.value),point:f,pointIndex:S,labelIndex:d}}).filter(f=>f!==null).sort((f,S)=>f.labelIndex-S.labelIndex),[o.data,C,m,x]),D=react.useMemo(()=>R.length===0?"":R.map((f,S)=>`${S===0?"M":"L"} ${f.x} ${f.y}`).join(" "),[R]),A=te[n]==="none"?void 0:te[n];return jsxRuntime.jsxs("g",{className:"chart-line-series",role:"list","aria-label":`${o.name} series`,"data-testid":`chart-series-${t}`,children:[jsxRuntime.jsx("path",{d:D,fill:"none",stroke:a,strokeWidth:je,strokeDasharray:A,strokeLinecap:"round",strokeLinejoin:"round",className:"chart-line","aria-hidden":"true"}),R.map(({x:f,y:S,point:d,pointIndex:c})=>{let g=r?.series===t&&r?.point===c,I=`${o.name}: ${d.label}, ${b(d.value)}`;return jsxRuntime.jsx(be,{type:"line",x:f,y:S,color:a,seriesIndex:t,pointIndex:c,tabIndex:s(t,c),label:I,isSelected:g,onFocus:i,onBlur:l,onHover:p,onHoverEnd:h,onKeyDown:u},`${d.label}-${c}`)})]})}Oe.displayName="ChartLineSeries";function Fe({type:o,data:t,dimensions:e,scales:a,titleId:n,descId:s,showGrid:r,xAxisLabel:i,yAxisLabel:l,getSeriesColor:p,getTabIndex:h,selectedPoint:u,formatValue:b,onKeyDown:C,onPointFocus:m,onPointBlur:x,onPointHover:R,onPointHoverEnd:D}){let{width:A,height:f,plotArea:S}=e,{allLabels:d,xScale:c,yScale:g,yTicks:I}=a;return jsxRuntime.jsxs("svg",{role:"graphics-document","aria-roledescription":`${o} chart`,"aria-labelledby":n,"aria-describedby":s,viewBox:`0 0 ${A} ${f}`,className:"h-full w-full",style:{maxHeight:f},"data-testid":"chart-svg",children:[jsxRuntime.jsx(_e,{yTicks:I,yScale:g,plotX:S.x,plotWidth:S.width,visible:r}),jsxRuntime.jsx(Ve,{allLabels:d,yTicks:I,xScale:c,yScale:g,plotArea:S,xAxisLabel:i,yAxisLabel:l,formatValue:b}),t.map((y,_)=>{let E=y.color||p(_),k=y.pattern||"solid";return o==="bar"?jsxRuntime.jsx(ke,{series:y,seriesIndex:_,totalSeries:t.length,dimensions:e,scales:a,color:E,getTabIndex:h,selectedPoint:u,onPointFocus:m,onPointBlur:x,onPointHover:R,onPointHoverEnd:D,onKeyDown:C,formatValue:b},y.name):jsxRuntime.jsx(Oe,{series:y,seriesIndex:_,scales:a,color:E,pattern:k,getTabIndex:h,selectedPoint:u,onPointFocus:m,onPointBlur:x,onPointHover:R,onPointHoverEnd:D,onKeyDown:C,formatValue:b},y.name)})]})}Fe.displayName="ChartSVG";var Ge=classVarianceAuthority.cva(["z-50 overflow-hidden rounded-md px-3 py-1.5 text-sm shadow-md","bg-[var(--content-foreground)] text-[var(--content-background)]","animate-in fade-in-0","data-[exiting]:animate-out data-[exiting]:fade-out-0"],{variants:{side:{top:["motion-safe:slide-in-from-bottom-2","data-[exiting]:motion-safe:slide-out-to-bottom-2"],bottom:["motion-safe:slide-in-from-top-2","data-[exiting]:motion-safe:slide-out-to-top-2"],left:["motion-safe:slide-in-from-right-2","data-[exiting]:motion-safe:slide-out-to-right-2"],right:["motion-safe:slide-in-from-left-2","data-[exiting]:motion-safe:slide-out-to-left-2"]}},defaultVariants:{side:"top"}});classVarianceAuthority.cva("fill-[var(--content-foreground)]",{variants:{side:{top:"rotate-180",bottom:"rotate-0",left:"rotate-90",right:"-rotate-90"}},defaultVariants:{side:"top"}});var vt=8;function Ke({visible:o,x:t,y:e,seriesName:a,label:n,value:s,chartWidth:r,chartHeight:i,trend:l,trendDirection:p}){let h=react.useRef(null),[u,b]=react.useState({left:0,top:0}),[C,m]=react.useState("top");return react.useEffect(()=>{if(!o||!h.current)return;let R=h.current.getBoundingClientRect(),D=R.width||120,A=R.height||50,f=t-D/2,S=e-A-vt,d="top";S<0&&(S=e+vt+16,d="bottom"),f<0&&(f=0),f+D>r&&(f=r-D),b({left:f,top:S}),m(d);},[o,t,e,r]),o?jsxRuntime.jsxs("div",{ref:h,"aria-hidden":"true",className:Ge({side:C}),style:{position:"absolute",left:u.left,top:u.top,pointerEvents:"none"},"data-testid":"chart-tooltip",children:[jsxRuntime.jsx("div",{className:"font-medium",children:a}),jsxRuntime.jsxs("div",{className:"opacity-80",children:[n,": ",jsxRuntime.jsx("span",{className:"font-semibold",children:s})]}),l&&jsxRuntime.jsxs("div",{className:"text-xs opacity-70 mt-1 flex items-center gap-1",children:[p==="up"?jsxRuntime.jsx("svg",{"aria-hidden":"true",className:"h-3 w-3 text-green-500",viewBox:"0 0 20 20",fill:"currentColor",children:jsxRuntime.jsx("path",{fillRule:"evenodd",d:"M10 17a.75.75 0 01-.75-.75V5.612L5.29 9.77a.75.75 0 01-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0110 17z",clipRule:"evenodd"})}):p==="down"?jsxRuntime.jsx("svg",{"aria-hidden":"true",className:"h-3 w-3 text-red-500",viewBox:"0 0 20 20",fill:"currentColor",children:jsxRuntime.jsx("path",{fillRule:"evenodd",d:"M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z",clipRule:"evenodd"})}):null,jsxRuntime.jsx("span",{children:l})]})]}):null}Ke.displayName="ChartTooltip";function $e({data:o,visible:t,type:e,getSeriesColor:a}){return t?jsxRuntime.jsx("div",{className:Je({visible:t}),role:"list","aria-label":"Chart legend","data-testid":"chart-legend",children:o.map((n,s)=>{let r=n.color||a(s),i=n.pattern||"solid";return jsxRuntime.jsxs("div",{role:"listitem",className:"flex items-center gap-2",children:[e==="bar"?jsxRuntime.jsx("div",{className:"h-3 w-3 rounded-sm",style:{backgroundColor:r},"data-testid":`legend-color-${s}`,"aria-hidden":"true"}):jsxRuntime.jsx("svg",{width:"24",height:"12","aria-hidden":"true","data-testid":`legend-color-${s}`,children:jsxRuntime.jsx("line",{x1:"0",y1:"6",x2:"24",y2:"6",stroke:r,strokeWidth:"2",strokeDasharray:te[i]==="none"?void 0:te[i]})}),jsxRuntime.jsx("span",{className:"text-sm text-[var(--content-foreground)]",children:n.name})]},n.name)})}):null}$e.displayName="ChartLegend";function We({announcement:o}){return jsxRuntime.jsx(reactAria.VisuallyHidden,{children:jsxRuntime.jsx("div",{role:"status","aria-live":"polite","aria-atomic":"true","data-testid":"chart-announcer",children:o})})}We.displayName="ChartAnnouncer";function Ue({data:o,title:t,xAxisLabel:e,yAxisLabel:a,formatValue:n,allLabels:s}){let r=o.length===1;return jsxRuntime.jsx(reactAria.VisuallyHidden,{children:jsxRuntime.jsxs("table",{"data-testid":"chart-data-table",children:[jsxRuntime.jsxs("caption",{children:[t," data table"]}),jsxRuntime.jsx("thead",{children:jsxRuntime.jsxs("tr",{children:[jsxRuntime.jsx("th",{scope:"col",children:e}),r?jsxRuntime.jsx("th",{scope:"col",children:a}):o.map(i=>jsxRuntime.jsxs("th",{scope:"col",children:[i.name," (",a,")"]},i.name))]})}),jsxRuntime.jsx("tbody",{children:s.map(i=>jsxRuntime.jsxs("tr",{children:[jsxRuntime.jsx("th",{scope:"row",children:i}),o.map(l=>{let p=l.data.find(h=>h.label===i);return jsxRuntime.jsx("td",{children:p?n(p.value):"\u2014"},l.name)})]},i))})]})})}Ue.displayName="ChartDataTable";function Ct(o){let{roving:t,onSelect:e,onClear:a,disabled:n=false}=o;return {onKeyDown:react.useCallback(r=>{if(!n)switch(r.key){case "ArrowRight":r.preventDefault(),t.moveRight();break;case "ArrowLeft":r.preventDefault(),t.moveLeft();break;case "ArrowDown":r.preventDefault(),t.moveDown();break;case "ArrowUp":r.preventDefault(),t.moveUp();break;case "Home":r.preventDefault(),t.moveToStart();break;case "End":r.preventDefault(),t.moveToEnd();break;case "Enter":case " ":r.preventDefault(),e?.();break;case "Escape":r.preventDefault(),a?.();break;}},[t,e,a,n])}}function go({chartId:o}){let t=ct(),{data:e,type:a,title:n,xAxisLabel:s,yAxisLabel:r,showTooltip:i,showGrid:l,showLegend:p,dimensions:h,scales:u,announcement:b,isTooltipVisible:C,tooltipPoint:m,currentTrend:x,currentTrendDirection:R,selectedPoint:D,getTabIndex:A,formatValue:f,getSeriesColor:S,getPointCoordinates:d,setFocus:c,selectPoint:g,clearSelection:I,showTooltipAt:y,hideTooltip:_}=t,E=`${o}-title`,k=`${o}-desc`,V=Ce({rows:e.length,cols:F=>e[F]?.data.length||0,onFocusChange:(F,U)=>{c(F,U);}}),{onKeyDown:G}=Ct({roving:V,onSelect:()=>{g(V.focusedRow,V.focusedCol);},onClear:()=>{I();}}),O=react.useCallback((F,U)=>{V.setFocus(F,U),y(F,U);},[V,y]),re=react.useCallback(()=>{},[]),De=react.useCallback((F,U)=>{y(F,U);},[y]),B=react.useCallback(()=>{_();},[_]),ae=react.useMemo(()=>m?d(m.series,m.point)||{x:0,y:0}:{x:0,y:0},[m,d]),z=react.useMemo(()=>m?e[m.series]:null,[m,e]),ee=react.useMemo(()=>m?z?.data[m.point]:null,[m,z]),ge=react.useMemo(()=>p??e.length>1,[p,e.length]);return jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(We,{announcement:b}),jsxRuntime.jsxs("div",{className:"relative",children:[jsxRuntime.jsx("div",{className:"relative z-0",children:jsxRuntime.jsx(Fe,{type:a,data:e,dimensions:h,scales:u,titleId:E,descId:k,showGrid:l,xAxisLabel:s,yAxisLabel:r,getSeriesColor:S,getTabIndex:A,selectedPoint:D,formatValue:f,onKeyDown:G,onPointFocus:O,onPointBlur:re,onPointHover:De,onPointHoverEnd:B})}),i&&ee&&z&&jsxRuntime.jsx(Ke,{visible:C,x:ae.x,y:ae.y,seriesName:z.name,label:ee.label,value:f(ee.value),chartWidth:h.width,chartHeight:h.height,trend:x,trendDirection:R})]}),jsxRuntime.jsx($e,{data:e,visible:ge,type:a,getSeriesColor:S}),jsxRuntime.jsx(Ue,{data:e,title:n,xAxisLabel:s,yAxisLabel:r,formatValue:f,allLabels:u.allLabels})]})}var St=react.forwardRef(({data:o,type:t,title:e,description:a,xAxisLabel:n,yAxisLabel:s,variant:r="default",className:i,showTooltip:l=true,showGrid:p=true,showLegend:h,announceTrends:u=false,trendUpFormat:b="up {percent}% from previous",trendDownFormat:C="down {percent}% from previous",startAtZero:m=true,reducedMotion:x,onPointFocus:R,onPointSelect:D,id:A,"aria-label":f,"aria-labelledby":S,"aria-describedby":d,"data-testid":c,...g},I)=>{let y=react.useRef(null),_=react.useId(),E=A||`chart-${_}`,k=`${E}-title`,V=`${E}-desc`,G=h??o.length>1;return jsxRuntime.jsxs("figure",{ref:O=>{typeof I=="function"?I(O):I&&(I.current=O),y.current=O;},id:E,role:"figure","aria-label":f,"aria-labelledby":S||k,"aria-describedby":d||V,"data-testid":c,className:se(we({variant:r}),"p-4",i),...g,children:[jsxRuntime.jsxs("figcaption",{className:"sr-only",children:[jsxRuntime.jsx("span",{id:k,children:e}),jsxRuntime.jsx("span",{id:V,children:a})]}),jsxRuntime.jsx(ut,{containerRef:y,data:o,type:t,title:e,description:a,xAxisLabel:n,yAxisLabel:s,showTooltip:l,showGrid:p,showLegend:G,announceTrends:u,trendUpFormat:b,trendDownFormat:C,startAtZero:m,reducedMotion:x,onPointFocus:R,onPointSelect:D,children:jsxRuntime.jsx(go,{chartId:E})})]})});St.displayName="Chart";var Tt=react.memo(St);Tt.displayName="Chart";var Pt=zod.z.object({className:zod.z.string().optional(),children:zod.z.any().optional(),id:zod.z.string().optional(),"aria-label":zod.z.string().optional(),"aria-labelledby":zod.z.string().optional(),"aria-describedby":zod.z.string().optional(),"aria-live":zod.z.enum(["off","polite","assertive"]).optional(),"aria-hidden":zod.z.boolean().optional(),"data-testid":zod.z.string().optional()});var It=zod.z.object({label:zod.z.string().min(1,"Label is required"),value:zod.z.number(),description:zod.z.string().optional()}),Rt=zod.z.enum(["solid","dashed","dotted"]),Dt=zod.z.object({name:zod.z.string().min(1,"Series name is required"),color:zod.z.string().optional(),pattern:Rt.optional().default("solid"),data:zod.z.array(It).min(1,"At least one data point required")}),wt=zod.z.enum(["bar","line"]),Et=zod.z.enum(["default","muted"]),vo=Pt.extend({data:zod.z.array(Dt).min(1,"At least one data series required").max(5,"Maximum 5 series allowed"),type:wt,title:zod.z.string().min(1,"Title is required"),description:zod.z.string().min(1,"Description is required"),xAxisLabel:zod.z.string().min(1,"X-axis label is required"),yAxisLabel:zod.z.string().min(1,"Y-axis label is required"),variant:Et.optional().default("default"),showTooltip:zod.z.boolean().optional().default(true),showGrid:zod.z.boolean().optional().default(true),showLegend:zod.z.boolean().optional(),announceTrends:zod.z.boolean().optional().default(false),trendUpFormat:zod.z.string().optional().default("up {percent}% from previous"),trendDownFormat:zod.z.string().optional().default("down {percent}% from previous"),startAtZero:zod.z.boolean().optional().default(true),reducedMotion:zod.z.boolean().optional(),onPointFocus:zod.z.unknown().optional(),onPointSelect:zod.z.unknown().optional()});exports.Chart=Tt;exports.ChartPropsSchema=vo;exports.ChartTypeSchema=wt;exports.ChartVariantSchema=Et;exports.DataPointSchema=It;exports.DataSeriesSchema=Dt;exports.LinePatternSchema=Rt;exports.chartVariants=we;exports.dataPointVariants=Ee;//# sourceMappingURL=index.js.map
1844
3
  //# sourceMappingURL=index.js.map