@glyphui/ui 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (563) hide show
  1. package/README.md +68 -12
  2. package/dist/chart.d.ts +14 -202
  3. package/dist/chart.js +14 -456
  4. package/dist/components/accordion/accordion.d.ts +15 -0
  5. package/dist/components/accordion/accordion.js +69 -0
  6. package/dist/components/accordion/accordion.styles.d.ts +65 -0
  7. package/dist/components/accordion/accordion.styles.js +30 -0
  8. package/dist/components/accordion/index.d.ts +3 -0
  9. package/dist/components/alert/alert-icons.js +62 -0
  10. package/dist/components/alert/alert.d.ts +19 -0
  11. package/dist/components/alert/alert.js +64 -0
  12. package/dist/components/alert/alert.styles.d.ts +107 -0
  13. package/dist/components/alert/alert.styles.js +86 -0
  14. package/dist/components/alert/index.d.ts +3 -0
  15. package/dist/components/alert-dialog/alert-dialog.d.ts +29 -0
  16. package/dist/components/alert-dialog/alert-dialog.js +89 -0
  17. package/dist/components/alert-dialog/alert-dialog.styles.d.ts +63 -0
  18. package/dist/components/alert-dialog/alert-dialog.styles.js +19 -0
  19. package/dist/components/alert-dialog/confirm-dialog.js +22 -0
  20. package/dist/components/alert-dialog/confirm.d.ts +30 -0
  21. package/dist/components/alert-dialog/confirm.js +62 -0
  22. package/dist/components/alert-dialog/index.d.ts +4 -0
  23. package/dist/components/aspect-ratio/aspect-ratio.d.ts +55 -0
  24. package/dist/components/aspect-ratio/aspect-ratio.js +28 -0
  25. package/dist/components/aspect-ratio/index.d.ts +2 -0
  26. package/dist/components/avatar/avatar.d.ts +13 -0
  27. package/dist/components/avatar/avatar.js +50 -0
  28. package/dist/components/avatar/avatar.styles.d.ts +77 -0
  29. package/dist/components/avatar/avatar.styles.js +32 -0
  30. package/dist/components/avatar/index.d.ts +3 -0
  31. package/dist/components/badge/badge.d.ts +10 -0
  32. package/dist/components/badge/badge.js +28 -0
  33. package/dist/components/badge/badge.styles.d.ts +62 -0
  34. package/dist/components/badge/badge.styles.js +72 -0
  35. package/dist/components/badge/index.d.ts +3 -0
  36. package/dist/components/banner/banner.d.ts +17 -0
  37. package/dist/components/banner/banner.js +71 -0
  38. package/dist/components/banner/banner.styles.d.ts +98 -0
  39. package/dist/components/banner/banner.styles.js +27 -0
  40. package/dist/components/banner/index.d.ts +3 -0
  41. package/dist/components/blockquote/blockquote.d.ts +72 -0
  42. package/dist/components/blockquote/blockquote.js +47 -0
  43. package/dist/components/blockquote/index.d.ts +2 -0
  44. package/dist/components/breadcrumb/breadcrumb.d.ts +28 -0
  45. package/dist/components/breadcrumb/breadcrumb.js +85 -0
  46. package/dist/components/breadcrumb/breadcrumb.styles.d.ts +63 -0
  47. package/dist/components/breadcrumb/breadcrumb.styles.js +18 -0
  48. package/dist/components/breadcrumb/index.d.ts +3 -0
  49. package/dist/components/button/button.d.ts +20 -0
  50. package/dist/components/button/button.js +52 -0
  51. package/dist/components/button/button.styles.d.ts +146 -0
  52. package/dist/components/button/button.styles.js +64 -0
  53. package/dist/components/button/index.d.ts +3 -0
  54. package/dist/components/calendar/calendar.d.ts +9 -0
  55. package/dist/components/calendar/calendar.js +75 -0
  56. package/dist/components/calendar/calendar.styles.d.ts +171 -0
  57. package/dist/components/calendar/calendar.styles.js +42 -0
  58. package/dist/components/calendar/index.d.ts +4 -0
  59. package/dist/components/card/card.d.ts +17 -0
  60. package/dist/components/card/card.js +79 -0
  61. package/dist/components/card/card.styles.d.ts +113 -0
  62. package/dist/components/card/card.styles.js +38 -0
  63. package/dist/components/card/index.d.ts +3 -0
  64. package/dist/components/carousel/carousel.d.ts +37 -0
  65. package/dist/components/carousel/carousel.js +190 -0
  66. package/dist/components/carousel/carousel.styles.d.ts +74 -0
  67. package/dist/components/carousel/carousel.styles.js +35 -0
  68. package/dist/components/carousel/index.d.ts +3 -0
  69. package/dist/components/checkbox/checkbox.d.ts +37 -0
  70. package/dist/components/checkbox/checkbox.js +44 -0
  71. package/dist/components/checkbox/index.d.ts +2 -0
  72. package/dist/components/code/code.d.ts +18 -0
  73. package/dist/components/code/code.js +75 -0
  74. package/dist/components/code/code.styles.d.ts +76 -0
  75. package/dist/components/code/code.styles.js +18 -0
  76. package/dist/components/code/index.d.ts +3 -0
  77. package/dist/components/collapsible/collapsible.d.ts +56 -0
  78. package/dist/components/collapsible/collapsible.js +43 -0
  79. package/dist/components/collapsible/index.d.ts +2 -0
  80. package/dist/components/combobox/combobox.d.ts +50 -0
  81. package/dist/components/combobox/combobox.js +141 -0
  82. package/dist/components/combobox/combobox.styles.d.ts +110 -0
  83. package/dist/components/combobox/combobox.styles.js +42 -0
  84. package/dist/components/combobox/index.d.ts +3 -0
  85. package/dist/components/command-menu/command-menu.d.ts +40 -0
  86. package/dist/components/command-menu/command-menu.js +148 -0
  87. package/dist/components/command-menu/command-menu.styles.d.ts +105 -0
  88. package/dist/components/command-menu/command-menu.styles.js +30 -0
  89. package/dist/components/command-menu/index.d.ts +3 -0
  90. package/dist/components/container/container.d.ts +34 -0
  91. package/dist/components/container/container.js +30 -0
  92. package/dist/components/container/index.d.ts +2 -0
  93. package/dist/components/context-menu/context-menu.d.ts +34 -0
  94. package/dist/components/context-menu/context-menu.js +157 -0
  95. package/dist/components/context-menu/context-menu.styles.d.ts +135 -0
  96. package/dist/components/context-menu/context-menu.styles.js +14 -0
  97. package/dist/components/context-menu/index.d.ts +3 -0
  98. package/dist/components/copy-button/copy-button.d.ts +61 -0
  99. package/dist/components/copy-button/copy-button.js +79 -0
  100. package/dist/components/copy-button/index.d.ts +2 -0
  101. package/dist/components/date-picker/date-picker.d.ts +51 -0
  102. package/dist/components/date-picker/date-picker.js +235 -0
  103. package/dist/components/date-picker/date-picker.styles.d.ts +77 -0
  104. package/dist/components/date-picker/date-picker.styles.js +31 -0
  105. package/dist/components/date-picker/index.d.ts +3 -0
  106. package/dist/components/description-list/description-list.d.ts +15 -0
  107. package/dist/components/description-list/description-list.js +51 -0
  108. package/dist/components/description-list/description-list.styles.d.ts +74 -0
  109. package/dist/components/description-list/description-list.styles.js +35 -0
  110. package/dist/components/description-list/index.d.ts +3 -0
  111. package/dist/components/dialog/close-icon.js +22 -0
  112. package/dist/components/dialog/dialog.d.ts +21 -0
  113. package/dist/components/dialog/dialog.js +79 -0
  114. package/dist/components/dialog/dialog.styles.d.ts +83 -0
  115. package/dist/components/dialog/dialog.styles.js +34 -0
  116. package/dist/components/dialog/index.d.ts +3 -0
  117. package/dist/components/dropdown-menu/dropdown-menu.d.ts +34 -0
  118. package/dist/components/dropdown-menu/dropdown-menu.js +160 -0
  119. package/dist/components/dropdown-menu/dropdown-menu.styles.d.ts +83 -0
  120. package/dist/components/dropdown-menu/dropdown-menu.styles.js +38 -0
  121. package/dist/components/dropdown-menu/index.d.ts +3 -0
  122. package/dist/components/eyebrow/eyebrow.d.ts +9 -0
  123. package/dist/components/eyebrow/eyebrow.js +20 -0
  124. package/dist/components/eyebrow/eyebrow.styles.d.ts +38 -0
  125. package/dist/components/eyebrow/eyebrow.styles.js +13 -0
  126. package/dist/components/eyebrow/index.d.ts +3 -0
  127. package/dist/components/field/field-context.d.ts +25 -0
  128. package/dist/components/field/field-context.js +27 -0
  129. package/dist/components/field/field.d.ts +28 -0
  130. package/dist/{field-CWZ_FdkD.js → components/field/field.js} +20 -34
  131. package/dist/components/field/field.styles.d.ts +53 -0
  132. package/dist/components/field/field.styles.js +22 -0
  133. package/dist/components/field/index.d.ts +4 -0
  134. package/dist/components/file-upload/file-upload.d.ts +52 -0
  135. package/dist/components/file-upload/file-upload.js +266 -0
  136. package/dist/components/file-upload/file-upload.styles.d.ts +123 -0
  137. package/dist/components/file-upload/file-upload.styles.js +32 -0
  138. package/dist/components/file-upload/index.d.ts +3 -0
  139. package/dist/components/grid/grid.d.ts +168 -0
  140. package/dist/components/grid/grid.js +126 -0
  141. package/dist/components/grid/index.d.ts +2 -0
  142. package/dist/components/heading/heading.d.ts +12 -0
  143. package/dist/components/heading/heading.js +28 -0
  144. package/dist/components/heading/heading.styles.d.ts +65 -0
  145. package/dist/components/heading/heading.styles.js +15 -0
  146. package/dist/components/heading/index.d.ts +3 -0
  147. package/dist/components/hover-card/hover-card.d.ts +43 -0
  148. package/dist/components/hover-card/hover-card.js +27 -0
  149. package/dist/components/hover-card/index.d.ts +2 -0
  150. package/dist/components/input/index.d.ts +3 -0
  151. package/dist/components/input/input.d.ts +13 -0
  152. package/dist/components/input/input.js +33 -0
  153. package/dist/components/input/input.styles.d.ts +55 -0
  154. package/dist/components/input/input.styles.js +33 -0
  155. package/dist/components/input-otp/index.d.ts +4 -0
  156. package/dist/components/input-otp/index.js +5 -0
  157. package/dist/components/input-otp/input-otp.d.ts +23 -0
  158. package/dist/components/input-otp/input-otp.js +86 -0
  159. package/dist/components/input-otp/input-otp.styles.d.ts +71 -0
  160. package/dist/components/input-otp/input-otp.styles.js +30 -0
  161. package/dist/components/kbd/index.d.ts +2 -0
  162. package/dist/components/kbd/kbd.d.ts +30 -0
  163. package/dist/components/kbd/kbd.js +17 -0
  164. package/dist/components/label/index.d.ts +2 -0
  165. package/dist/components/label/label.d.ts +31 -0
  166. package/dist/components/label/label.js +17 -0
  167. package/dist/components/link/index.d.ts +3 -0
  168. package/dist/components/link/link.d.ts +14 -0
  169. package/dist/components/link/link.js +57 -0
  170. package/dist/components/link/link.styles.d.ts +89 -0
  171. package/dist/components/link/link.styles.js +33 -0
  172. package/dist/components/modal/index.d.ts +3 -0
  173. package/dist/components/modal/modal.d.ts +33 -0
  174. package/dist/components/modal/modal.js +56 -0
  175. package/dist/components/modal/modal.styles.d.ts +61 -0
  176. package/dist/components/modal/modal.styles.js +14 -0
  177. package/dist/components/navigation-menu/index.d.ts +3 -0
  178. package/dist/components/navigation-menu/navigation-menu.d.ts +37 -0
  179. package/dist/components/navigation-menu/navigation-menu.js +138 -0
  180. package/dist/components/navigation-menu/navigation-menu.styles.d.ts +111 -0
  181. package/dist/components/navigation-menu/navigation-menu.styles.js +39 -0
  182. package/dist/components/number-input/index.d.ts +3 -0
  183. package/dist/components/number-input/number-input.d.ts +30 -0
  184. package/dist/components/number-input/number-input.js +151 -0
  185. package/dist/components/number-input/number-input.styles.d.ts +68 -0
  186. package/dist/components/number-input/number-input.styles.js +35 -0
  187. package/dist/components/pagination/index.d.ts +4 -0
  188. package/dist/components/pagination/pagination-range.d.ts +11 -0
  189. package/dist/components/pagination/pagination-range.js +42 -0
  190. package/dist/components/pagination/pagination.d.ts +18 -0
  191. package/dist/components/pagination/pagination.js +94 -0
  192. package/dist/components/pagination/pagination.styles.d.ts +65 -0
  193. package/dist/components/pagination/pagination.styles.js +35 -0
  194. package/dist/components/password-input/index.d.ts +3 -0
  195. package/dist/components/password-input/password-input.d.ts +20 -0
  196. package/dist/components/password-input/password-input.js +106 -0
  197. package/dist/components/password-input/password-input.styles.d.ts +66 -0
  198. package/dist/components/password-input/password-input.styles.js +21 -0
  199. package/dist/components/popover/index.d.ts +2 -0
  200. package/dist/components/popover/popover.d.ts +52 -0
  201. package/dist/components/popover/popover.js +35 -0
  202. package/dist/components/progress/index.d.ts +3 -0
  203. package/dist/components/progress/progress.d.ts +29 -0
  204. package/dist/components/progress/progress.js +108 -0
  205. package/dist/components/progress/progress.styles.d.ts +236 -0
  206. package/dist/components/progress/progress.styles.js +69 -0
  207. package/dist/components/prose/index.d.ts +2 -0
  208. package/dist/components/prose/prose.d.ts +90 -0
  209. package/dist/components/prose/prose.js +67 -0
  210. package/dist/components/radio-group/index.d.ts +3 -0
  211. package/dist/components/radio-group/radio-group.d.ts +26 -0
  212. package/dist/components/radio-group/radio-group.js +102 -0
  213. package/dist/components/radio-group/radio-group.styles.d.ts +80 -0
  214. package/dist/components/radio-group/radio-group.styles.js +56 -0
  215. package/dist/components/resizable/index.d.ts +3 -0
  216. package/dist/components/resizable/resizable.d.ts +17 -0
  217. package/dist/components/resizable/resizable.js +81 -0
  218. package/dist/components/resizable/resizable.styles.d.ts +53 -0
  219. package/dist/components/resizable/resizable.styles.js +19 -0
  220. package/dist/components/scroll-area/index.d.ts +2 -0
  221. package/dist/components/scroll-area/scroll-area.d.ts +70 -0
  222. package/dist/components/scroll-area/scroll-area.js +70 -0
  223. package/dist/components/section/index.d.ts +2 -0
  224. package/dist/components/section/section.d.ts +52 -0
  225. package/dist/components/section/section.js +41 -0
  226. package/dist/components/segmented-control/index.d.ts +2 -0
  227. package/dist/components/segmented-control/segmented-control.d.ts +92 -0
  228. package/dist/components/segmented-control/segmented-control.js +86 -0
  229. package/dist/components/select/index.d.ts +2 -0
  230. package/dist/components/select/select.d.ts +85 -0
  231. package/dist/components/select/select.js +81 -0
  232. package/dist/components/separator/index.d.ts +2 -0
  233. package/dist/components/separator/separator.d.ts +40 -0
  234. package/dist/components/separator/separator.js +27 -0
  235. package/dist/components/sheet/index.d.ts +3 -0
  236. package/dist/components/sheet/sheet.d.ts +20 -0
  237. package/dist/components/sheet/sheet.js +83 -0
  238. package/dist/components/sheet/sheet.styles.d.ts +101 -0
  239. package/dist/components/sheet/sheet.styles.js +62 -0
  240. package/dist/components/skeleton/index.d.ts +2 -0
  241. package/dist/components/skeleton/skeleton.d.ts +30 -0
  242. package/dist/components/skeleton/skeleton.js +18 -0
  243. package/dist/components/slider/index.d.ts +3 -0
  244. package/dist/components/slider/slider.d.ts +38 -0
  245. package/dist/components/slider/slider.js +68 -0
  246. package/dist/components/slider/slider.styles.d.ts +56 -0
  247. package/dist/components/slider/slider.styles.js +23 -0
  248. package/dist/components/spinner/index.d.ts +3 -0
  249. package/dist/components/spinner/spinner.d.ts +9 -0
  250. package/dist/components/spinner/spinner.js +36 -0
  251. package/dist/components/spinner/spinner.styles.d.ts +53 -0
  252. package/dist/components/spinner/spinner.styles.js +18 -0
  253. package/dist/components/stack/index.d.ts +2 -0
  254. package/dist/components/stack/stack.d.ts +109 -0
  255. package/dist/components/stack/stack.js +62 -0
  256. package/dist/components/stat-card/index.d.ts +3 -0
  257. package/dist/components/stat-card/stat-card.d.ts +22 -0
  258. package/dist/components/stat-card/stat-card.js +82 -0
  259. package/dist/components/stat-card/stat-card.styles.d.ts +83 -0
  260. package/dist/components/stat-card/stat-card.styles.js +16 -0
  261. package/dist/components/stepper/index.d.ts +3 -0
  262. package/dist/components/stepper/stepper.d.ts +20 -0
  263. package/dist/components/stepper/stepper.js +100 -0
  264. package/dist/components/stepper/stepper.styles.d.ts +170 -0
  265. package/dist/components/stepper/stepper.styles.js +67 -0
  266. package/dist/components/switch/index.d.ts +2 -0
  267. package/dist/components/switch/switch.d.ts +51 -0
  268. package/dist/components/switch/switch.js +43 -0
  269. package/dist/components/table/index.d.ts +2 -0
  270. package/dist/components/table/table.d.ts +78 -0
  271. package/dist/components/table/table.js +82 -0
  272. package/dist/components/tabs/index.d.ts +2 -0
  273. package/dist/components/tabs/tabs.d.ts +73 -0
  274. package/dist/components/tabs/tabs.js +103 -0
  275. package/dist/components/tag-input/index.d.ts +3 -0
  276. package/dist/components/tag-input/tag-input.d.ts +30 -0
  277. package/dist/components/tag-input/tag-input.js +149 -0
  278. package/dist/components/tag-input/tag-input.styles.d.ts +71 -0
  279. package/dist/components/tag-input/tag-input.styles.js +29 -0
  280. package/dist/components/text/index.d.ts +3 -0
  281. package/dist/components/text/text.d.ts +9 -0
  282. package/dist/components/text/text.js +25 -0
  283. package/dist/components/text/text.styles.d.ts +188 -0
  284. package/dist/components/text/text.styles.js +36 -0
  285. package/dist/components/textarea/index.d.ts +2 -0
  286. package/dist/components/textarea/textarea.d.ts +29 -0
  287. package/dist/components/textarea/textarea.js +23 -0
  288. package/dist/components/timeline/index.d.ts +3 -0
  289. package/dist/components/timeline/timeline.d.ts +28 -0
  290. package/dist/components/timeline/timeline.js +61 -0
  291. package/dist/components/timeline/timeline.styles.d.ts +110 -0
  292. package/dist/components/timeline/timeline.styles.js +30 -0
  293. package/dist/components/toast/index.d.ts +2 -0
  294. package/dist/components/toast/toast.d.ts +119 -0
  295. package/dist/components/toast/toast.js +75 -0
  296. package/dist/components/toggle/index.d.ts +3 -0
  297. package/dist/components/toggle/toggle.d.ts +20 -0
  298. package/dist/components/toggle/toggle.js +58 -0
  299. package/dist/components/toggle/toggle.styles.d.ts +80 -0
  300. package/dist/components/toggle/toggle.styles.js +34 -0
  301. package/dist/components/tooltip/index.d.ts +2 -0
  302. package/dist/components/tooltip/tooltip.d.ts +39 -0
  303. package/dist/components/tooltip/tooltip.js +29 -0
  304. package/dist/components/tree-view/index.d.ts +3 -0
  305. package/dist/components/tree-view/tree-view.d.ts +31 -0
  306. package/dist/components/tree-view/tree-view.js +192 -0
  307. package/dist/components/tree-view/tree-view.styles.d.ts +59 -0
  308. package/dist/components/tree-view/tree-view.styles.js +32 -0
  309. package/dist/components/visually-hidden/index.d.ts +2 -0
  310. package/dist/components/visually-hidden/visually-hidden.d.ts +10 -0
  311. package/dist/components/visually-hidden/visually-hidden.js +16 -0
  312. package/dist/data-table.d.ts +3 -110
  313. package/dist/data-table.js +2 -224
  314. package/dist/form.d.ts +2 -79
  315. package/dist/form.js +1 -83
  316. package/dist/hooks/index.d.ts +5 -0
  317. package/dist/hooks/use-component-config.d.ts +13 -0
  318. package/dist/hooks/use-component-config.js +22 -0
  319. package/dist/hooks/use-controllable-state.d.ts +9 -0
  320. package/dist/hooks/use-controllable-state.js +18 -0
  321. package/dist/hooks/use-media-query.d.ts +4 -0
  322. package/dist/hooks/use-media-query.js +13 -0
  323. package/dist/hooks/use-scrolled.d.ts +4 -0
  324. package/dist/hooks/use-scrolled.js +17 -0
  325. package/dist/index.d.ts +223 -6849
  326. package/dist/index.js +148 -6718
  327. package/dist/internal/dev.js +12 -0
  328. package/dist/internal/icons.js +91 -0
  329. package/dist/motion/active-indicator.d.ts +12 -0
  330. package/dist/motion/active-indicator.js +26 -0
  331. package/dist/motion/animated-number.d.ts +21 -0
  332. package/dist/motion/animated-number.js +88 -0
  333. package/dist/motion/features.js +10 -0
  334. package/dist/motion/index.d.ts +10 -0
  335. package/dist/motion/index.js +10 -0
  336. package/dist/motion/motion-root.js +48 -0
  337. package/dist/motion/page-transition.d.ts +16 -0
  338. package/dist/motion/page-transition.js +23 -0
  339. package/dist/motion/presence-list.js +46 -0
  340. package/dist/motion/presence.d.ts +11 -0
  341. package/dist/motion/presence.js +17 -0
  342. package/dist/motion/reveal.d.ts +18 -0
  343. package/dist/motion/reveal.js +37 -0
  344. package/dist/motion/stagger.d.ts +29 -0
  345. package/dist/motion/stagger.js +52 -0
  346. package/dist/motion/use-motion-settings.d.ts +25 -0
  347. package/dist/motion/use-motion-settings.js +47 -0
  348. package/dist/motion/use-reveal.d.ts +11 -0
  349. package/dist/motion/use-reveal.js +41 -0
  350. package/dist/patterns/app-shell/app-shell-context.d.ts +14 -0
  351. package/dist/patterns/app-shell/app-shell-context.js +16 -0
  352. package/dist/patterns/app-shell/app-shell.d.ts +21 -0
  353. package/dist/patterns/app-shell/app-shell.js +82 -0
  354. package/dist/patterns/app-shell/app-shell.styles.d.ts +302 -0
  355. package/dist/patterns/app-shell/app-shell.styles.js +106 -0
  356. package/dist/patterns/app-shell/index.d.ts +7 -0
  357. package/dist/patterns/app-shell/navbar.d.ts +29 -0
  358. package/dist/patterns/app-shell/navbar.js +116 -0
  359. package/dist/patterns/app-shell/page-header.d.ts +12 -0
  360. package/dist/patterns/app-shell/page-header.js +38 -0
  361. package/dist/patterns/app-shell/sidebar.d.ts +31 -0
  362. package/dist/patterns/app-shell/sidebar.js +141 -0
  363. package/dist/patterns/async/async.d.ts +40 -0
  364. package/dist/patterns/async/async.js +61 -0
  365. package/dist/patterns/async/index.d.ts +3 -0
  366. package/dist/patterns/async/states.d.ts +68 -0
  367. package/dist/patterns/async/states.js +116 -0
  368. package/dist/patterns/chart/cartesian.d.ts +128 -0
  369. package/dist/patterns/chart/cartesian.js +552 -0
  370. package/dist/patterns/chart/chart-card.d.ts +39 -0
  371. package/dist/patterns/chart/chart-card.js +103 -0
  372. package/dist/patterns/chart/chart-parts.d.ts +53 -0
  373. package/dist/patterns/chart/chart-parts.js +282 -0
  374. package/dist/patterns/chart/chart.styles.d.ts +200 -0
  375. package/dist/patterns/chart/chart.styles.js +43 -0
  376. package/dist/patterns/chart/export.d.ts +45 -0
  377. package/dist/patterns/chart/export.js +293 -0
  378. package/dist/patterns/chart/funnel.d.ts +23 -0
  379. package/dist/patterns/chart/funnel.js +129 -0
  380. package/dist/patterns/chart/heatmap.d.ts +37 -0
  381. package/dist/patterns/chart/heatmap.js +258 -0
  382. package/dist/patterns/chart/icons.js +39 -0
  383. package/dist/patterns/chart/pie.d.ts +36 -0
  384. package/dist/patterns/chart/pie.js +120 -0
  385. package/dist/patterns/chart/radial.d.ts +75 -0
  386. package/dist/patterns/chart/radial.js +282 -0
  387. package/dist/patterns/chart/sankey.d.ts +29 -0
  388. package/dist/patterns/chart/sankey.js +136 -0
  389. package/dist/patterns/chart/scatter.d.ts +42 -0
  390. package/dist/patterns/chart/scatter.js +203 -0
  391. package/dist/patterns/chart/sparkline.d.ts +20 -0
  392. package/dist/patterns/chart/sparkline.js +102 -0
  393. package/dist/patterns/chart/treemap.d.ts +25 -0
  394. package/dist/patterns/chart/treemap.js +128 -0
  395. package/dist/patterns/chart/use-element-width.js +23 -0
  396. package/dist/patterns/data-table/data-table.d.ts +109 -0
  397. package/dist/patterns/data-table/data-table.js +233 -0
  398. package/dist/patterns/form/form.d.ts +89 -0
  399. package/dist/patterns/form/form.js +111 -0
  400. package/dist/provider/color-mode-script.d.ts +15 -0
  401. package/dist/provider/color-mode-script.js +13 -0
  402. package/dist/provider/color-mode.d.ts +24 -0
  403. package/dist/provider/color-mode.js +87 -0
  404. package/dist/provider/glyph-context.d.ts +51 -0
  405. package/dist/provider/glyph-context.js +28 -0
  406. package/dist/provider/glyph-provider.d.ts +37 -0
  407. package/dist/provider/glyph-provider.js +48 -0
  408. package/dist/provider/index.d.ts +6 -0
  409. package/dist/provider/theme-scope.d.ts +12 -0
  410. package/dist/provider/theme-scope.js +42 -0
  411. package/dist/provider/theme-style.js +15 -0
  412. package/dist/react-router.d.ts +18 -0
  413. package/dist/react-router.js +44 -0
  414. package/dist/register.d.ts +57 -0
  415. package/dist/themes.d.ts +22 -0
  416. package/dist/themes.js +49 -0
  417. package/dist/utils/cn.d.ts +6 -0
  418. package/dist/utils/cn.js +12 -0
  419. package/dist/utils/compose-refs.js +11 -0
  420. package/dist/{registry-BGSJoiLq.d.ts → utils/registry.d.ts} +2 -3
  421. package/dist/utils/styles.js +20 -0
  422. package/dist/{tv-DuMh7UH2.d.ts → utils/tv.d.ts} +2 -2
  423. package/dist/utils/tv.js +8 -0
  424. package/dist/utils/tw-merge-config.js +13 -0
  425. package/dist/vite.d.ts +27 -0
  426. package/dist/vite.js +29 -0
  427. package/fonts.css +10 -0
  428. package/package.json +33 -5
  429. package/src/components/accordion/accordion.styles.ts +1 -1
  430. package/src/components/alert/alert.styles.ts +1 -1
  431. package/src/components/alert-dialog/alert-dialog.styles.ts +5 -4
  432. package/src/components/alert-dialog/confirm-dialog.tsx +42 -0
  433. package/src/components/alert-dialog/confirm.tsx +12 -31
  434. package/src/components/aspect-ratio/aspect-ratio.tsx +1 -1
  435. package/src/components/avatar/avatar.styles.ts +1 -1
  436. package/src/components/badge/badge.styles.ts +12 -17
  437. package/src/components/banner/banner.styles.ts +5 -5
  438. package/src/components/blockquote/blockquote.tsx +1 -1
  439. package/src/components/breadcrumb/breadcrumb.styles.ts +1 -1
  440. package/src/components/button/button.styles.ts +6 -2
  441. package/src/components/calendar/calendar.styles.ts +4 -3
  442. package/src/components/card/card.styles.ts +1 -1
  443. package/src/components/carousel/carousel.styles.ts +1 -1
  444. package/src/components/checkbox/checkbox.tsx +20 -5
  445. package/src/components/code/code.styles.ts +2 -2
  446. package/src/components/collapsible/collapsible.tsx +1 -1
  447. package/src/components/combobox/combobox.styles.ts +5 -2
  448. package/src/components/command-menu/command-menu.styles.ts +6 -4
  449. package/src/components/container/container.tsx +1 -1
  450. package/src/components/context-menu/context-menu.styles.ts +4 -2
  451. package/src/components/copy-button/copy-button.tsx +1 -1
  452. package/src/components/date-picker/date-picker.styles.ts +2 -2
  453. package/src/components/description-list/description-list.styles.ts +1 -1
  454. package/src/components/dialog/dialog.styles.ts +11 -4
  455. package/src/components/dropdown-menu/dropdown-menu.styles.ts +5 -5
  456. package/src/components/eyebrow/eyebrow.styles.ts +2 -2
  457. package/src/components/field/field-context.ts +2 -0
  458. package/src/components/field/field.styles.ts +5 -3
  459. package/src/components/field/field.tsx +25 -4
  460. package/src/components/file-upload/file-upload.styles.ts +4 -4
  461. package/src/components/file-upload/file-upload.tsx +51 -48
  462. package/src/components/grid/grid.tsx +1 -1
  463. package/src/components/heading/heading.styles.ts +1 -1
  464. package/src/components/hover-card/hover-card.tsx +5 -2
  465. package/src/components/input/input.styles.ts +1 -1
  466. package/src/components/input-otp/input-otp.styles.ts +1 -1
  467. package/src/components/kbd/kbd.tsx +1 -1
  468. package/src/components/label/label.tsx +1 -1
  469. package/src/components/link/link.styles.ts +2 -2
  470. package/src/components/modal/modal.styles.ts +3 -1
  471. package/src/components/modal/modal.tsx +30 -5
  472. package/src/components/navigation-menu/navigation-menu.styles.ts +2 -2
  473. package/src/components/navigation-menu/navigation-menu.tsx +14 -4
  474. package/src/components/number-input/number-input.styles.ts +1 -1
  475. package/src/components/pagination/pagination.styles.ts +6 -4
  476. package/src/components/pagination/pagination.tsx +14 -1
  477. package/src/components/password-input/password-input.styles.ts +4 -4
  478. package/src/components/popover/popover.tsx +1 -1
  479. package/src/components/progress/progress.styles.ts +2 -2
  480. package/src/components/prose/prose.tsx +2 -2
  481. package/src/components/radio-group/radio-group.styles.ts +1 -1
  482. package/src/components/resizable/resizable.styles.ts +1 -1
  483. package/src/components/scroll-area/scroll-area.tsx +24 -2
  484. package/src/components/section/section.tsx +1 -1
  485. package/src/components/segmented-control/segmented-control.tsx +40 -9
  486. package/src/components/select/select.tsx +2 -2
  487. package/src/components/separator/separator.tsx +1 -1
  488. package/src/components/sheet/sheet.styles.ts +1 -1
  489. package/src/components/skeleton/skeleton.tsx +1 -1
  490. package/src/components/slider/slider.styles.ts +1 -1
  491. package/src/components/slider/slider.tsx +9 -2
  492. package/src/components/spinner/spinner.styles.ts +1 -1
  493. package/src/components/stack/stack.tsx +1 -1
  494. package/src/components/stat-card/stat-card.styles.ts +1 -1
  495. package/src/components/stepper/stepper.styles.ts +3 -3
  496. package/src/components/switch/switch.tsx +2 -2
  497. package/src/components/table/table.tsx +1 -1
  498. package/src/components/tabs/tabs.tsx +46 -15
  499. package/src/components/tag-input/tag-input.styles.ts +2 -2
  500. package/src/components/tag-input/tag-input.tsx +27 -18
  501. package/src/components/text/text.styles.ts +5 -5
  502. package/src/components/textarea/textarea.tsx +1 -1
  503. package/src/components/timeline/timeline.styles.ts +1 -1
  504. package/src/components/toast/toast.tsx +1 -1
  505. package/src/components/toggle/toggle.styles.ts +1 -1
  506. package/src/components/tooltip/tooltip.tsx +6 -3
  507. package/src/components/tree-view/tree-view.styles.ts +1 -1
  508. package/src/index.ts +3 -0
  509. package/src/integrations/react-router.tsx +39 -0
  510. package/src/integrations/vite.test-d.ts +6 -0
  511. package/src/integrations/vite.test.ts +28 -0
  512. package/src/integrations/vite.ts +33 -0
  513. package/src/internal/dev.ts +12 -0
  514. package/src/motion/active-indicator.tsx +26 -0
  515. package/src/motion/animated-number.tsx +113 -0
  516. package/src/motion/features.ts +7 -0
  517. package/src/motion/index.ts +9 -0
  518. package/src/motion/motion-root.tsx +46 -0
  519. package/src/motion/page-transition.tsx +30 -0
  520. package/src/motion/presence-list.tsx +42 -0
  521. package/src/motion/presence.tsx +13 -0
  522. package/src/motion/reveal.tsx +50 -0
  523. package/src/motion/stagger.tsx +81 -0
  524. package/src/motion/use-motion-settings.ts +52 -0
  525. package/src/motion/use-reveal.ts +54 -0
  526. package/src/patterns/app-shell/app-shell.styles.ts +19 -12
  527. package/src/patterns/app-shell/navbar.tsx +34 -16
  528. package/src/patterns/app-shell/sidebar.tsx +35 -9
  529. package/src/patterns/async/states.tsx +1 -1
  530. package/src/patterns/chart/cartesian.tsx +755 -0
  531. package/src/patterns/chart/chart-card.tsx +148 -0
  532. package/src/patterns/chart/chart-parts.tsx +330 -57
  533. package/src/patterns/chart/chart.styles.ts +29 -3
  534. package/src/patterns/chart/export.ts +334 -0
  535. package/src/patterns/chart/funnel.tsx +171 -0
  536. package/src/patterns/chart/heatmap.tsx +339 -0
  537. package/src/patterns/chart/icons.tsx +44 -0
  538. package/src/patterns/chart/index.ts +52 -7
  539. package/src/patterns/chart/pie.tsx +169 -0
  540. package/src/patterns/chart/radial.tsx +361 -0
  541. package/src/patterns/chart/sankey.tsx +197 -0
  542. package/src/patterns/chart/scatter.tsx +254 -0
  543. package/src/patterns/chart/sparkline.tsx +111 -0
  544. package/src/patterns/chart/test-utils.tsx +35 -0
  545. package/src/patterns/chart/treemap.tsx +198 -0
  546. package/src/patterns/chart/use-element-width.ts +20 -0
  547. package/src/patterns/data-table/data-table.tsx +1 -1
  548. package/src/patterns/form/form.tsx +49 -7
  549. package/src/patterns/form/index.ts +1 -0
  550. package/src/provider/color-mode-script.ts +28 -0
  551. package/src/provider/color-mode.tsx +14 -20
  552. package/src/provider/glyph-context.ts +41 -1
  553. package/src/provider/glyph-provider.tsx +45 -14
  554. package/src/provider/index.ts +4 -0
  555. package/src/provider/theme-scope.tsx +20 -11
  556. package/src/themes.ts +3 -3
  557. package/src/utils/styles.ts +17 -3
  558. package/styles.css +194 -3
  559. package/dist/field-context-anGPYXbW.js +0 -267
  560. package/dist/index-BWK4czIG.d.ts +0 -233
  561. package/dist/states-DR-OoDT-.js +0 -288
  562. package/dist/tv-DBAGlgXJ.js +0 -58
  563. package/src/patterns/chart/chart.tsx +0 -369
