@glyphui/ui 0.4.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 (546) hide show
  1. package/README.md +9 -6
  2. package/dist/chart.d.ts +14 -202
  3. package/dist/chart.js +14 -465
  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-CVbywksE.js → components/field/field.js} +6 -35
  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 -86
  315. package/dist/form.js +1 -108
  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 -6856
  326. package/dist/index.js +148 -6791
  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/{color-mode-script-KdwItKN2.d.ts → provider/color-mode-script.d.ts} +4 -5
  401. package/dist/{color-mode-script-Bf7LYkOj.js → provider/color-mode-script.js} +1 -1
  402. package/dist/provider/color-mode.d.ts +24 -0
  403. package/dist/provider/color-mode.js +87 -0
  404. package/dist/{glyph-context-DxGJrdvr.d.ts → provider/glyph-context.d.ts} +13 -12
  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 +1 -1
  413. package/dist/register.d.ts +57 -0
  414. package/dist/themes.d.ts +22 -0
  415. package/dist/themes.js +49 -0
  416. package/dist/utils/cn.d.ts +6 -0
  417. package/dist/utils/cn.js +12 -0
  418. package/dist/utils/compose-refs.js +11 -0
  419. package/dist/{registry-BGSJoiLq.d.ts → utils/registry.d.ts} +2 -3
  420. package/dist/utils/styles.js +20 -0
  421. package/dist/{tv-DuMh7UH2.d.ts → utils/tv.d.ts} +2 -2
  422. package/dist/utils/tv.js +8 -0
  423. package/dist/utils/tw-merge-config.js +13 -0
  424. package/dist/vite.d.ts +1 -1
  425. package/dist/vite.js +1 -1
  426. package/package.json +3 -2
  427. package/src/components/accordion/accordion.styles.ts +1 -1
  428. package/src/components/alert/alert.styles.ts +1 -1
  429. package/src/components/alert-dialog/alert-dialog.styles.ts +5 -4
  430. package/src/components/alert-dialog/confirm-dialog.tsx +42 -0
  431. package/src/components/alert-dialog/confirm.tsx +12 -31
  432. package/src/components/aspect-ratio/aspect-ratio.tsx +1 -1
  433. package/src/components/avatar/avatar.styles.ts +1 -1
  434. package/src/components/badge/badge.styles.ts +1 -1
  435. package/src/components/banner/banner.styles.ts +1 -1
  436. package/src/components/blockquote/blockquote.tsx +1 -1
  437. package/src/components/breadcrumb/breadcrumb.styles.ts +1 -1
  438. package/src/components/button/button.styles.ts +4 -2
  439. package/src/components/calendar/calendar.styles.ts +1 -1
  440. package/src/components/card/card.styles.ts +1 -1
  441. package/src/components/carousel/carousel.styles.ts +1 -1
  442. package/src/components/checkbox/checkbox.tsx +20 -5
  443. package/src/components/code/code.styles.ts +2 -2
  444. package/src/components/collapsible/collapsible.tsx +1 -1
  445. package/src/components/combobox/combobox.styles.ts +5 -2
  446. package/src/components/command-menu/command-menu.styles.ts +6 -4
  447. package/src/components/container/container.tsx +1 -1
  448. package/src/components/context-menu/context-menu.styles.ts +4 -2
  449. package/src/components/copy-button/copy-button.tsx +1 -1
  450. package/src/components/date-picker/date-picker.styles.ts +2 -2
  451. package/src/components/description-list/description-list.styles.ts +1 -1
  452. package/src/components/dialog/dialog.styles.ts +11 -4
  453. package/src/components/dropdown-menu/dropdown-menu.styles.ts +3 -3
  454. package/src/components/eyebrow/eyebrow.styles.ts +1 -1
  455. package/src/components/field/field.styles.ts +1 -1
  456. package/src/components/file-upload/file-upload.styles.ts +1 -1
  457. package/src/components/file-upload/file-upload.tsx +51 -48
  458. package/src/components/grid/grid.tsx +1 -1
  459. package/src/components/heading/heading.styles.ts +1 -1
  460. package/src/components/hover-card/hover-card.tsx +5 -2
  461. package/src/components/input/input.styles.ts +1 -1
  462. package/src/components/input-otp/input-otp.styles.ts +1 -1
  463. package/src/components/kbd/kbd.tsx +1 -1
  464. package/src/components/label/label.tsx +1 -1
  465. package/src/components/link/link.styles.ts +1 -1
  466. package/src/components/modal/modal.styles.ts +1 -1
  467. package/src/components/navigation-menu/navigation-menu.styles.ts +2 -2
  468. package/src/components/number-input/number-input.styles.ts +1 -1
  469. package/src/components/pagination/pagination.styles.ts +6 -4
  470. package/src/components/pagination/pagination.tsx +14 -1
  471. package/src/components/password-input/password-input.styles.ts +1 -1
  472. package/src/components/popover/popover.tsx +1 -1
  473. package/src/components/progress/progress.styles.ts +2 -2
  474. package/src/components/prose/prose.tsx +1 -1
  475. package/src/components/radio-group/radio-group.styles.ts +1 -1
  476. package/src/components/resizable/resizable.styles.ts +1 -1
  477. package/src/components/scroll-area/scroll-area.tsx +1 -1
  478. package/src/components/section/section.tsx +1 -1
  479. package/src/components/segmented-control/segmented-control.tsx +24 -8
  480. package/src/components/select/select.tsx +2 -2
  481. package/src/components/separator/separator.tsx +1 -1
  482. package/src/components/sheet/sheet.styles.ts +1 -1
  483. package/src/components/skeleton/skeleton.tsx +1 -1
  484. package/src/components/slider/slider.styles.ts +1 -1
  485. package/src/components/spinner/spinner.styles.ts +1 -1
  486. package/src/components/stack/stack.tsx +1 -1
  487. package/src/components/stat-card/stat-card.styles.ts +1 -1
  488. package/src/components/stepper/stepper.styles.ts +1 -1
  489. package/src/components/switch/switch.tsx +2 -2
  490. package/src/components/table/table.tsx +1 -1
  491. package/src/components/tabs/tabs.tsx +46 -15
  492. package/src/components/tag-input/tag-input.styles.ts +1 -1
  493. package/src/components/tag-input/tag-input.tsx +27 -18
  494. package/src/components/text/text.styles.ts +1 -1
  495. package/src/components/textarea/textarea.tsx +1 -1
  496. package/src/components/timeline/timeline.styles.ts +1 -1
  497. package/src/components/toast/toast.tsx +1 -1
  498. package/src/components/toggle/toggle.styles.ts +1 -1
  499. package/src/components/tooltip/tooltip.tsx +6 -3
  500. package/src/components/tree-view/tree-view.styles.ts +1 -1
  501. package/src/index.ts +3 -0
  502. package/src/motion/active-indicator.tsx +26 -0
  503. package/src/motion/animated-number.tsx +113 -0
  504. package/src/motion/features.ts +7 -0
  505. package/src/motion/index.ts +9 -0
  506. package/src/motion/motion-root.tsx +46 -0
  507. package/src/motion/page-transition.tsx +30 -0
  508. package/src/motion/presence-list.tsx +42 -0
  509. package/src/motion/presence.tsx +13 -0
  510. package/src/motion/reveal.tsx +50 -0
  511. package/src/motion/stagger.tsx +81 -0
  512. package/src/motion/use-motion-settings.ts +52 -0
  513. package/src/motion/use-reveal.ts +54 -0
  514. package/src/patterns/app-shell/app-shell.styles.ts +18 -11
  515. package/src/patterns/app-shell/sidebar.tsx +28 -7
  516. package/src/patterns/async/states.tsx +1 -1
  517. package/src/patterns/chart/cartesian.tsx +755 -0
  518. package/src/patterns/chart/chart-card.tsx +148 -0
  519. package/src/patterns/chart/chart-parts.tsx +323 -58
  520. package/src/patterns/chart/chart.styles.ts +29 -3
  521. package/src/patterns/chart/export.ts +334 -0
  522. package/src/patterns/chart/funnel.tsx +171 -0
  523. package/src/patterns/chart/heatmap.tsx +339 -0
  524. package/src/patterns/chart/icons.tsx +44 -0
  525. package/src/patterns/chart/index.ts +52 -7
  526. package/src/patterns/chart/pie.tsx +169 -0
  527. package/src/patterns/chart/radial.tsx +361 -0
  528. package/src/patterns/chart/sankey.tsx +197 -0
  529. package/src/patterns/chart/scatter.tsx +254 -0
  530. package/src/patterns/chart/sparkline.tsx +111 -0
  531. package/src/patterns/chart/test-utils.tsx +35 -0
  532. package/src/patterns/chart/treemap.tsx +198 -0
  533. package/src/patterns/chart/use-element-width.ts +20 -0
  534. package/src/patterns/data-table/data-table.tsx +1 -1
  535. package/src/patterns/form/form.tsx +1 -1
  536. package/src/provider/glyph-context.ts +3 -1
  537. package/src/provider/glyph-provider.tsx +31 -12
  538. package/src/provider/theme-scope.tsx +14 -9
  539. package/src/themes.ts +3 -3
  540. package/src/utils/styles.ts +17 -3
  541. package/styles.css +122 -3
  542. package/dist/field-context-7I-cWvvS.js +0 -267
  543. package/dist/index-Bi7rZ6ba.d.ts +0 -244
  544. package/dist/states-IvhjBgna.js +0 -288
  545. package/dist/tv-DLbT66dC.js +0 -70
  546. package/src/patterns/chart/chart.tsx +0 -386