@@ -0,0 +1,148 @@
1
+ import { type ComponentProps, type ReactNode, useRef } from 'react'
2
+ import { IconButton } from '../../components/button'
3
+ import { Card, CardDescription, CardTitle } from '../../components/card'
4
+ import {
5
+ DropdownMenu,
6
+ DropdownMenuContent,
7
+ DropdownMenuItem,
8
+ DropdownMenuTrigger,
9
+ } from '../../components/dropdown-menu'
10
+ import { SegmentedControl, type SegmentedOption } from '../../components/segmented-control'
11
+ import { useComponentConfig } from '../../hooks/use-component-config'
12
+ import { useControllableState } from '../../hooks/use-controllable-state'
13
+ import { chartCardStyles } from './chart.styles'
14
+ import { ChartViewContext } from './chart-parts'
15
+ import { exportChart } from './export'
16
+ import { ChartIcon, DownloadIcon, TableIcon } from './icons'
17
+
18
+ export interface ChartPeriod<P extends string> {
19
+ options: readonly SegmentedOption<P>[]
20
+ value?: P
21
+ defaultValue?: P
22
+ onValueChange?: (value: P) => void
23
+ /** Default "Period". */
24
+ 'aria-label'?: string
25
+ }
26
+
27
+ export interface ChartCardProps<P extends string = string>
28
+ extends Omit<ComponentProps<'div'>, 'title' | 'children'> {
29
+ title: ReactNode
30
+ description?: ReactNode
31
+ /** A period selector in the header, e.g. 7d / 30d / 90d. */
32
+ period?: ChartPeriod<P>
33
+ /** Extra controls in the header, before the built-in buttons. */
34
+ actions?: ReactNode
35
+ /** Adds a button that shows the chart's data as a table. Default true. */
36
+ tableToggle?: boolean
37
+ view?: 'chart' | 'table'
38
+ defaultView?: 'chart' | 'table'
39
+ onViewChange?: (view: 'chart' | 'table') => void
40
+ /** Adds a download menu (PNG and SVG). Default true. */
41
+ exportable?: boolean | { fileName?: string }
42
+ /** Called when a download fails, e.g. to show a toast. Logs to the console by default. */
43
+ onExportError?: (error: unknown) => void
44
+ /** One chart from `@glyphui/ui/chart`. Its own title becomes the accessible name only. */
45
+ children: ReactNode
46
+ }
47
+
48
+ function slug(value: ReactNode): string {
49
+ const text = typeof value === 'string' || typeof value === 'number' ? String(value) : 'chart'
50
+ return (
51
+ text
52
+ .toLowerCase()
53
+ .replace(/[^a-z0-9]+/g, '-')
54
+ .replace(/^-|-$/g, '') || 'chart'
55
+ )
56
+ }
57
+
58
+ /**
59
+ * A card for one chart: title, period selector, "view as table" toggle and a download menu.
60
+ * The chart inside hides its own caption and switches to a table when asked.
61
+ */
62
+ export function ChartCard<P extends string = string>(inProps: ChartCardProps<P>) {
63
+ const [props, slots] = useComponentConfig('ChartCard', inProps)
64
+ const {
65
+ title,
66
+ description,
67
+ period,
68
+ actions,
69
+ tableToggle = true,
70
+ view: viewProp,
71
+ defaultView = 'chart',
72
+ onViewChange,
73
+ exportable = true,
74
+ onExportError,
75
+ className,
76
+ children,
77
+ ...rest
78
+ } = props
79
+ const s = chartCardStyles()
80
+ const body = useRef<HTMLDivElement>(null)
81
+ const [view, setView] = useControllableState({
82
+ value: viewProp,
83
+ defaultValue: defaultView,
84
+ onChange: onViewChange,
85
+ })
86
+ const fileName = (typeof exportable === 'object' ? exportable.fileName : undefined) ?? slug(title)
87
+
88
+ const download = (format: 'png' | 'svg') => {
89
+ exportChart(body.current, { format, fileName }).catch((error: unknown) => {
90
+ if (onExportError) onExportError(error)
91
+ else console.error(error)
92
+ })
93
+ }
94
+
95
+ return (
96
+ <Card data-slot="chart-card" className={s.root({ class: [slots.root, className] })} {...rest}>
97
+ <div className={s.header({ class: slots.header })}>
98
+ <div className={s.heading({ class: slots.heading })}>
99
+ <CardTitle>{title}</CardTitle>
100
+ {description && <CardDescription>{description}</CardDescription>}
101
+ </div>
102
+ <div className={s.controls({ class: slots.controls })}>
103
+ {period && (
104
+ <SegmentedControl<P>
105
+ size="sm"
106
+ aria-label={period['aria-label'] ?? 'Period'}
107
+ options={period.options}
108
+ {...(period.value !== undefined && { value: period.value })}
109
+ {...(period.defaultValue !== undefined && { defaultValue: period.defaultValue })}
110
+ {...(period.onValueChange && { onValueChange: period.onValueChange })}
111
+ />
112
+ )}
113
+ {actions}
114
+ {tableToggle && (
115
+ <IconButton
116
+ size="sm"
117
+ variant="ghost"
118
+ aria-label="View as table"
119
+ aria-pressed={view === 'table'}
120
+ icon={view === 'table' ? <ChartIcon /> : <TableIcon />}
121
+ onClick={() => setView(view === 'table' ? 'chart' : 'table')}
122
+ />
123
+ )}
124
+ {exportable && (
125
+ <DropdownMenu>
126
+ <DropdownMenuTrigger asChild>
127
+ <IconButton
128
+ size="sm"
129
+ variant="ghost"
130
+ aria-label="Download chart"
131
+ icon={<DownloadIcon />}
132
+ disabled={view === 'table'}
133
+ />
134
+ </DropdownMenuTrigger>
135
+ <DropdownMenuContent align="end">
136
+ <DropdownMenuItem onSelect={() => download('png')}>Download PNG</DropdownMenuItem>
137
+ <DropdownMenuItem onSelect={() => download('svg')}>Download SVG</DropdownMenuItem>
138
+ </DropdownMenuContent>
139
+ </DropdownMenu>
140
+ )}
141
+ </div>
142
+ </div>
143
+ <div ref={body} className={s.body({ class: slots.body })}>
144
+ <ChartViewContext.Provider value={{ view }}>{children}</ChartViewContext.Provider>
145
+ </div>
146
+ </Card>
147
+ )
148
+ }
@@ -1,9 +1,25 @@
1
- import { type ReactNode, useId } from 'react'
1
+ import { createContext, type ReactNode, useContext, useEffect, useId, useRef } from 'react'
2
+ import { Skeleton } from '../../components/skeleton'
3
+ import {
4
+ Table,
5
+ TableBody,
6
+ TableCell,
7
+ TableHead,
8
+ TableHeader,
9
+ TableRow,
10
+ } from '../../components/table'
2
11
  import { useComponentConfig } from '../../hooks/use-component-config'
12
+ import { useMotionSettings } from '../../motion/use-motion-settings'
13
+ import { EmptyState, ErrorState } from '../async/states'
3
14
  import { chartStyles } from './chart.styles'
4
15
 
5
16
  /** Theme colors a series can use by name. Any CSS color also works. */
6
17
  export type ChartColor =
18
+ | 'chart-1'
19
+ | 'chart-2'
20
+ | 'chart-3'
21
+ | 'chart-4'
22
+ | 'chart-5'
7
23
  | 'primary'
8
24
  | 'info'
9
25
  | 'success'
@@ -12,36 +28,120 @@ export type ChartColor =
12
28
  | 'foreground'
13
29
  | 'muted-foreground'
14
30
 
15
- const palette: ChartColor[] = ['primary', 'info', 'success', 'warning', 'danger']
31
+ /** A theme color name or any CSS color. */
32
+ export type ChartColorValue = ChartColor | (string & {})
16
33
 
34
+ export type Key<T> = Extract<keyof T, string>
35
+
36
+ /** Series without a color use the theme's chart palette, in order. */
37
+ const palette: ChartColor[] = ['chart-1', 'chart-2', 'chart-3', 'chart-4', 'chart-5']
38
+
39
+ /**
40
+ * Turns a theme color name into a CSS value. Uses the `--glyph-*` variables the theme always
41
+ * emits: Tailwind only emits `--color-*` variables it finds in your source, and these names
42
+ * are built at runtime.
43
+ */
17
44
  export function resolveColor(color: string | undefined, index: number): string {
18
45
  const value = color ?? palette[index % palette.length]!
19
- return /^[a-z-]+$/.test(value) ? `var(--color-${value})` : value
46
+ return /^[a-z][a-z\d-]*$/.test(value) ? `var(--glyph-${value})` : value
47
+ }
48
+
49
+ /**
50
+ * The palette color for the `index`-th category. After the fifth, colors are blended toward
51
+ * the muted surface instead of repeating, so no two categories ever share a color.
52
+ */
53
+ export function categoryColor(color: string | undefined, index: number): string {
54
+ if (color) return resolveColor(color, index)
55
+ const base = resolveColor(undefined, index)
56
+ const round = Math.floor(index / palette.length)
57
+ if (round === 0) return base
58
+ const mix = Math.max(30, 100 - round * 30)
59
+ return `color-mix(in oklab, ${base} ${mix}%, var(--glyph-muted))`
20
60
  }