@@ -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,5 +1,16 @@
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. */
@@ -17,37 +28,120 @@ export type ChartColor =
17
28
  | 'foreground'
18
29
  | 'muted-foreground'
19
30
 
31
+ /** A theme color name or any CSS color. */
32
+ export type ChartColorValue = ChartColor | (string & {})
33
+
34
+ export type Key<T> = Extract<keyof T, string>
35
+
20
36
  /** Series without a color use the theme's chart palette, in order. */
21
37
  const palette: ChartColor[] = ['chart-1', 'chart-2', 'chart-3', 'chart-4', 'chart-5']
22
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
+ */
23
44
  export function resolveColor(color: string | undefined, index: number): string {
24
45
  const value = color ?? palette[index % palette.length]!
25
- return /^[a-z][a-z\d-]*$/.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))`
26
60
  }
27
61
 
28
62
  export const defaultFormatter = (value: number) =>
29
- typeof value === 'number' ? value.toLocaleString() : String(value)
63
+ typeof value === 'number' && Number.isFinite(value) ? value.toLocaleString() : String(value)
30
64
 
31
65
  export function useChartParts() {
32
66
  const [, slots] = useComponentConfig('Chart', {})
33
67
  return { s: chartStyles(), slots }
34
68
  }
35
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
+
36
83
  export interface LegendEntry {
84
+ /** Stable id used for toggling. Defaults to the label. */
85
+ key?: string
37
86
  label: ReactNode
38
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
39
96
  }
40
97
 
41
- 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) {
42
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
+ }
43
120
  return (
44
- <ul data-slot="chart-legend" className={s.legend({ class: slots.legend })} aria-hidden="true">
45
- {items.map((item, i) => (
46
- <li key={i} className={s.legendItem({ class: slots.legendItem })}>
47
- <span className={s.swatch({ class: slots.swatch })} style={{ background: item.color }} />
48
- {item.label}
49
- </li>
50
- ))}
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
+ })}
51
145
  </ul>
52
146
  )
53
147
  }
@@ -60,21 +154,28 @@ interface TooltipPayloadItem {
60
154
  payload?: Record<string, unknown> & { fill?: string }
61
155
  }
62
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>`. */
63
168
  export function ChartTooltipCard({
64
169
  active,
65
170
  payload,
66
171
  label,
67
172
  formatter,
68
173
  labelFormatter,
69
- }: {
70
- active?: boolean
71
- payload?: readonly TooltipPayloadItem[]
72
- label?: ReactNode
73
- formatter: (value: number) => string
74
- labelFormatter?: ((label: ReactNode) => ReactNode) | undefined
75
- }) {
174
+ total,
175
+ }: ChartTooltipCardProps) {
76
176
  const { s, slots } = useChartParts()
77
177
  if (!active || !payload?.length) return null
178
+ const sum = payload.reduce((acc, item) => acc + (Number(item.value) || 0), 0)
78
179
  return (
79
180
  <div data-slot="chart-tooltip" className={s.tooltip({ class: slots.tooltip })}>
80
181
  {label !== undefined && label !== '' && (
@@ -96,25 +197,125 @@ export function ChartTooltipCard({
96
197
  </span>
97
198
  </div>
98
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
+ )}
99
206
  </div>
100
207
  )
101
208
  }
102
209
 
103
- 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 {
104
234
  /** Accessible name of the chart. Shown above it unless `hideTitle` is set. */
105
235
  title: string
106
236
  description?: ReactNode
107
- hideTitle?: boolean
108
- className?: string
237
+ hideTitle?: boolean | undefined
238
+ className?: string | undefined
109
239
  legend?: LegendEntry[] | undefined
240
+ /** Makes the legend interactive. */
241
+ onLegendToggle?: ((key: string) => void) | undefined
110
242
  /** Columns and rows for the screen-reader table that mirrors the chart. */
111
- 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
112
253
  children: ReactNode
113
254
  }
114
255
 
115
256
  /**
116
- * Wraps every chart in a `<figure>` with a caption and a visually hidden data table,
117
- * 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.
118
319
  */
119
320
  export function ChartFrame({
120
321
  title,
@@ -122,18 +323,61 @@ export function ChartFrame({
122
323
  hideTitle,
123
324
  className,
124
325
  legend,
326
+ onLegendToggle,
125
327
  table,
328
+ height = 240,
329
+ isEmpty,
330
+ loading,
331
+ error,
332
+ onRetry,
333
+ empty,
334
+ accessiblePlot,
126
335
  children,
127
336
  }: ChartFrameProps) {
128
337
  const { s, slots } = useChartParts()
129
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
+
130
373
  return (
131
374
  <figure
132
375
  data-slot="chart"
133
376
  aria-labelledby={titleId}
377
+ aria-busy={loading || undefined}
134
378
  className={s.root({ class: [slots.root, className] })}
135
379
  >
136
- <figcaption className={hideTitle ? s.srOnly() : s.caption({ class: slots.caption })}>
380
+ <figcaption className={captionHidden ? s.srOnly() : s.caption({ class: slots.caption })}>
137
381
  <span id={titleId} className={s.title({ class: slots.title })}>
138
382
  {title}
139
383
  </span>
@@ -141,39 +385,60 @@ export function ChartFrame({
141
385
  <span className={s.description({ class: slots.description })}>{description}</span>
142
386
  )}
143
387
  </figcaption>
144
- {/* Screen readers get the data table below. Charts pass `accessibilityLayer={false}`
145
- so recharts adds no focusable nodes inside this hidden plot. */}
146
- <div className={s.plot({ class: slots.plot })} aria-hidden="true">
147
- {children}
148
- </div>
149
- {legend && legend.length > 1 && <ChartLegend items={legend} />}
150
- <table className={s.srOnly()}>
151
- <caption>{title}</caption>
152
- <thead>
153
- <tr>
154
- {table.columns.map((column) => (
155
- <th key={column} scope="col">
156
- {column}
157
- </th>
158
- ))}
159
- </tr>
160
- </thead>
161
- <tbody>
162
- {table.rows.map((row, i) => (
163
- <tr key={i}>
164
- {row.map((cell, j) =>
165
- j === 0 ? (
166
- <th key={j} scope="row">
167
- {cell}
168
- </th>
169
- ) : (
170
- <td key={j}>{cell}</td>
171
- ),
172
- )}
173
- </tr>
174
- ))}
175
- </tbody>
176
- </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
+ )}
177
434
  </figure>
178
435
  )
179
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
+ }
@@ -1,16 +1,27 @@
1
1
  import type { StylesEntry } from '../../utils/registry'
2
- import { themedBorder } from '../../utils/styles'
2
+ import { focusRing, themedBorder } from '../../utils/styles'
3
3
  import { tv, type VariantProps } from '../../utils/tv'
4
4
 
5
- export const chartStyles = tv({
5
+ export const chartStyles = /* @__PURE__ */ tv({
6
6
  slots: {
7
7
  root: 'm-0 grid min-w-0 gap-3',
8
8
  caption: 'grid gap-0.5',
9
9
  title: 'text-small font-medium text-foreground',
10
10
  description: 'text-caption text-muted-foreground',
11
- plot: 'min-w-0 [&_.recharts-surface]:overflow-visible [&_.recharts-text]:fill-muted-foreground',
11
+ plot: [
12
+ 'relative min-w-0 [&_.recharts-surface]:overflow-visible',
13
+ '[&_.recharts-text]:fill-muted-foreground [&_.recharts-cartesian-axis-tick_text]:tabular-nums',
14
+ '[&_.recharts-layer]:outline-none [&_.recharts-sector]:outline-none [&_.recharts-rectangle]:outline-none',
15
+ ],
16
+ state: 'flex items-center justify-center',
17
+ skeleton: 'h-full w-full rounded-md',
12
18
  legend: 'flex flex-wrap items-center gap-x-4 gap-y-1 text-caption text-muted-foreground',
13
19
  legendItem: 'inline-flex items-center gap-1.5',
20
+ legendButton: [
21
+ '-mx-1 inline-flex cursor-pointer items-center gap-1.5 rounded-xs px-1 py-0.5 transition-opacity hover:text-foreground',
22
+ 'aria-[pressed=false]:opacity-45 aria-[pressed=false]:line-through aria-[pressed=false]:decoration-faint-foreground',
23
+ focusRing,
24
+ ],
14
25
  swatch: 'size-2.5 shrink-0 rounded-[2px]',
15
26
  tooltip: [
16
27
  'grid min-w-36 gap-1.5 rounded-md border-border bg-raised px-3 py-2 text-caption text-foreground shadow-lg',
@@ -20,16 +31,31 @@ export const chartStyles = tv({
20
31
  tooltipRow: 'flex items-center gap-2',
21
32
  tooltipName: 'flex-1 text-muted-foreground',
22
33
  tooltipValue: 'font-mono font-medium tabular-nums text-foreground',
34
+ tooltipTotal: 'mt-0.5 flex items-center gap-2 border-t border-border pt-1.5',
23
35
  center:
24
36
  'pointer-events-none absolute inset-0 flex flex-col items-center justify-center text-center',
37
+ table: 'max-h-96 overflow-auto rounded-md border-(length:--glyph-border-width) border-border',
25
38
  srOnly: 'sr-only',
26
39
  },
27
40
  })
28
41
 
29
42
  export type ChartVariants = VariantProps<typeof chartStyles>
30
43
 
44
+ export const chartCardStyles = /* @__PURE__ */ tv({
45
+ slots: {
46
+ root: 'min-w-0',
47
+ header: 'flex flex-wrap items-start justify-between gap-3',
48
+ heading: 'grid min-w-0 gap-1',
49
+ controls: 'flex flex-wrap items-center gap-2',
50
+ body: 'min-w-0',
51
+ },
52
+ })
53
+
54
+ export type ChartCardVariants = VariantProps<typeof chartCardStyles>
55
+
31
56
  declare module '@glyphui/tokens' {
32
57
  interface GlyphComponentRegistry {
33
58
  Chart: StylesEntry<typeof chartStyles>
59
+ ChartCard: StylesEntry<typeof chartCardStyles>
34
60
  }
35
61
  }