21
61
 
22
62
  export const defaultFormatter = (value: number) =>
23
- typeof value === 'number' ? value.toLocaleString() : String(value)
63
+ typeof value === 'number' && Number.isFinite(value) ? value.toLocaleString() : String(value)
24
64
 
25
65
  export function useChartParts() {
26
66
  const [, slots] = useComponentConfig('Chart', {})
27
67
  return { s: chartStyles(), slots }
28
68
  }
29
69
 
70
+ /**
71
+ * Entry and update animation for recharts series, from the theme's motion settings:
72
+ * off for `none` and reduced motion, a little longer and bouncier for `expressive`.
73
+ */
74
+ export function useChartAnimation() {
75
+ const { animate, level, duration } = useMotionSettings()
76
+ return {
77
+ isAnimationActive: animate,
78
+ animationDuration: Math.round(duration.slow * 1000 * (level === 'expressive' ? 2.6 : 2)),
79
+ animationEasing: 'ease-out' as const,
80
+ }
81
+ }
82
+
30
83
  export interface LegendEntry {
84
+ /** Stable id used for toggling. Defaults to the label. */
85
+ key?: string
31
86
  label: ReactNode
32
87
  color: string
88
+ hidden?: boolean
89
+ }
90
+
91
+ export interface ChartLegendProps {
92
+ items: LegendEntry[]
93
+ /** Makes entries buttons that show and hide their series. */
94
+ onToggle?: ((key: string) => void) | undefined
95
+ className?: string
33
96
  }
34
97
 
35
- export function ChartLegend({ items }: { items: LegendEntry[] }) {
98
+ /** Legend under a chart. Interactive when `onToggle` is set: click or press Enter to hide a series. */
99
+ export function ChartLegend({ items, onToggle, className }: ChartLegendProps) {
36
100
  const { s, slots } = useChartParts()
101
+ if (!onToggle) {
102
+ return (
103
+ <ul
104
+ data-slot="chart-legend"
105
+ className={s.legend({ class: [slots.legend, className] })}
106
+ aria-hidden="true"
107
+ >
108
+ {items.map((item, i) => (
109
+ <li key={item.key ?? i} className={s.legendItem({ class: slots.legendItem })}>
110
+ <span
111
+ className={s.swatch({ class: slots.swatch })}
112
+ style={{ background: item.color }}
113
+ />
114
+ {item.label}
115
+ </li>
116
+ ))}
117
+ </ul>
118
+ )
119
+ }
37
120
  return (
38
- <ul data-slot="chart-legend" className={s.legend({ class: slots.legend })} aria-hidden="true">
39
- {items.map((item, i) => (
40
- <li key={i} className={s.legendItem({ class: slots.legendItem })}>
41
- <span className={s.swatch({ class: slots.swatch })} style={{ background: item.color }} />
42
- {item.label}
43
- </li>
44
- ))}
121
+ <ul
122
+ data-slot="chart-legend"
123
+ aria-label="Series"
124
+ className={s.legend({ class: [slots.legend, className] })}
125
+ >
126
+ {items.map((item, i) => {
127
+ const key = item.key ?? String(item.label ?? i)
128
+ return (
129
+ <li key={key} className={s.legendItem({ class: slots.legendItem })}>
130
+ <button
131
+ type="button"
132
+ aria-pressed={!item.hidden}
133
+ className={s.legendButton({ class: slots.legendButton })}
134
+ onClick={() => onToggle(key)}
135
+ >
136
+ <span
137
+ className={s.swatch({ class: slots.swatch })}
138
+ style={{ background: item.color }}
139
+ />
140
+ {item.label}
141
+ </button>
142
+ </li>
143
+ )
144
+ })}
45
145
  </ul>
46
146
  )
47
147
  }
@@ -54,21 +154,28 @@ interface TooltipPayloadItem {
54
154
  payload?: Record<string, unknown> & { fill?: string }
55
155
  }
56
156
 
157
+ export interface ChartTooltipCardProps {
158
+ active?: boolean | undefined
159
+ payload?: readonly TooltipPayloadItem[] | undefined
160
+ label?: ReactNode
161
+ formatter: (value: number) => string
162
+ labelFormatter?: ((label: ReactNode) => ReactNode) | undefined
163
+ /** Adds a total row, for stacked charts. */
164
+ total?: boolean | undefined
165
+ }
166
+
167
+ /** The themed tooltip every chart uses. Render it from recharts' `<Tooltip content>`. */
57
168
  export function ChartTooltipCard({
58
169
  active,
59
170
  payload,
60
171
  label,
61
172
  formatter,
62
173
  labelFormatter,
63
- }: {
64
- active?: boolean
65
- payload?: readonly TooltipPayloadItem[]
66
- label?: ReactNode
67
- formatter: (value: number) => string
68
- labelFormatter?: ((label: ReactNode) => ReactNode) | undefined
69
- }) {
174
+ total,
175
+ }: ChartTooltipCardProps) {
70
176
  const { s, slots } = useChartParts()
71
177
  if (!active || !payload?.length) return null
178
+ const sum = payload.reduce((acc, item) => acc + (Number(item.value) || 0), 0)
72
179
  return (
73
180
  <div data-slot="chart-tooltip" className={s.tooltip({ class: slots.tooltip })}>
74
181
  {label !== undefined && label !== '' && (
@@ -90,25 +197,125 @@ export function ChartTooltipCard({
90
197
  </span>
91
198
  </div>
92
199
  ))}
200
+ {total && payload.length > 1 && (
201
+ <div className={s.tooltipTotal({ class: slots.tooltipTotal })}>
202
+ <span className={s.tooltipName({ class: slots.tooltipName })}>Total</span>
203
+ <span className={s.tooltipValue({ class: slots.tooltipValue })}>{formatter(sum)}</span>
204
+ </div>
205
+ )}
93
206
  </div>
94
207
  )
95
208
  }
96
209
 
97
- export interface ChartFrameProps {
210
+ /** Loading, error and empty handling shared by every chart. */
211
+ export interface ChartStateProps {
212
+ /** Shows a skeleton in place of the plot. */
213
+ loading?: boolean
214
+ /** Shows an error message in place of the plot, with a retry button when `onRetry` is set. */
215
+ error?: unknown
216
+ onRetry?: () => unknown
217
+ /** Shown when there is no data. Defaults to a short "No data" message. */
218
+ empty?: ReactNode
219
+ }
220
+
221
+ /** Set by `ChartCard`: lets a chart render as a visible table instead of a plot. */
222
+ export interface ChartViewContextValue {
223
+ view: 'chart' | 'table'
224
+ }
225
+
226
+ export const ChartViewContext = createContext<ChartViewContextValue | null>(null)
227
+
228
+ export interface ChartTable {
229
+ columns: string[]
230
+ rows: (string | number)[][]
231
+ }
232
+
233
+ export interface ChartFrameProps extends ChartStateProps {
98
234
  /** Accessible name of the chart. Shown above it unless `hideTitle` is set. */
99
235
  title: string
100
236
  description?: ReactNode
101
- hideTitle?: boolean
102
- className?: string
237
+ hideTitle?: boolean | undefined
238
+ className?: string | undefined
103
239
  legend?: LegendEntry[] | undefined
240
+ /** Makes the legend interactive. */
241
+ onLegendToggle?: ((key: string) => void) | undefined
104
242
  /** Columns and rows for the screen-reader table that mirrors the chart. */
105
- table: { columns: string[]; rows: (string | number)[][] }
243
+ table: ChartTable
244
+ /** Plot height, so loading, error and empty states keep the layout stable. */
245
+ height?: number | undefined
246
+ /** True when there is nothing to plot. */
247
+ isEmpty?: boolean | undefined
248
+ /**
249
+ * The plot itself is accessible (e.g. Heatmap's keyboard grid), so it is not hidden from
250
+ * screen readers and no duplicate table is rendered.
251
+ */
252
+ accessiblePlot?: boolean | undefined
106
253
  children: ReactNode
107
254
  }
108
255
 
109
256
  /**
110
- * Wraps every chart in a `<figure>` with a caption and a visually hidden data table,
111
- * so screen-reader users get the numbers the chart shows.
257
+ * Keeps everything inside a hidden plot out of the tab order. Recharts makes some parts
258
+ * focusable (brush handles, the Sankey surface); inside an `aria-hidden` plot they would be
259
+ * reachable but unnamed. Keyboard and screen-reader users get the data from the table.
260
+ */
261
+ function useUnfocusable(enabled: boolean) {
262
+ const ref = useRef<HTMLDivElement>(null)
263
+ useEffect(() => {
264
+ const plot = ref.current
265
+ if (!enabled || !plot) return
266
+ const apply = () => {
267
+ for (const node of plot.querySelectorAll<HTMLElement | SVGElement>('[tabindex]')) {
268
+ if (node.getAttribute('tabindex') !== '-1') node.setAttribute('tabindex', '-1')
269
+ }
270
+ }
271
+ apply()
272
+ const observer = new MutationObserver(apply)
273
+ observer.observe(plot, { subtree: true, childList: true, attributeFilter: ['tabindex'] })
274
+ return () => observer.disconnect()
275
+ }, [enabled])
276
+ return ref
277
+ }
278
+
279
+ function DataTable({ table }: { table: ChartTable }) {
280
+ const { s, slots } = useChartParts()
281
+ return (
282
+ <div className={s.table({ class: slots.table })}>
283
+ <Table>
284
+ <TableHeader>
285
+ <TableRow>
286
+ {table.columns.map((column) => (
287
+ <TableHead key={column} scope="col">
288
+ {column}
289
+ </TableHead>
290
+ ))}
291
+ </TableRow>
292
+ </TableHeader>
293
+ <TableBody>
294
+ {table.rows.map((row, i) => (
295
+ <TableRow key={i}>
296
+ {row.map((cell, j) =>
297
+ j === 0 ? (
298
+ <TableHead key={j} scope="row" className="font-normal text-foreground">
299
+ {cell}
300
+ </TableHead>
301
+ ) : (
302
+ <TableCell key={j} className="tabular-nums">
303
+ {cell}
304
+ </TableCell>
305
+ ),
306
+ )}
307
+ </TableRow>
308
+ ))}
309
+ </TableBody>
310
+ </Table>
311
+ </div>
312
+ )
313
+ }
314
+
315
+ /**
316
+ * Wraps every chart in a `<figure>` with a caption and a visually hidden data table, so
317
+ * screen-reader users get the numbers the chart shows. Also renders loading, error and empty
318
+ * states in place of the plot, and the visible table when a `ChartCard` asks for it.
112
319
  */
113
320
  export function ChartFrame({
114
321
  title,
@@ -116,18 +323,61 @@ export function ChartFrame({
116
323
  hideTitle,
117
324
  className,
118
325
  legend,
326
+ onLegendToggle,
119
327
  table,
328
+ height = 240,
329
+ isEmpty,
330
+ loading,
331
+ error,
332
+ onRetry,
333
+ empty,
334
+ accessiblePlot,
120
335
  children,
121
336
  }: ChartFrameProps) {
122
337
  const { s, slots } = useChartParts()
123
338
  const titleId = useId()
339
+ const viewContext = useContext(ChartViewContext)
340
+ const plotRef = useUnfocusable(!accessiblePlot)
341
+ const asTable = viewContext?.view === 'table'
342
+ const captionHidden = hideTitle || viewContext !== null
343
+
344
+ let plot: ReactNode
345
+ let state = false
346
+ if (loading) {
347
+ state = true
348
+ plot = (
349
+ <div className={s.state({ class: slots.state })} style={{ height }} role="status">
350
+ <Skeleton className={s.skeleton({ class: slots.skeleton })} />
351
+ <span className={s.srOnly()}>Loading chart…</span>
352
+ </div>
353
+ )
354
+ } else if (error) {
355
+ state = true
356
+ plot = (
357
+ <div className={s.state({ class: slots.state })} style={{ minHeight: height }}>
358
+ <ErrorState error={error} onRetry={onRetry} title="Couldn’t load this chart" />
359
+ </div>
360
+ )
361
+ } else if (isEmpty) {
362
+ state = true
363
+ plot = (
364
+ <div className={s.state({ class: slots.state })} style={{ minHeight: height }}>
365
+ {empty ?? <EmptyState title="No data yet" description="Nothing to show for this period." />}
366
+ </div>
367
+ )
368
+ } else if (asTable) {
369
+ state = true
370
+ plot = <DataTable table={table} />
371
+ }
372
+
124
373
  return (
125
374
  <figure
126
375
  data-slot="chart"
127
376
  aria-labelledby={titleId}
377
+ aria-busy={loading || undefined}
128
378
  className={s.root({ class: [slots.root, className] })}
129
379
  >
130
- <figcaption className={hideTitle ? s.srOnly() : s.caption({ class: slots.caption })}>
380
+ <figcaption className={captionHidden ? s.srOnly() : s.caption({ class: slots.caption })}>
131
381
  <span id={titleId} className={s.title({ class: slots.title })}>
132
382
  {title}
133
383
  </span>
@@ -135,37 +385,60 @@ export function ChartFrame({
135
385
  <span className={s.description({ class: slots.description })}>{description}</span>
136
386
  )}
137
387
  </figcaption>
138
- <div className={s.plot({ class: slots.plot })} aria-hidden="true">
139
- {children}
140
- </div>
141
- {legend && legend.length > 1 && <ChartLegend items={legend} />}
142
- <table className={s.srOnly()}>
143
- <caption>{title}</caption>
144
- <thead>
145
- <tr>
146
- {table.columns.map((column) => (
147
- <th key={column} scope="col">
148
- {column}
149
- </th>
150
- ))}
151
- </tr>
152
- </thead>
153
- <tbody>
154
- {table.rows.map((row, i) => (
155
- <tr key={i}>
156
- {row.map((cell, j) =>
157
- j === 0 ? (
158
- <th key={j} scope="row">
159
- {cell}
160
- </th>
161
- ) : (
162
- <td key={j}>{cell}</td>
163
- ),
164
- )}
165
- </tr>
166
- ))}
167
- </tbody>
168
- </table>
388
+ {state ? (
389
+ plot
390
+ ) : (
391
+ <>
392
+ {/* Screen readers get the data table below. Charts pass `accessibilityLayer={false}`
393
+ so recharts adds no focusable nodes inside this hidden plot. */}
394
+ <div
395
+ ref={plotRef}
396
+ data-slot="chart-plot"
397
+ className={s.plot({ class: slots.plot })}
398
+ aria-hidden={accessiblePlot ? undefined : 'true'}
399
+ >
400
+ {children}
401
+ </div>
402
+ {legend && legend.length > 1 && <ChartLegend items={legend} onToggle={onLegendToggle} />}
403
+ {!accessiblePlot && (
404
+ <table className={s.srOnly()}>
405
+ <caption>{title}</caption>
406
+ <thead>
407
+ <tr>
408
+ {table.columns.map((column) => (
409
+ <th key={column} scope="col">
410
+ {column}
411
+ </th>
412
+ ))}
413
+ </tr>
414
+ </thead>
415
+ <tbody>
416
+ {table.rows.map((row, i) => (
417
+ <tr key={i}>
418
+ {row.map((cell, j) =>
419
+ j === 0 ? (
420
+ <th key={j} scope="row">
421
+ {cell}
422
+ </th>
423
+ ) : (
424
+ <td key={j}>{cell}</td>
425
+ ),
426
+ )}
427
+ </tr>
428
+ ))}
429
+ </tbody>
430
+ </table>
431
+ )}
432
+ </>
433
+ )}
169
434
  </figure>
170
435
  )
171
436
  }
437
+
438
+ /** Hidden-series state for interactive legends, controlled or not. */
439
+ export function toggleIn<K extends string>(list: readonly K[], key: K, total: number): K[] {
440
+ if (list.includes(key)) return list.filter((k) => k !== key)
441
+ // Never hide the last visible series: an empty plot reads as broken.
442
+ if (list.length + 1 >= total) return [...list]
443
+ return [...list, key]
444
+ }