@webjsdev/ui 0.3.17 → 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 (360) hide show
  1. package/README.md +67 -8
  2. package/THIRD_PARTY_NOTICES.md +349 -0
  3. package/package.json +4 -2
  4. package/packages/registry/blocks/_shared/placeholder.svg +1 -0
  5. package/packages/registry/blocks/charts/chart-area-axes.ts +72 -0
  6. package/packages/registry/blocks/charts/chart-area-default.ts +64 -0
  7. package/packages/registry/blocks/charts/chart-area-gradient.ts +82 -0
  8. package/packages/registry/blocks/charts/chart-area-icons.ts +77 -0
  9. package/packages/registry/blocks/charts/chart-area-interactive.ts +204 -0
  10. package/packages/registry/blocks/charts/chart-area-legend.ts +72 -0
  11. package/packages/registry/blocks/charts/chart-area-linear.ts +64 -0
  12. package/packages/registry/blocks/charts/chart-area-stacked-expand.ts +77 -0
  13. package/packages/registry/blocks/charts/chart-area-stacked.ts +71 -0
  14. package/packages/registry/blocks/charts/chart-area-step.ts +68 -0
  15. package/packages/registry/blocks/charts/chart-bar-active.ts +93 -0
  16. package/packages/registry/blocks/charts/chart-bar-default.ts +60 -0
  17. package/packages/registry/blocks/charts/chart-bar-horizontal.ts +62 -0
  18. package/packages/registry/blocks/charts/chart-bar-interactive.ts +185 -0
  19. package/packages/registry/blocks/charts/chart-bar-label-custom.ts +78 -0
  20. package/packages/registry/blocks/charts/chart-bar-label.ts +66 -0
  21. package/packages/registry/blocks/charts/chart-bar-mixed.ts +87 -0
  22. package/packages/registry/blocks/charts/chart-bar-multiple.ts +67 -0
  23. package/packages/registry/blocks/charts/chart-bar-negative.ts +61 -0
  24. package/packages/registry/blocks/charts/chart-bar-stacked.ts +68 -0
  25. package/packages/registry/blocks/charts/chart-line-default.ts +61 -0
  26. package/packages/registry/blocks/charts/chart-line-dots-colors.ts +86 -0
  27. package/packages/registry/blocks/charts/chart-line-dots-custom.ts +75 -0
  28. package/packages/registry/blocks/charts/chart-line-dots.ts +65 -0
  29. package/packages/registry/blocks/charts/chart-line-interactive.ts +185 -0
  30. package/packages/registry/blocks/charts/chart-line-label-custom.ts +94 -0
  31. package/packages/registry/blocks/charts/chart-line-label.ts +73 -0
  32. package/packages/registry/blocks/charts/chart-line-linear.ts +61 -0
  33. package/packages/registry/blocks/charts/chart-line-multiple.ts +71 -0
  34. package/packages/registry/blocks/charts/chart-line-step.ts +61 -0
  35. package/packages/registry/blocks/charts/chart-pie-donut-active.ts +86 -0
  36. package/packages/registry/blocks/charts/chart-pie-donut-text.ts +89 -0
  37. package/packages/registry/blocks/charts/chart-pie-donut.ts +77 -0
  38. package/packages/registry/blocks/charts/chart-pie-interactive.ts +120 -0
  39. package/packages/registry/blocks/charts/chart-pie-label-custom.ts +82 -0
  40. package/packages/registry/blocks/charts/chart-pie-label-list.ts +86 -0
  41. package/packages/registry/blocks/charts/chart-pie-label.ts +77 -0
  42. package/packages/registry/blocks/charts/chart-pie-legend.ts +79 -0
  43. package/packages/registry/blocks/charts/chart-pie-separator-none.ts +77 -0
  44. package/packages/registry/blocks/charts/chart-pie-simple.ts +77 -0
  45. package/packages/registry/blocks/charts/chart-pie-stacked.ts +99 -0
  46. package/packages/registry/blocks/charts/chart-radar-default.ts +61 -0
  47. package/packages/registry/blocks/charts/chart-radar-dots.ts +61 -0
  48. package/packages/registry/blocks/charts/chart-radar-grid-circle-fill.ts +61 -0
  49. package/packages/registry/blocks/charts/chart-radar-grid-circle-no-lines.ts +61 -0
  50. package/packages/registry/blocks/charts/chart-radar-grid-circle.ts +61 -0
  51. package/packages/registry/blocks/charts/chart-radar-grid-custom.ts +61 -0
  52. package/packages/registry/blocks/charts/chart-radar-grid-fill.ts +61 -0
  53. package/packages/registry/blocks/charts/chart-radar-grid-none.ts +60 -0
  54. package/packages/registry/blocks/charts/chart-radar-icons.ts +76 -0
  55. package/packages/registry/blocks/charts/chart-radar-label-custom.ts +77 -0
  56. package/packages/registry/blocks/charts/chart-radar-legend.ts +70 -0
  57. package/packages/registry/blocks/charts/chart-radar-lines-only.ts +68 -0
  58. package/packages/registry/blocks/charts/chart-radar-multiple.ts +68 -0
  59. package/packages/registry/blocks/charts/chart-radar-radius.ts +68 -0
  60. package/packages/registry/blocks/charts/chart-radial-grid.ts +80 -0
  61. package/packages/registry/blocks/charts/chart-radial-label.ts +85 -0
  62. package/packages/registry/blocks/charts/chart-radial-shape.ts +81 -0
  63. package/packages/registry/blocks/charts/chart-radial-simple.ts +79 -0
  64. package/packages/registry/blocks/charts/chart-radial-stacked.ts +69 -0
  65. package/packages/registry/blocks/charts/chart-radial-text.ts +82 -0
  66. package/packages/registry/blocks/charts/chart-tooltip-advanced.ts +84 -0
  67. package/packages/registry/blocks/charts/chart-tooltip-default.ts +62 -0
  68. package/packages/registry/blocks/charts/chart-tooltip-formatter.ts +75 -0
  69. package/packages/registry/blocks/charts/chart-tooltip-icons.ts +68 -0
  70. package/packages/registry/blocks/charts/chart-tooltip-indicator-line.ts +62 -0
  71. package/packages/registry/blocks/charts/chart-tooltip-indicator-none.ts +62 -0
  72. package/packages/registry/blocks/charts/chart-tooltip-label-custom.ts +65 -0
  73. package/packages/registry/blocks/charts/chart-tooltip-label-formatter.ts +66 -0
  74. package/packages/registry/blocks/charts/chart-tooltip-label-none.ts +62 -0
  75. package/packages/registry/blocks/dashboard-01/components/app-sidebar.ts +108 -0
  76. package/packages/registry/blocks/dashboard-01/components/chart-area-interactive.ts +276 -0
  77. package/packages/registry/blocks/dashboard-01/components/dashboard-data.ts +89 -0
  78. package/packages/registry/blocks/dashboard-01/components/data-table.ts +644 -0
  79. package/packages/registry/blocks/dashboard-01/components/nav-documents.ts +65 -0
  80. package/packages/registry/blocks/dashboard-01/components/nav-main.ts +61 -0
  81. package/packages/registry/blocks/dashboard-01/components/nav-secondary.ts +40 -0
  82. package/packages/registry/blocks/dashboard-01/components/nav-user.ts +66 -0
  83. package/packages/registry/blocks/dashboard-01/components/section-cards.ts +59 -0
  84. package/packages/registry/blocks/dashboard-01/components/site-header.ts +52 -0
  85. package/packages/registry/blocks/dashboard-01/page.ts +39 -0
  86. package/packages/registry/blocks/login-01/components/login-form.ts +70 -0
  87. package/packages/registry/blocks/login-01/page.ts +19 -0
  88. package/packages/registry/blocks/login-02/components/login-form.ts +68 -0
  89. package/packages/registry/blocks/login-02/page.ts +36 -0
  90. package/packages/registry/blocks/login-03/components/login-form.ts +89 -0
  91. package/packages/registry/blocks/login-03/page.ts +28 -0
  92. package/packages/registry/blocks/login-04/components/login-form.ts +91 -0
  93. package/packages/registry/blocks/login-04/page.ts +20 -0
  94. package/packages/registry/blocks/login-05/components/login-form.ts +75 -0
  95. package/packages/registry/blocks/login-05/page.ts +19 -0
  96. package/packages/registry/blocks/preview/cards/activate-agent-dialog.ts +77 -0
  97. package/packages/registry/blocks/preview/cards/analytics-card.ts +64 -0
  98. package/packages/registry/blocks/preview/cards/anomaly-alert.ts +35 -0
  99. package/packages/registry/blocks/preview/cards/bar-chart-card.ts +91 -0
  100. package/packages/registry/blocks/preview/cards/book-appointment.ts +51 -0
  101. package/packages/registry/blocks/preview/cards/codespaces-card.ts +172 -0
  102. package/packages/registry/blocks/preview/cards/contributions-activity.ts +63 -0
  103. package/packages/registry/blocks/preview/cards/contributors.ts +51 -0
  104. package/packages/registry/blocks/preview/cards/environment-variables.ts +47 -0
  105. package/packages/registry/blocks/preview/cards/feedback-form.ts +61 -0
  106. package/packages/registry/blocks/preview/cards/file-upload.ts +51 -0
  107. package/packages/registry/blocks/preview/cards/github-profile.ts +66 -0
  108. package/packages/registry/blocks/preview/cards/icon-preview-grid.ts +47 -0
  109. package/packages/registry/blocks/preview/cards/invite-team.ts +78 -0
  110. package/packages/registry/blocks/preview/cards/invoice.ts +91 -0
  111. package/packages/registry/blocks/preview/cards/live-waveform.ts +330 -0
  112. package/packages/registry/blocks/preview/cards/no-team-members.ts +61 -0
  113. package/packages/registry/blocks/preview/cards/not-found.ts +46 -0
  114. package/packages/registry/blocks/preview/cards/observability-card.ts +44 -0
  115. package/packages/registry/blocks/preview/cards/pie-chart-card.ts +84 -0
  116. package/packages/registry/blocks/preview/cards/report-bug.ts +71 -0
  117. package/packages/registry/blocks/preview/cards/shipping-address.ts +79 -0
  118. package/packages/registry/blocks/preview/cards/shortcuts.ts +57 -0
  119. package/packages/registry/blocks/preview/cards/skeleton-loading.ts +37 -0
  120. package/packages/registry/blocks/preview/cards/sleep-report.ts +85 -0
  121. package/packages/registry/blocks/preview/cards/style-overview.ts +64 -0
  122. package/packages/registry/blocks/preview/cards/typography-specimen.ts +107 -0
  123. package/packages/registry/blocks/preview/cards/ui-elements.ts +164 -0
  124. package/packages/registry/blocks/preview/cards/usage-card.ts +63 -0
  125. package/packages/registry/blocks/preview/cards/visitors.ts +71 -0
  126. package/packages/registry/blocks/preview/cards/weekly-fitness-summary.ts +54 -0
  127. package/packages/registry/blocks/preview/page.ts +103 -0
  128. package/packages/registry/blocks/preview-02/cards/account-access.ts +70 -0
  129. package/packages/registry/blocks/preview-02/cards/card-overview.ts +75 -0
  130. package/packages/registry/blocks/preview-02/cards/claimable-balance.ts +54 -0
  131. package/packages/registry/blocks/preview-02/cards/contribution-history.ts +80 -0
  132. package/packages/registry/blocks/preview-02/cards/cover-art.ts +41 -0
  133. package/packages/registry/blocks/preview-02/cards/dividend-income.ts +76 -0
  134. package/packages/registry/blocks/preview-02/cards/empty-connect-bank.ts +38 -0
  135. package/packages/registry/blocks/preview-02/cards/empty-distribute-track.ts +38 -0
  136. package/packages/registry/blocks/preview-02/cards/empty-explore-catalog.ts +39 -0
  137. package/packages/registry/blocks/preview-02/cards/faq.ts +79 -0
  138. package/packages/registry/blocks/preview-02/cards/front-door.ts +40 -0
  139. package/packages/registry/blocks/preview-02/cards/index-investing.ts +31 -0
  140. package/packages/registry/blocks/preview-02/cards/kitchen-island.ts +90 -0
  141. package/packages/registry/blocks/preview-02/cards/loading-card.ts +34 -0
  142. package/packages/registry/blocks/preview-02/cards/new-milestone.ts +53 -0
  143. package/packages/registry/blocks/preview-02/cards/notification-settings.ts +126 -0
  144. package/packages/registry/blocks/preview-02/cards/payments.ts +88 -0
  145. package/packages/registry/blocks/preview-02/cards/payout-threshold.ts +92 -0
  146. package/packages/registry/blocks/preview-02/cards/power-usage.ts +84 -0
  147. package/packages/registry/blocks/preview-02/cards/preferences.ts +89 -0
  148. package/packages/registry/blocks/preview-02/cards/qr-connect.ts +54 -0
  149. package/packages/registry/blocks/preview-02/cards/receiving-method.ts +89 -0
  150. package/packages/registry/blocks/preview-02/cards/recent-transactions.ts +101 -0
  151. package/packages/registry/blocks/preview-02/cards/release-catalog.ts +73 -0
  152. package/packages/registry/blocks/preview-02/cards/roller-shades.ts +110 -0
  153. package/packages/registry/blocks/preview-02/cards/savings-progress.ts +68 -0
  154. package/packages/registry/blocks/preview-02/cards/savings-targets.ts +132 -0
  155. package/packages/registry/blocks/preview-02/cards/sidebar-nav.ts +88 -0
  156. package/packages/registry/blocks/preview-02/cards/social-links.ts +57 -0
  157. package/packages/registry/blocks/preview-02/cards/stock-performance.ts +106 -0
  158. package/packages/registry/blocks/preview-02/cards/syncing-state.ts +37 -0
  159. package/packages/registry/blocks/preview-02/cards/transfer-funds.ts +111 -0
  160. package/packages/registry/blocks/preview-02/cards/upcoming-payments.ts +69 -0
  161. package/packages/registry/blocks/preview-02/page.ts +111 -0
  162. package/packages/registry/blocks/preview-03/cards/files-chat.ts +75 -0
  163. package/packages/registry/blocks/preview-03/cards/group-chat.ts +114 -0
  164. package/packages/registry/blocks/preview-03/cards/reasoning-chat.ts +88 -0
  165. package/packages/registry/blocks/preview-03/cards/simple-chat.ts +53 -0
  166. package/packages/registry/blocks/preview-03/components/chat-card.ts +126 -0
  167. package/packages/registry/blocks/preview-03/components/chat-player.ts +174 -0
  168. package/packages/registry/blocks/preview-03/components/part-file.ts +77 -0
  169. package/packages/registry/blocks/preview-03/components/part-reasoning.ts +25 -0
  170. package/packages/registry/blocks/preview-03/components/part-source.ts +48 -0
  171. package/packages/registry/blocks/preview-03/components/part-text.ts +69 -0
  172. package/packages/registry/blocks/preview-03/components/part-tool.ts +71 -0
  173. package/packages/registry/blocks/preview-03/components/prompt-input.ts +86 -0
  174. package/packages/registry/blocks/preview-03/page.ts +39 -0
  175. package/packages/registry/blocks/sidebar-01/components/app-sidebar.ts +113 -0
  176. package/packages/registry/blocks/sidebar-01/components/search-form.ts +33 -0
  177. package/packages/registry/blocks/sidebar-01/components/version-switcher.ts +50 -0
  178. package/packages/registry/blocks/sidebar-01/page.ts +58 -0
  179. package/packages/registry/blocks/sidebar-02/components/app-sidebar.ts +131 -0
  180. package/packages/registry/blocks/sidebar-02/components/search-form.ts +33 -0
  181. package/packages/registry/blocks/sidebar-02/components/version-switcher.ts +50 -0
  182. package/packages/registry/blocks/sidebar-02/page.ts +53 -0
  183. package/packages/registry/blocks/sidebar-03/components/app-sidebar.ts +134 -0
  184. package/packages/registry/blocks/sidebar-03/page.ts +60 -0
  185. package/packages/registry/blocks/sidebar-04/components/app-sidebar.ts +136 -0
  186. package/packages/registry/blocks/sidebar-04/page.ts +58 -0
  187. package/packages/registry/blocks/sidebar-05/components/app-sidebar.ts +147 -0
  188. package/packages/registry/blocks/sidebar-05/components/search-form.ts +33 -0
  189. package/packages/registry/blocks/sidebar-05/page.ts +58 -0
  190. package/packages/registry/blocks/sidebar-06/components/app-sidebar.ts +105 -0
  191. package/packages/registry/blocks/sidebar-06/components/nav-main.ts +56 -0
  192. package/packages/registry/blocks/sidebar-06/components/sidebar-opt-in-form.ts +44 -0
  193. package/packages/registry/blocks/sidebar-06/page.ts +57 -0
  194. package/packages/registry/blocks/sidebar-07/components/app-sidebar.ts +108 -0
  195. package/packages/registry/blocks/sidebar-07/components/nav-main.ts +61 -0
  196. package/packages/registry/blocks/sidebar-07/components/nav-projects.ts +65 -0
  197. package/packages/registry/blocks/sidebar-07/components/nav-user.ts +70 -0
  198. package/packages/registry/blocks/sidebar-07/components/team-switcher.ts +66 -0
  199. package/packages/registry/blocks/sidebar-07/page.ts +59 -0
  200. package/packages/registry/blocks/sidebar-08/components/app-sidebar.ts +122 -0
  201. package/packages/registry/blocks/sidebar-08/components/nav-main.ts +68 -0
  202. package/packages/registry/blocks/sidebar-08/components/nav-projects.ts +65 -0
  203. package/packages/registry/blocks/sidebar-08/components/nav-secondary.ts +40 -0
  204. package/packages/registry/blocks/sidebar-08/components/nav-user.ts +70 -0
  205. package/packages/registry/blocks/sidebar-08/page.ts +59 -0
  206. package/packages/registry/blocks/sidebar-09/components/app-sidebar.ts +213 -0
  207. package/packages/registry/blocks/sidebar-09/components/nav-user.ts +70 -0
  208. package/packages/registry/blocks/sidebar-09/page.ts +52 -0
  209. package/packages/registry/blocks/sidebar-10/components/app-sidebar.ts +125 -0
  210. package/packages/registry/blocks/sidebar-10/components/nav-actions.ts +84 -0
  211. package/packages/registry/blocks/sidebar-10/components/nav-favorites.ts +66 -0
  212. package/packages/registry/blocks/sidebar-10/components/nav-main.ts +37 -0
  213. package/packages/registry/blocks/sidebar-10/components/nav-secondary.ts +43 -0
  214. package/packages/registry/blocks/sidebar-10/components/nav-workspaces.ts +78 -0
  215. package/packages/registry/blocks/sidebar-10/components/team-switcher.ts +63 -0
  216. package/packages/registry/blocks/sidebar-10/page.ts +46 -0
  217. package/packages/registry/blocks/sidebar-11/components/app-sidebar.ts +114 -0
  218. package/packages/registry/blocks/sidebar-11/page.ts +63 -0
  219. package/packages/registry/blocks/sidebar-12/components/app-sidebar.ts +63 -0
  220. package/packages/registry/blocks/sidebar-12/components/calendars.ts +53 -0
  221. package/packages/registry/blocks/sidebar-12/components/date-picker.ts +26 -0
  222. package/packages/registry/blocks/sidebar-12/components/nav-user.ts +70 -0
  223. package/packages/registry/blocks/sidebar-12/page.ts +43 -0
  224. package/packages/registry/blocks/sidebar-13/components/settings-dialog.ts +114 -0
  225. package/packages/registry/blocks/sidebar-13/page.ts +21 -0
  226. package/packages/registry/blocks/sidebar-14/components/app-sidebar.ts +120 -0
  227. package/packages/registry/blocks/sidebar-14/page.ts +55 -0
  228. package/packages/registry/blocks/sidebar-15/components/calendars.ts +53 -0
  229. package/packages/registry/blocks/sidebar-15/components/date-picker.ts +26 -0
  230. package/packages/registry/blocks/sidebar-15/components/nav-favorites.ts +66 -0
  231. package/packages/registry/blocks/sidebar-15/components/nav-main.ts +35 -0
  232. package/packages/registry/blocks/sidebar-15/components/nav-secondary.ts +43 -0
  233. package/packages/registry/blocks/sidebar-15/components/nav-user.ts +70 -0
  234. package/packages/registry/blocks/sidebar-15/components/nav-workspaces.ts +75 -0
  235. package/packages/registry/blocks/sidebar-15/components/sidebar-left.ts +126 -0
  236. package/packages/registry/blocks/sidebar-15/components/sidebar-right.ts +62 -0
  237. package/packages/registry/blocks/sidebar-15/components/team-switcher.ts +63 -0
  238. package/packages/registry/blocks/sidebar-15/page.ts +47 -0
  239. package/packages/registry/blocks/sidebar-16/components/app-sidebar.ts +124 -0
  240. package/packages/registry/blocks/sidebar-16/components/nav-main.ts +67 -0
  241. package/packages/registry/blocks/sidebar-16/components/nav-projects.ts +65 -0
  242. package/packages/registry/blocks/sidebar-16/components/nav-secondary.ts +40 -0
  243. package/packages/registry/blocks/sidebar-16/components/nav-user.ts +70 -0
  244. package/packages/registry/blocks/sidebar-16/components/search-form.ts +27 -0
  245. package/packages/registry/blocks/sidebar-16/components/site-header.ts +42 -0
  246. package/packages/registry/blocks/sidebar-16/page.ts +38 -0
  247. package/packages/registry/blocks/signup-01/components/signup-form.ts +78 -0
  248. package/packages/registry/blocks/signup-01/page.ts +19 -0
  249. package/packages/registry/blocks/signup-02/components/signup-form.ts +78 -0
  250. package/packages/registry/blocks/signup-02/page.ts +36 -0
  251. package/packages/registry/blocks/signup-03/components/signup-form.ts +79 -0
  252. package/packages/registry/blocks/signup-03/page.ts +28 -0
  253. package/packages/registry/blocks/signup-04/components/signup-form.ts +100 -0
  254. package/packages/registry/blocks/signup-04/page.ts +20 -0
  255. package/packages/registry/blocks/signup-05/components/signup-form.ts +75 -0
  256. package/packages/registry/blocks/signup-05/page.ts +20 -0
  257. package/packages/registry/components/accordion.ts +20 -7
  258. package/packages/registry/components/alert-dialog.ts +58 -13
  259. package/packages/registry/components/alert.ts +11 -6
  260. package/packages/registry/components/attachment.ts +119 -0
  261. package/packages/registry/components/avatar.ts +17 -8
  262. package/packages/registry/components/badge.ts +16 -9
  263. package/packages/registry/components/breadcrumb.ts +6 -6
  264. package/packages/registry/components/bubble.ts +95 -0
  265. package/packages/registry/components/button-group.ts +73 -0
  266. package/packages/registry/components/button.ts +35 -25
  267. package/packages/registry/components/calendar.ts +566 -0
  268. package/packages/registry/components/card.ts +8 -8
  269. package/packages/registry/components/carousel.ts +352 -0
  270. package/packages/registry/components/chart-element.ts +547 -0
  271. package/packages/registry/components/chart-geometry.ts +457 -0
  272. package/packages/registry/components/chart.ts +932 -0
  273. package/packages/registry/components/checkbox.ts +22 -84
  274. package/packages/registry/components/code-block.ts +191 -0
  275. package/packages/registry/components/collapsible.ts +5 -2
  276. package/packages/registry/components/combobox.ts +802 -0
  277. package/packages/registry/components/command.ts +618 -0
  278. package/packages/registry/components/context-menu.ts +302 -0
  279. package/packages/registry/components/dialog.ts +156 -45
  280. package/packages/registry/components/direction.ts +43 -0
  281. package/packages/registry/components/drawer.ts +226 -0
  282. package/packages/registry/components/dropdown-menu.ts +262 -83
  283. package/packages/registry/components/empty.ts +8 -8
  284. package/packages/registry/components/field.ts +20 -18
  285. package/packages/registry/components/floating.ts +124 -0
  286. package/packages/registry/components/hover-card.ts +19 -8
  287. package/packages/registry/components/input-group.ts +118 -0
  288. package/packages/registry/components/input-otp.ts +365 -0
  289. package/packages/registry/components/input.ts +3 -3
  290. package/packages/registry/components/item.ts +134 -0
  291. package/packages/registry/components/kbd.ts +2 -2
  292. package/packages/registry/components/label.ts +1 -1
  293. package/packages/registry/components/marker.ts +63 -0
  294. package/packages/registry/components/menubar.ts +388 -0
  295. package/packages/registry/components/message-scroller.ts +470 -0
  296. package/packages/registry/components/message.ts +69 -0
  297. package/packages/registry/components/native-select.ts +5 -5
  298. package/packages/registry/components/navigation-menu.ts +564 -0
  299. package/packages/registry/components/pagination.ts +5 -5
  300. package/packages/registry/components/popover.ts +30 -84
  301. package/packages/registry/components/progress.ts +7 -3
  302. package/packages/registry/components/questionnaire.ts +840 -0
  303. package/packages/registry/components/radio-group.ts +18 -73
  304. package/packages/registry/components/resizable.ts +330 -0
  305. package/packages/registry/components/scroll-area.ts +46 -0
  306. package/packages/registry/components/select.ts +650 -0
  307. package/packages/registry/components/separator.ts +1 -1
  308. package/packages/registry/components/sheet.ts +128 -0
  309. package/packages/registry/components/sidebar.ts +457 -0
  310. package/packages/registry/components/skeleton.ts +1 -1
  311. package/packages/registry/components/slider.ts +333 -0
  312. package/packages/registry/components/sonner.ts +74 -7
  313. package/packages/registry/components/spinner.ts +7 -4
  314. package/packages/registry/components/switch.ts +117 -9
  315. package/packages/registry/components/table.ts +11 -9
  316. package/packages/registry/components/tabs.ts +76 -25
  317. package/packages/registry/components/textarea.ts +1 -1
  318. package/packages/registry/components/toggle-group.ts +30 -11
  319. package/packages/registry/components/toggle.ts +20 -13
  320. package/packages/registry/components/tooltip.ts +68 -7
  321. package/packages/registry/components/trace.ts +75 -0
  322. package/packages/registry/lib/dom.ts +3 -4
  323. package/packages/registry/lib/icons-hugeicons.ts +955 -0
  324. package/packages/registry/lib/icons-phosphor.ts +955 -0
  325. package/packages/registry/lib/icons-remixicon.ts +955 -0
  326. package/packages/registry/lib/icons-tabler.ts +955 -0
  327. package/packages/registry/lib/icons.ts +768 -0
  328. package/packages/registry/lib/utils.ts +52 -8
  329. package/packages/registry/registry.json +4056 -72
  330. package/packages/registry/styles/shadcn-tailwind.css +654 -0
  331. package/packages/registry/styles/style-luma.css +1987 -0
  332. package/packages/registry/styles/style-lyra.css +1965 -0
  333. package/packages/registry/styles/style-maia.css +1987 -0
  334. package/packages/registry/styles/style-mira.css +1992 -0
  335. package/packages/registry/styles/style-new-york.css +1597 -0
  336. package/packages/registry/styles/style-nova.css +1990 -0
  337. package/packages/registry/styles/style-rhea.css +1987 -0
  338. package/packages/registry/styles/style-sera.css +1977 -0
  339. package/packages/registry/styles/style-vega.css +1990 -0
  340. package/packages/registry/styles/supplement.json +54 -0
  341. package/packages/registry/styles/tw-animate.css +6 -0
  342. package/packages/registry/themes/base-colors.js +8 -2
  343. package/packages/registry/themes/fonts.js +120 -0
  344. package/packages/registry/themes/index.css +2 -0
  345. package/packages/registry/themes/items.js +61 -0
  346. package/packages/registry/themes/preset.js +302 -0
  347. package/packages/registry/themes/themes.js +1193 -0
  348. package/src/commands/add.js +266 -14
  349. package/src/commands/diff.js +34 -20
  350. package/src/commands/info.js +7 -0
  351. package/src/commands/init.js +127 -10
  352. package/src/commands/list.js +43 -11
  353. package/src/commands/view.js +33 -5
  354. package/src/lint/rules/no-restyle.js +1 -1
  355. package/src/registry/extract.js +194 -5
  356. package/src/registry/icons.js +31 -0
  357. package/src/registry/local.js +15 -1
  358. package/src/registry/schema.js +13 -0
  359. package/src/registry/style-bake.js +506 -0
  360. package/src/utils/theme.js +110 -21
@@ -0,0 +1,932 @@
1
+ /**
2
+ * Chart: shadcn's Chart, drawn as plain SVG by the server. Tier-2. shadcn's
3
+ * is Recharts (D10: the kit takes no third-party runtime dependency), so
4
+ * this file ports the parts of Recharts the shadcn charts use: the bar, area,
5
+ * line, pie, radar and radial bar layouts, Recharts' default margins and axis
6
+ * sizes, and shadcn's tooltip and legend markup. Two files come with it and
7
+ * are re-exported here: `chart-geometry.ts`, the maths (scales, nice ticks,
8
+ * the d3 curves, the bar and sector paths), and `chart-element.ts`, the
9
+ * `<ui-chart>` element with the tooltip and legend markup it draws. Each chart function returns the whole `<ui-chart>`
10
+ * (shadcn's ChartContainer plus the chart inside it), so the first paint is
11
+ * the complete chart and it reads with JavaScript off.
12
+ *
13
+ * shadcn parity:
14
+ * ChartContainer config className → the chart function's `config` and `class`
15
+ * <BarChart> / <LineChart> / <AreaChart> → barChart({ ... }) / lineChart / areaChart
16
+ * <PieChart> / <RadarChart> / <RadialBarChart> → pieChart / radarChart / radialBarChart
17
+ * <Bar> / <Line> / <Area> children → `bars` / `lines` / `areas` arrays (same prop names)
18
+ * <CartesianGrid> / <XAxis> / <YAxis> → `grid` / `xAxis` / `yAxis` (same prop names)
19
+ * <ChartTooltip content={<ChartTooltipContent … />} />
20
+ * → `tooltip: { indicator, hideLabel, nameKey, … }`
21
+ * <ChartLegend content={<ChartLegendContent />} /> → `legend: { … }`
22
+ * <LabelList> / <Label> → `labels: [{ position, offset, class, … }]`, `center`
23
+ * shape / dot / label render props → the same options as functions returning `svg` templates
24
+ * ChartConfig → ChartConfig (same shape; `icon` is a function returning a template)
25
+ *
26
+ * The series colours are custom properties in the container's `style`
27
+ * (`--color-desktop: var(--chart-1)`, `light-dark(a, b)` for a `theme`
28
+ * entry), so `fill: 'var(--color-desktop)'` reads exactly as in shadcn.
29
+ * Every part carries a `ui-chart-*` class named after the `recharts-*` class
30
+ * it replaces (`recharts-pie-label-text` → `ui-chart-pie-label-text`), so a
31
+ * shadcn `className` such as `[&_.recharts-text]:fill-background` ports by
32
+ * that one rename.
33
+ *
34
+ * Sizing: the SVG has a `viewBox` of the chart's nominal size (450 by 253 for
35
+ * the default `aspect-video` container, 250 square for the polar charts) and
36
+ * scales to its container. Give `width` / `height` when the container has
37
+ * another shape (`class: 'aspect-auto h-[250px] w-full'` wants a wide box).
38
+ *
39
+ * The element: `<ui-chart>` adds what needs a pointer. Hovering shows the
40
+ * tooltip, the cursor and the active dots for the nearest point; the chart
41
+ * takes focus (Recharts' accessibilityLayer), the arrow keys step through the
42
+ * points, Home / End jump, Escape hides. Every tooltip is resolved by the
43
+ * server, one per point, so a `labelFormatter`, a `formatter` or an `icon`
44
+ * runs once on the server and never ships. A point whose tooltip is all text
45
+ * is served as data and drawn by the element; one with an icon or a custom
46
+ * row is served as markup.
47
+ *
48
+ * Design tokens used: `--chart-1` to `--chart-5` through the config, plus
49
+ * `border`, `muted`, `muted-foreground`, `background` and `foreground`.
50
+ *
51
+ * A11y (handled by the element, plus your part): the SVG is a `role="img"`
52
+ * named by `title`; once the element upgrades and the chart has a tooltip it
53
+ * becomes a focusable `role="application"` whose arrow keys move the tooltip,
54
+ * and each step is announced through a polite live region. Your part: pass a
55
+ * `title` that says what the chart shows, and keep the numbers available in
56
+ * the text around it (the card's description or footer) for a reader who
57
+ * cannot use the chart.
58
+ *
59
+ * @example
60
+ * ```html
61
+ * ${barChart({
62
+ * config: { desktop: { label: 'Desktop', color: 'var(--chart-1)' } },
63
+ * data: [{ month: 'January', desktop: 186 }, { month: 'February', desktop: 305 }],
64
+ * title: 'Desktop visitors by month',
65
+ * grid: { vertical: false },
66
+ * xAxis: { dataKey: 'month', tickLine: false, axisLine: false, tickMargin: 10, tickFormatter: (v) => String(v).slice(0, 3) },
67
+ * tooltip: { cursor: false, hideLabel: true },
68
+ * bars: [{ dataKey: 'desktop', fill: 'var(--color-desktop)', radius: 8 }],
69
+ * })}
70
+ * ```
71
+ */
72
+ import { html, svg, nothing } from '@webjsdev/core';
73
+ import { cn } from '../lib/utils.ts';
74
+ import {
75
+ RADIAN, fmt, formatValue, num, percent, estimateTextWidth, niceTicks, linearTicks, linearScale, bandScale, pointScale,
76
+ curvePath, rectanglePath, polarToCartesian, sectorPath, polygonPath, stackData, visibleTicks, barPositions,
77
+ type CurveType, type Pt, type SectorGeometry, type Tick,
78
+ } from './chart-geometry.ts';
79
+ import {
80
+ UiChart, chartStyle, payloadConfig, chartTooltipView, chartTooltipMarkup, chartTooltipContent, chartLegendContent, isPlainView,
81
+ type ChartConfig, type ChartConfigItem, type ChartDatum, type Margin, type ChartPayloadItem, type ChartTooltipOptions,
82
+ type ChartLegendOptions, type ChartBaseOptions, type ChartTooltipView, type TooltipLook,
83
+ } from './chart-element.ts';
84
+
85
+ export {
86
+ fmt, formatValue, estimateTextWidth, niceTicks, linearTicks, linearScale, bandScale, pointScale, curvePath,
87
+ rectanglePath, polarToCartesian, sectorPath, polygonPath, stackData, visibleTicks, barPositions,
88
+ };
89
+ export type { CurveType, SectorGeometry, Tick };
90
+ export { UiChart, chartStyle, payloadConfig, chartTooltipView, chartTooltipMarkup, chartTooltipContent, chartLegendContent };
91
+ export type {
92
+ ChartConfig, ChartConfigItem, ChartDatum, Margin, ChartPayloadItem, ChartTooltipOptions, ChartLegendOptions, ChartBaseOptions,
93
+ ChartTooltipView,
94
+ };
95
+
96
+ // --------------------------------------------------------------------------
97
+ // Labels (Recharts' Label / LabelList positions)
98
+ // --------------------------------------------------------------------------
99
+
100
+ export type LabelPosition = 'top' | 'bottom' | 'left' | 'right' | 'inside' | 'insideLeft' | 'insideRight' | 'insideTop' | 'insideBottom' | 'center' | 'middle' | 'outside' | 'insideStart' | 'insideEnd' | 'end';
101
+
102
+ /** A LabelList: one label per data point of a series. */
103
+ export interface ChartLabelOptions {
104
+ /** The value to print, a key of the datum. Defaults to the series' dataKey. */
105
+ dataKey?: string;
106
+ position?: LabelPosition;
107
+ offset?: number;
108
+ class?: string;
109
+ fill?: string;
110
+ stroke?: string;
111
+ fontSize?: number;
112
+ formatter?: (value: unknown, datum: ChartDatum, index: number) => unknown;
113
+ /** A custom label: return an `svg` template. */
114
+ content?: (props: { x: number; y: number; width: number; height: number; value: unknown; datum: ChartDatum; index: number }) => unknown;
115
+ }
116
+
117
+ type VAnchor = 'start' | 'middle' | 'end';
118
+ const DY: Record<VAnchor, string> = { start: '0.71em', middle: '0.355em', end: '0em' };
119
+
120
+ /** A text element as Recharts' `<Text>` writes one: the position on the text, the baseline shift on a tspan. */
121
+ export function chartText(x: number, y: number, value: unknown, o: { anchor?: 'start' | 'middle' | 'end'; vAnchor?: VAnchor; class?: string; fill?: string | null; stroke?: string; fontSize?: number; baseline?: string } = {}) {
122
+ // A size in pixels, as Recharts takes it, written in em of the chart's
123
+ // 12px text so it keeps its size when the chart scales (see `_watchSize`).
124
+ const size = o.fontSize != null ? `${fmt(o.fontSize / 12)}em` : nothing;
125
+ return svg`<text x=${fmt(x)} y=${fmt(y)} text-anchor=${o.anchor ?? 'middle'} fill=${o.fill ?? nothing} stroke=${o.stroke ?? nothing} font-size=${size} alignment-baseline=${o.baseline ?? nothing} class=${cn('ui-chart-text', o.class)}><tspan x=${fmt(x)} dy=${DY[o.vAnchor ?? 'end']}>${value}</tspan></text>`;
126
+ }
127
+
128
+ function cartesianLabelAttrs(box: { x: number; y: number; width: number; height: number }, position: LabelPosition, offset: number): { x: number; y: number; anchor: 'start' | 'middle' | 'end'; vAnchor: VAnchor } {
129
+ const { x, y, width, height } = box;
130
+ const vs = height >= 0 ? 1 : -1;
131
+ const hs = width >= 0 ? 1 : -1;
132
+ switch (position) {
133
+ case 'top': return { x: x + width / 2, y: y - vs * offset, anchor: 'middle', vAnchor: vs > 0 ? 'end' : 'start' };
134
+ case 'bottom': return { x: x + width / 2, y: y + height + vs * offset, anchor: 'middle', vAnchor: vs > 0 ? 'start' : 'end' };
135
+ case 'left': return { x: x - hs * offset, y: y + height / 2, anchor: hs > 0 ? 'end' : 'start', vAnchor: 'middle' };
136
+ case 'right': return { x: x + width + hs * offset, y: y + height / 2, anchor: hs > 0 ? 'start' : 'end', vAnchor: 'middle' };
137
+ case 'insideLeft': return { x: x + hs * offset, y: y + height / 2, anchor: hs > 0 ? 'start' : 'end', vAnchor: 'middle' };
138
+ case 'insideRight': return { x: x + width - hs * offset, y: y + height / 2, anchor: hs > 0 ? 'end' : 'start', vAnchor: 'middle' };
139
+ case 'insideTop': return { x: x + width / 2, y: y + vs * offset, anchor: 'middle', vAnchor: 'start' };
140
+ case 'insideBottom': return { x: x + width / 2, y: y + height - vs * offset, anchor: 'middle', vAnchor: 'end' };
141
+ default: return { x: x + width / 2, y: y + height / 2, anchor: 'middle', vAnchor: 'middle' };
142
+ }
143
+ }
144
+
145
+ function labelList(labels: ChartLabelOptions[] | undefined, seriesKey: string, items: Array<{ box: { x: number; y: number; width: number; height: number }; datum: ChartDatum; index: number; fill?: string }>, fallbackPosition: LabelPosition) {
146
+ if (!labels?.length) return '';
147
+ return labels.map((l) => svg`<g class="ui-chart-label-list">${items.map(({ box, datum, index, fill }) => {
148
+ const raw = datum[l.dataKey ?? seriesKey];
149
+ const value = l.formatter ? l.formatter(raw, datum, index) : raw;
150
+ if (l.content) return l.content({ ...box, value, datum, index });
151
+ const a = cartesianLabelAttrs(box, l.position ?? fallbackPosition, l.offset ?? 5);
152
+ // A label takes its mark's colour unless it names one, as Recharts' does.
153
+ return chartText(a.x, a.y, value, { anchor: a.anchor, vAnchor: a.vAnchor, class: cn('ui-chart-label', l.class), fill: l.fill ?? fill, stroke: l.stroke, fontSize: l.fontSize });
154
+ })}</g>`);
155
+ }
156
+
157
+ // --------------------------------------------------------------------------
158
+ // The container (shadcn's ChartContainer) and the tooltip layer
159
+ // --------------------------------------------------------------------------
160
+
161
+ /**
162
+ * shadcn's ChartContainer classes, its `[&_.recharts-*]` rules renamed to the
163
+ * `ui-chart-*` parts, plus one of the kit's own: strokes keep their width in
164
+ * pixels when the chart scales, as Recharts draws them at the real size.
165
+ */
166
+ export function chartContainerClass(): string {
167
+ return "cn-chart flex aspect-video justify-center text-xs [&_.ui-chart-cartesian-axis-tick_text]:fill-muted-foreground [&_.ui-chart-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.ui-chart-curve.ui-chart-tooltip-cursor]:stroke-border [&_.ui-chart-dot[stroke='#fff']]:stroke-transparent [&_.ui-chart-layer]:outline-hidden [&_.ui-chart-polar-grid_[stroke='#ccc']]:stroke-border [&_.ui-chart-radial-bar-background-sector]:fill-muted [&_.ui-chart-rectangle.ui-chart-tooltip-cursor]:fill-muted [&_.ui-chart-reference-line_[stroke='#ccc']]:stroke-border [&_.ui-chart-sector]:outline-hidden [&_.ui-chart-sector[stroke='#fff']]:stroke-transparent [&_.ui-chart-surface]:outline-hidden [&_.ui-chart-surface_*]:[vector-effect:non-scaling-stroke]";
168
+ }
169
+
170
+ /** One tooltip point: its category, its series, and where the tooltip anchors (viewBox units). */
171
+ export interface ChartPoint {
172
+ label: unknown;
173
+ payload: ChartPayloadItem[];
174
+ x: number;
175
+ y: number;
176
+ }
177
+
178
+ interface Frame {
179
+ width: number;
180
+ height: number;
181
+ /** How the element maps a pointer to a point: along x, along y, or by the hovered shape. */
182
+ hit: 'x' | 'y' | 'target';
183
+ body: unknown;
184
+ points: ChartPoint[];
185
+ legend: ChartPayloadItem[];
186
+ margin: Margin;
187
+ /** The plot area, the box Recharts keeps a tooltip inside. */
188
+ plot: { x: number; y: number; w: number; h: number };
189
+ /** The element may widen the plot to fill its box (a cartesian chart). */
190
+ fit?: boolean;
191
+ }
192
+
193
+ // Recharts sorts a legend by each item's value (its `itemSorter` default),
194
+ // which for a series is its name: a stable sort, so equal names keep order.
195
+ function sortLegend(items: ChartPayloadItem[]): ChartPayloadItem[] {
196
+ return [...items].sort((a, b) => {
197
+ const x = String(a.value);
198
+ const y = String(b.value);
199
+ return x < y ? -1 : x > y ? 1 : 0;
200
+ });
201
+ }
202
+
203
+ function legendLayer(o: ChartBaseOptions, f: Frame) {
204
+ if (!o.legend) return '';
205
+ // An HTML row over the chart rather than a foreignObject inside it, as
206
+ // Recharts' legend wrapper is: anchored at the margin, as wide as the area
207
+ // between the side margins, as tall as its content (a wrapping legend grows
208
+ // away from the edge), and in real pixels, since WebKit misplaces the
209
+ // content of a scaled foreignObject and Firefox does not scale its font.
210
+ const pct = (v: number, of: number) => `${fmt((v / of) * 100)}%`;
211
+ const edge = o.legend.verticalAlign === 'top' ? `top: ${pct(f.margin.top, f.height)}` : `bottom: ${pct(f.margin.bottom, f.height)}`;
212
+ return html`<div class="ui-chart-legend-wrapper absolute" style=${`left: ${pct(f.margin.left, f.width)}; right: ${pct(f.margin.right, f.width)}; ${edge}`}>${chartLegendContent(o.config, sortLegend(f.legend), o.legend)}</div>`;
213
+ }
214
+
215
+ function frame(o: ChartBaseOptions, f: Frame) {
216
+ const t = o.tooltip || null;
217
+ const def = t && t.defaultIndex != null ? f.points[t.defaultIndex] : undefined;
218
+ const a11y = o.accessibilityLayer !== false;
219
+ // Each point's tooltip is resolved here, on the server, so a formatter never
220
+ // ships. One that is all strings is served as data (a 90 point chart would
221
+ // otherwise carry 90 copies of the markup); one with an icon or a custom
222
+ // row is served as markup.
223
+ const look: TooltipLook = t ? { indicator: t.indicator, hideIndicator: t.hideIndicator, class: t.class, labelClass: t.labelClass } : {};
224
+ const views = t ? f.points.map((p) => chartTooltipView(o.config, p.label, p.payload, t)) : [];
225
+ // cn() keeps both aspect utilities, so an authored one replaces the default here.
226
+ const base = /(^|\s)aspect-/.test(o.class ?? '') ? chartContainerClass().replace(' aspect-video', '') : chartContainerClass();
227
+ return html`<ui-chart data-slot="chart" data-chart=${o.id ?? nothing} class=${cn(base, o.class)} style=${chartStyle(o.config) || nothing}><div class="ui-chart-wrapper relative h-full w-full min-w-0"><svg class="ui-chart-surface block h-full w-full overflow-visible" xmlns="http://www.w3.org/2000/svg" viewBox=${`0 0 ${fmt(f.width)} ${fmt(f.height)}`} role="img" aria-label=${o.title ?? nothing} data-hit=${t ? f.hit : nothing} data-keyboard=${t && a11y ? 'true' : nothing} data-default-index=${def ? String(t?.defaultIndex) : nothing} data-fit=${f.fit ? 'x' : nothing} data-plot=${t || f.fit ? [f.plot.x, f.plot.y, f.plot.w, f.plot.h].map((v) => fmt(v)).join(' ') : nothing}>${o.defs ? svg`<defs>${o.defs}</defs>` : ''}${f.body}</svg>${legendLayer(o, f)}${t ? html`<div data-slot="chart-tooltip" class="ui-chart-tooltip-wrapper pointer-events-none absolute top-0 left-0 z-10 [transition:transform_400ms]" ?hidden=${!def} style=${def ? `transform: translate(${fmt(def.x + 10)}px, ${fmt(def.y + 10)}px)` : nothing}>${def && t && t.defaultIndex != null ? chartTooltipMarkup(views[t.defaultIndex], look) : ''}</div><div data-slot="chart-tooltips" hidden data-look=${JSON.stringify(look)}>${views.map((v, i) => (isPlainView(v)
228
+ ? html`<div data-index=${String(i)} data-x=${fmt(f.points[i].x)} data-y=${fmt(f.points[i].y)} data-tip=${JSON.stringify(v)}></div>`
229
+ : html`<div data-index=${String(i)} data-x=${fmt(f.points[i].x)} data-y=${fmt(f.points[i].y)}>${chartTooltipMarkup(v, look)}</div>`))}</div><span data-slot="chart-live" class="sr-only" aria-live="polite"></span>` : ''}</div></ui-chart>`;
230
+ }
231
+
232
+ /** The per-point overlay groups: hidden until their point is active (or it is the default). */
233
+ function overlay(slot: 'chart-cursor' | 'chart-active', parts: unknown[], defaultIndex: number | undefined) {
234
+ return svg`<g data-slot=${slot}>${parts.map((p, i) => svg`<g data-index=${String(i)} visibility=${i === defaultIndex ? 'visible' : 'hidden'}>${p}</g>`)}</g>`;
235
+ }
236
+
237
+ function resolveMargin(m: Partial<Margin> | undefined): Margin {
238
+ if (!m) return { top: 5, right: 5, bottom: 5, left: 5 };
239
+ return { top: m.top ?? 0, right: m.right ?? 0, bottom: m.bottom ?? 0, left: m.left ?? 0 };
240
+ }
241
+
242
+ // --------------------------------------------------------------------------
243
+ // Cartesian charts: bar, line, area
244
+ // --------------------------------------------------------------------------
245
+
246
+ export interface ChartAxisOptions {
247
+ dataKey?: string;
248
+ type?: 'category' | 'number';
249
+ hide?: boolean;
250
+ tick?: boolean;
251
+ tickLine?: boolean;
252
+ axisLine?: boolean;
253
+ tickMargin?: number;
254
+ tickSize?: number;
255
+ tickFormatter?: (value: unknown, index: number) => unknown;
256
+ minTickGap?: number;
257
+ tickCount?: number;
258
+ interval?: number | 'preserveEnd' | 'preserveStart' | 'preserveStartEnd';
259
+ /** The x axis's height, default 30. */
260
+ height?: number;
261
+ /** The y axis's width, default 60. */
262
+ width?: number;
263
+ domain?: [number | 'auto' | 'dataMin' | 'dataMax', number | 'auto' | 'dataMin' | 'dataMax'];
264
+ allowDecimals?: boolean;
265
+ padding?: { left?: number; right?: number; top?: number; bottom?: number };
266
+ /** Run the axis the other way, Recharts' `reversed` (a right-to-left chart's x axis). */
267
+ reversed?: boolean;
268
+ class?: string;
269
+ }
270
+
271
+ export interface ChartDotOptions { r?: number; fill?: string; stroke?: string; strokeWidth?: number; fillOpacity?: number; class?: string }
272
+ export type ChartDotRender = (props: { cx: number; cy: number; r: number; fill: string; stroke: string; datum: ChartDatum; index: number; dataKey: string }) => unknown;
273
+
274
+ export interface ChartBarShapeProps {
275
+ x: number; y: number; width: number; height: number;
276
+ radius: number | number[]; fill: string; datum: ChartDatum; index: number; dataKey: string;
277
+ stroke?: string; strokeWidth?: number; fillOpacity?: number;
278
+ }
279
+
280
+ /** A bar as the kit draws one (Recharts' `<Rectangle>`), for a custom `shape` to reuse. */
281
+ export function chartRectangle(p: Omit<ChartBarShapeProps, 'datum' | 'index' | 'dataKey'> & { strokeDasharray?: number | string; strokeDashoffset?: number; class?: string }) {
282
+ return svg`<path class=${cn('ui-chart-rectangle', p.class)} fill=${p.fill} fill-opacity=${p.fillOpacity ?? nothing} stroke=${p.stroke ?? nothing} stroke-width=${p.strokeWidth ?? nothing} stroke-dasharray=${p.strokeDasharray ?? nothing} stroke-dashoffset=${p.strokeDashoffset ?? nothing} d=${rectanglePath(p.x, p.y, p.width, p.height, p.radius)}></path>`;
283
+ }
284
+
285
+ export interface ChartSeriesOptions {
286
+ dataKey: string;
287
+ name?: string;
288
+ fill?: string;
289
+ stroke?: string;
290
+ strokeWidth?: number;
291
+ fillOpacity?: number;
292
+ strokeDasharray?: string;
293
+ stackId?: string;
294
+ /** Line and area curve, Recharts' `type`. */
295
+ type?: CurveType;
296
+ /** Bar corner radius, one number or `[tl, tr, br, bl]`. */
297
+ radius?: number | number[];
298
+ /** Line dots (default on for lines, off for areas), options or a render function. */
299
+ dot?: boolean | ChartDotOptions | ChartDotRender;
300
+ /** The hover dot (lines and areas), default on. */
301
+ activeDot?: boolean | ChartDotOptions;
302
+ labels?: ChartLabelOptions[];
303
+ /** A bar drawn active from the first paint, and how (Recharts' `activeIndex` + `activeBar`). */
304
+ activeIndex?: number;
305
+ activeBar?: { fill?: string; fillOpacity?: number; stroke?: string; strokeWidth?: number; strokeDasharray?: string; strokeDashoffset?: number };
306
+ /** A custom bar, Recharts' `shape`. */
307
+ shape?: (props: ChartBarShapeProps) => unknown;
308
+ barSize?: number;
309
+ class?: string;
310
+ hide?: boolean;
311
+ }
312
+
313
+ export interface CartesianChartOptions extends ChartBaseOptions {
314
+ layout?: 'horizontal' | 'vertical';
315
+ barCategoryGap?: number | string;
316
+ barGap?: number | string;
317
+ maxBarSize?: number;
318
+ stackOffset?: 'none' | 'expand' | 'sign';
319
+ grid?: false | { horizontal?: boolean; vertical?: boolean; strokeDasharray?: string; class?: string };
320
+ xAxis?: ChartAxisOptions | false;
321
+ yAxis?: ChartAxisOptions | false;
322
+ }
323
+
324
+ type Family = 'bar' | 'line' | 'area';
325
+
326
+ function valueDomain(spec: ChartAxisOptions | undefined, dataMin: number, dataMax: number, expand: boolean): { domain: [number, number]; ticks: number[] } {
327
+ if (expand) return { domain: [0, 1], ticks: niceTicks(0, 1, spec?.tickCount ?? 5) };
328
+ const d = spec?.domain;
329
+ const pick = (v: unknown, auto: number, data: number): number => (typeof v === 'number' ? v : v === 'dataMin' || v === 'dataMax' ? data : auto);
330
+ const lo = pick(d?.[0], Math.min(0, dataMin), dataMin);
331
+ const hi = pick(d?.[1], dataMax, dataMax);
332
+ const ticks = niceTicks(lo, hi, spec?.tickCount ?? 5, spec?.allowDecimals ?? true);
333
+ const [d0, d1] = d ?? [];
334
+ if (typeof d0 === 'number' && typeof d1 === 'number') return { domain: [d0, d1], ticks: ticks.filter((t) => t >= d0 && t <= d1) };
335
+ return { domain: [Math.min(lo, ticks[0]), Math.max(hi, ticks[ticks.length - 1])], ticks };
336
+ }
337
+
338
+ function dotProps(spec: boolean | ChartDotOptions | ChartDotRender | undefined, fallback: boolean): ChartDotOptions | ChartDotRender | null {
339
+ if (spec === undefined) return fallback ? {} : null;
340
+ if (spec === false) return null;
341
+ if (spec === true) return {};
342
+ return spec;
343
+ }
344
+
345
+ function drawDot(spec: ChartDotOptions | ChartDotRender, base: { cx: number; cy: number; r: number; fill: string; stroke: string; strokeWidth: number }, datum: ChartDatum, index: number, dataKey: string, cls: string) {
346
+ if (typeof spec === 'function') return spec({ cx: base.cx, cy: base.cy, r: base.r, fill: base.fill, stroke: base.stroke, datum, index, dataKey });
347
+ return svg`<circle cx=${fmt(base.cx)} cy=${fmt(base.cy)} r=${fmt(spec.r ?? base.r)} fill=${spec.fill ?? base.fill} fill-opacity=${spec.fillOpacity ?? nothing} stroke=${spec.stroke ?? base.stroke} stroke-width=${spec.strokeWidth ?? base.strokeWidth} class=${cn('ui-chart-dot', cls, spec.class)}></circle>`;
348
+ }
349
+
350
+ function cartesian(o: CartesianChartOptions, family: Family, all: ChartSeriesOptions[]) {
351
+ const W = o.width ?? 450;
352
+ const H = o.height ?? Math.round((W * 9) / 16);
353
+ const margin = resolveMargin(o.margin);
354
+ const vertical = o.layout === 'vertical';
355
+ const xSpec = o.xAxis || undefined;
356
+ const ySpec = o.yAxis || undefined;
357
+ const catSpec = vertical ? ySpec : xSpec;
358
+ const valSpec = vertical ? xSpec : ySpec;
359
+ const xShown = !!xSpec && !xSpec.hide;
360
+ const yShown = !!ySpec && !ySpec.hide;
361
+ const xH = xShown ? xSpec.height ?? 30 : 0;
362
+ const yW = yShown ? ySpec.width ?? 60 : 0;
363
+ const legendH = o.legend ? o.legend.height ?? 28 : 0;
364
+ const legendTop = !!o.legend && o.legend.verticalAlign === 'top';
365
+ const plot = {
366
+ x: margin.left + yW,
367
+ y: margin.top + (legendTop ? legendH : 0),
368
+ w: W - margin.left - margin.right - yW,
369
+ h: H - margin.top - margin.bottom - xH - legendH,
370
+ };
371
+ const data = o.data;
372
+ const series = all.filter((s) => !s.hide);
373
+ const catKey = catSpec?.dataKey;
374
+ const categories = data.map((d, i) => (catKey ? d[catKey] : i));
375
+
376
+ // Values: each series' [low, high] per row, stacked by stackId.
377
+ const offsetMode = o.stackOffset ?? (family === 'bar' ? 'sign' : 'none');
378
+ const bands: Record<string, Array<[number, number]>> = {};
379
+ const stacks = new Map<string, string[]>();
380
+ for (const s of series) if (s.stackId != null) stacks.set(s.stackId, [...(stacks.get(s.stackId) ?? []), s.dataKey]);
381
+ for (const keys of stacks.values()) Object.assign(bands, stackData(data, keys, offsetMode === 'expand' ? 'expand' : offsetMode === 'sign' ? 'sign' : 'none'));
382
+ for (const s of series) if (s.stackId == null) bands[s.dataKey] = data.map((d) => [0, num(d[s.dataKey])]);
383
+ const flat = series.flatMap((s) => bands[s.dataKey].flat());
384
+ const { domain, ticks: valueTicks } = valueDomain(valSpec, flat.length ? Math.min(...flat) : 0, flat.length ? Math.max(...flat) : 0, offsetMode === 'expand');
385
+ const vRange: [number, number] = vertical ? [plot.x, plot.x + plot.w] : [plot.y + plot.h, plot.y];
386
+ const vScale = linearScale(domain, valSpec?.reversed ? [vRange[1], vRange[0]] : vRange);
387
+
388
+ // Categories: bands under bars, points under lines and areas.
389
+ const pad = catSpec?.padding ?? {};
390
+ const c0 = vertical ? plot.y + (pad.top ?? 0) : plot.x + (pad.left ?? 0);
391
+ const c1 = vertical ? plot.y + plot.h - (pad.bottom ?? 0) : plot.x + plot.w - (pad.right ?? 0);
392
+ // A reversed category axis lays the rows out from the far end.
393
+ const slot = (i: number) => (catSpec?.reversed ? data.length - 1 - i : i);
394
+ const band = bandScale(data.length, c0, c1);
395
+ const point = pointScale(data.length, c0, c1);
396
+ const catAt = (i: number) => (family === 'bar' ? band.at(slot(i)) + band.bandwidth / 2 : point(slot(i)));
397
+ const color = (s: ChartSeriesOptions) => (family === 'bar' ? s.fill : s.stroke ?? s.fill) ?? '#808080';
398
+ const defaultIndex = o.tooltip ? o.tooltip.defaultIndex : undefined;
399
+
400
+ // Grid lines at the value ticks and the category ticks.
401
+ const g = o.grid;
402
+ const gridLine = (x1: number, y1: number, x2: number, y2: number) => svg`<line stroke="#ccc" fill="none" stroke-dasharray=${(g && g.strokeDasharray) || nothing} x1=${fmt(x1)} y1=${fmt(y1)} x2=${fmt(x2)} y2=${fmt(y2)}></line>`;
403
+ const valueCoords = valueTicks.map((t) => vScale(t));
404
+ const catCoords = data.map((_, i) => catAt(i));
405
+ const hLines = vertical ? catCoords : valueCoords;
406
+ const vLines = vertical ? valueCoords : catCoords;
407
+ const grid = g === undefined || g === false ? '' : svg`<g class=${cn('ui-chart-cartesian-grid', g.class)}>${g.horizontal === false ? '' : svg`<g class="ui-chart-cartesian-grid-horizontal">${hLines.map((y) => gridLine(plot.x, y, plot.x + plot.w, y))}</g>`}${g.vertical === false ? '' : svg`<g class="ui-chart-cartesian-grid-vertical">${vLines.map((x) => gridLine(x, plot.y, x, plot.y + plot.h))}</g>`}</g>`;
408
+
409
+ // The hover cursor per point.
410
+ const cursorOn = !!o.tooltip && o.tooltip.cursor !== false;
411
+ const cursors = cursorOn
412
+ ? data.map((_, i) => {
413
+ if (family === 'bar') {
414
+ const a = band.at(slot(i));
415
+ return vertical
416
+ ? svg`<path class="ui-chart-rectangle ui-chart-tooltip-cursor" fill="#ccc" d=${rectanglePath(plot.x, a, plot.w, band.bandwidth)}></path>`
417
+ : svg`<path class="ui-chart-rectangle ui-chart-tooltip-cursor" fill="#ccc" d=${rectanglePath(a, plot.y, band.bandwidth, plot.h)}></path>`;
418
+ }
419
+ const c = catAt(i);
420
+ return vertical
421
+ ? svg`<path class="ui-chart-curve ui-chart-tooltip-cursor" stroke="#ccc" fill="none" d=${`M${fmt(plot.x)},${fmt(c)}L${fmt(plot.x + plot.w)},${fmt(c)}`}></path>`
422
+ : svg`<path class="ui-chart-curve ui-chart-tooltip-cursor" stroke="#ccc" fill="none" d=${`M${fmt(c)},${fmt(plot.y)}L${fmt(c)},${fmt(plot.y + plot.h)}`}></path>`;
423
+ })
424
+ : [];
425
+
426
+ // Marks.
427
+ const slotIds: string[] = [];
428
+ for (const s of series) { const id = s.stackId != null ? `stack:${s.stackId}` : `bar:${s.dataKey}`; if (!slotIds.includes(id)) slotIds.push(id); }
429
+ const slotSpecs = slotIds.map((id) => ({ barSize: series.find((s) => (s.stackId != null ? `stack:${s.stackId}` : `bar:${s.dataKey}`) === id)?.barSize }));
430
+ const positions = family === 'bar' ? barPositions(band.bandwidth, slotSpecs, o.barGap, o.barCategoryGap, o.maxBarSize) : [];
431
+
432
+ const marks = series.map((s) => {
433
+ const sb = bands[s.dataKey];
434
+ if (family === 'bar') {
435
+ const pos = positions[slotIds.indexOf(s.stackId != null ? `stack:${s.stackId}` : `bar:${s.dataKey}`)];
436
+ const boxes = data.map((d, i) => {
437
+ const [lo, hi] = sb[i];
438
+ const a = band.at(slot(i)) + pos.offset;
439
+ const box = vertical
440
+ ? { x: vScale(lo), y: a, width: vScale(hi) - vScale(lo), height: pos.size }
441
+ : { x: a, y: vScale(hi), width: pos.size, height: vScale(lo) - vScale(hi) };
442
+ return { box, datum: d, index: i, fill: typeof d.fill === 'string' ? d.fill : s.fill };
443
+ });
444
+ return svg`<g class=${cn('ui-chart-layer ui-chart-bar', s.class)}><g class="ui-chart-layer ui-chart-bar-rectangles">${boxes.map(({ box, datum, index }) => {
445
+ const fill = typeof datum.fill === 'string' ? datum.fill : s.fill ?? '#808080';
446
+ const active = s.activeIndex === index ? s.activeBar ?? {} : null;
447
+ const shape = s.shape
448
+ ? s.shape({ ...box, radius: s.radius ?? 0, fill, datum, index, dataKey: s.dataKey, stroke: s.stroke, strokeWidth: s.strokeWidth, fillOpacity: s.fillOpacity })
449
+ : svg`<path class="ui-chart-rectangle" fill=${active?.fill ?? fill} fill-opacity=${active?.fillOpacity ?? s.fillOpacity ?? nothing} stroke=${active?.stroke ?? s.stroke ?? nothing} stroke-width=${active?.strokeWidth ?? s.strokeWidth ?? nothing} stroke-dasharray=${active?.strokeDasharray ?? nothing} stroke-dashoffset=${active?.strokeDashoffset ?? nothing} d=${rectanglePath(box.x, box.y, box.width, box.height, s.radius ?? 0)}></path>`;
450
+ return svg`<g class="ui-chart-layer ui-chart-bar-rectangle" data-index=${String(index)}>${shape}</g>`;
451
+ })}</g>${labelList(s.labels, s.dataKey, boxes, 'center')}</g>`;
452
+ }
453
+ const top = data.map((_, i): Pt => (vertical ? [vScale(sb[i][1]), catAt(i)] : [catAt(i), vScale(sb[i][1])]));
454
+ const curve = s.type ?? 'linear';
455
+ const stroke = s.stroke ?? '#3182bd';
456
+ const pts = top.map((p, i) => ({ box: { x: p[0], y: p[1], width: 0, height: 0 }, datum: data[i], index: i }));
457
+ const dot = dotProps(s.dot, family === 'line');
458
+ const dots = dot
459
+ ? svg`<g class="ui-chart-layer ui-chart-dots">${top.map((p, i) => drawDot(dot, { cx: p[0], cy: p[1], r: 3, fill: family === 'line' ? '#fff' : s.fill ?? stroke, stroke, strokeWidth: s.strokeWidth ?? 1 }, data[i], i, s.dataKey, family === 'line' ? 'ui-chart-line-dot' : 'ui-chart-area-dot'))}</g>`
460
+ : '';
461
+ if (family === 'line') {
462
+ return svg`<g class=${cn('ui-chart-layer ui-chart-line', s.class)}><path class="ui-chart-curve ui-chart-line-curve" fill="none" stroke=${stroke} stroke-width=${s.strokeWidth ?? 1} stroke-dasharray=${s.strokeDasharray ?? nothing} d=${curvePath(top, curve)}></path>${dots}${labelList(s.labels, s.dataKey, pts, 'top')}</g>`;
463
+ }
464
+ const bottom = data.map((_, i): Pt => {
465
+ const lo = s.stackId != null ? sb[i][0] : Math.max(domain[0], Math.min(0, domain[1]));
466
+ return vertical ? [vScale(lo), catAt(i)] : [catAt(i), vScale(lo)];
467
+ }).reverse();
468
+ const area = `${curvePath(top, curve)}${curvePath(bottom, curve, 'L')}Z`;
469
+ return svg`<g class=${cn('ui-chart-layer ui-chart-area', s.class)}><path class="ui-chart-curve ui-chart-area-area" fill=${s.fill ?? '#3182bd'} fill-opacity=${s.fillOpacity ?? 0.6} stroke="none" d=${area}></path><path class="ui-chart-curve ui-chart-area-curve" fill="none" stroke=${stroke} stroke-width=${s.strokeWidth ?? 1} stroke-dasharray=${s.strokeDasharray ?? nothing} d=${curvePath(top, curve)}></path>${dots}${labelList(s.labels, s.dataKey, pts, 'top')}</g>`;
470
+ });
471
+
472
+ // Active dots per point (lines and areas).
473
+ const actives = family === 'bar' || !o.tooltip
474
+ ? []
475
+ : data.map((_, i) => series.map((s) => {
476
+ if (s.activeDot === false) return '';
477
+ const sb = bands[s.dataKey];
478
+ const [cx, cy] = vertical ? [vScale(sb[i][1]), catAt(i)] : [catAt(i), vScale(sb[i][1])];
479
+ const spec = typeof s.activeDot === 'object' ? s.activeDot : {};
480
+ return drawDot(spec, { cx, cy, r: 4, fill: s.stroke ?? s.fill ?? '#3182bd', stroke: '#fff', strokeWidth: 2 }, data[i], i, s.dataKey, 'ui-chart-active-dot');
481
+ }));
482
+
483
+ // Axes.
484
+ const axis = (which: 'x' | 'y') => {
485
+ const spec = which === 'x' ? xSpec : ySpec;
486
+ if (!spec || spec.hide) return '';
487
+ const isCat = (which === 'x') !== vertical;
488
+ const fmtTick = (v: unknown, i: number) => (spec.tickFormatter ? spec.tickFormatter(v, i) : v);
489
+ const ticks: Tick[] = isCat
490
+ ? categories.map((v, i) => ({ value: v, label: fmtTick(v, i), coord: catAt(i) }))
491
+ : valueTicks.map((v, i) => ({ value: v, label: fmtTick(v, i), coord: vScale(v) }));
492
+ const size = (t: Tick) => (which === 'x' ? estimateTextWidth(String(t.label ?? '')) : 14);
493
+ const interval = spec.interval ?? 'preserveEnd';
494
+ const minTickGap = spec.minTickGap ?? 5;
495
+ // Every tick is served; the ones that do not fit are hidden. The server
496
+ // can only estimate a label's width, so the element measures the real
497
+ // text and thins the ticks again (see `_fitTicks`).
498
+ const shown = new Map(visibleTicks(ticks, size, 0, which === 'x' ? W : H, minTickGap, interval).map((t) => [t.coord, t.at ?? t.coord]));
499
+ const all = spec.tick === false ? [] : ticks;
500
+ const tickSize = spec.tickSize ?? 6;
501
+ const margin2 = spec.tickMargin ?? 2;
502
+ const edge = which === 'x' ? plot.y + plot.h : plot.x;
503
+ const line = spec.axisLine === false ? '' : which === 'x'
504
+ ? svg`<line class="ui-chart-cartesian-axis-line" stroke="#666" fill="none" x1=${fmt(plot.x)} y1=${fmt(edge)} x2=${fmt(plot.x + plot.w)} y2=${fmt(edge)}></line>`
505
+ : svg`<line class="ui-chart-cartesian-axis-line" stroke="#666" fill="none" x1=${fmt(edge)} y1=${fmt(plot.y)} x2=${fmt(edge)} y2=${fmt(plot.y + plot.h)}></line>`;
506
+ return svg`<g class=${cn('ui-chart-layer ui-chart-cartesian-axis', which === 'x' ? 'ui-chart-xAxis' : 'ui-chart-yAxis', spec.class)} data-axis=${which} data-gap=${String(minTickGap)} data-interval=${String(interval)} data-extent=${fmt(which === 'x' ? W : H)}>${line}<g class="ui-chart-cartesian-axis-ticks">${all.map((t) => {
507
+ const at = shown.get(t.coord) ?? t.coord;
508
+ const tl = spec.tickLine === false ? '' : which === 'x'
509
+ ? svg`<line class="ui-chart-cartesian-axis-tick-line" stroke="#666" fill="none" x1=${fmt(at)} y1=${fmt(edge + tickSize)} x2=${fmt(at)} y2=${fmt(edge)}></line>`
510
+ : svg`<line class="ui-chart-cartesian-axis-tick-line" stroke="#666" fill="none" x1=${fmt(edge - tickSize)} y1=${fmt(at)} x2=${fmt(edge)} y2=${fmt(at)}></line>`;
511
+ const text = which === 'x'
512
+ ? chartText(at, edge + tickSize + margin2, t.label, { anchor: 'middle', vAnchor: 'start', fill: '#666', class: 'ui-chart-cartesian-axis-tick-value' })
513
+ : chartText(edge - tickSize - margin2, at, t.label, { anchor: 'end', vAnchor: 'middle', fill: '#666', class: 'ui-chart-cartesian-axis-tick-value' });
514
+ return svg`<g class="ui-chart-layer ui-chart-cartesian-axis-tick" data-coord=${fmt(t.coord)} visibility=${shown.has(t.coord) ? nothing : 'hidden'}>${tl}${text}</g>`;
515
+ })}</g></g>`;
516
+ };
517
+
518
+ // Tooltip points: the series at each row, anchored where Recharts puts a
519
+ // tooltip it opens without a pointer (combineCoordinateForDefaultIndex):
520
+ // on the category, halfway between the plot's near edge and the chart's far one.
521
+ const points: ChartPoint[] = data.map((d, i) => ({
522
+ label: categories[i],
523
+ payload: series.map((s) => ({ dataKey: s.dataKey, name: s.name ?? s.dataKey, value: d[s.dataKey], color: color(s), payload: d })),
524
+ x: vertical ? (plot.x + W) / 2 : catAt(i),
525
+ y: vertical ? catAt(i) : (plot.y + H) / 2,
526
+ }));
527
+
528
+ const body = svg`${grid}${overlay('chart-cursor', cursors, defaultIndex)}${marks}${axis('x')}${axis('y')}${actives.length ? overlay('chart-active', actives, defaultIndex) : ''}`;
529
+ return frame(o, {
530
+ width: W, height: H, hit: vertical ? 'y' : 'x', body, points, margin, plot, fit: true,
531
+ legend: series.map((s) => ({ dataKey: s.dataKey, name: s.name ?? s.dataKey, value: s.name ?? s.dataKey, color: color(s), payload: {} })),
532
+ });
533
+ }
534
+
535
+ /** A bar chart: Recharts' `<BarChart>` with its `<Bar>`s. */
536
+ export function barChart(o: CartesianChartOptions & { bars: ChartSeriesOptions[] }) {
537
+ return cartesian(o, 'bar', o.bars);
538
+ }
539
+
540
+ /** A line chart: Recharts' `<LineChart>` with its `<Line>`s. */
541
+ export function lineChart(o: CartesianChartOptions & { lines: ChartSeriesOptions[] }) {
542
+ return cartesian(o, 'line', o.lines);
543
+ }
544
+
545
+ /** An area chart: Recharts' `<AreaChart>` with its `<Area>`s. */
546
+ export function areaChart(o: CartesianChartOptions & { areas: ChartSeriesOptions[] }) {
547
+ return cartesian(o, 'area', o.areas);
548
+ }
549
+
550
+ // --------------------------------------------------------------------------
551
+ // Polar charts: pie, radar, radial bar
552
+ // --------------------------------------------------------------------------
553
+
554
+ /** Where a polar chart sits: its centre and radii, what a `center` label receives. */
555
+ export interface ChartPolarBox { cx: number; cy: number; innerRadius: number; outerRadius: number }
556
+
557
+ export interface ChartSectorProps extends SectorGeometry {
558
+ fill: string;
559
+ stroke: string;
560
+ strokeWidth: number;
561
+ datum: ChartDatum;
562
+ index: number;
563
+ value: number;
564
+ midAngle: number;
565
+ }
566
+
567
+ /** A sector as the kit draws one, for a custom `shape` / `activeShape` to reuse. */
568
+ export function chartSector(p: Partial<ChartSectorProps> & SectorGeometry, cls = '') {
569
+ return svg`<path class=${cn('ui-chart-sector', cls)} fill=${p.fill ?? nothing} stroke=${p.stroke ?? nothing} stroke-width=${p.strokeWidth ?? nothing} d=${sectorPath(p)}></path>`;
570
+ }
571
+
572
+ export interface ChartPieOptions {
573
+ dataKey: string;
574
+ nameKey?: string;
575
+ /** This ring's own rows (two rings of a stacked pie), else the chart's `data`. */
576
+ data?: ChartDatum[];
577
+ innerRadius?: number | string;
578
+ outerRadius?: number | string;
579
+ cx?: number | string;
580
+ cy?: number | string;
581
+ startAngle?: number;
582
+ endAngle?: number;
583
+ paddingAngle?: number;
584
+ cornerRadius?: number;
585
+ fill?: string;
586
+ stroke?: string;
587
+ strokeWidth?: number;
588
+ /** Slice labels outside the pie: true, options, or a render function. */
589
+ label?: boolean | { offsetRadius?: number; class?: string; fill?: string; fontSize?: number }
590
+ | ((props: { x: number; y: number; cx: number; cy: number; midAngle: number; innerRadius: number; outerRadius: number; value: unknown; datum: ChartDatum; index: number; fill: string; textAnchor: 'start' | 'middle' | 'end' }) => unknown);
591
+ labelLine?: boolean;
592
+ /** LabelLists on the slices (`position` `inside` by default, or `outside`). */
593
+ labels?: ChartLabelOptions[];
594
+ activeIndex?: number;
595
+ /** How the active slice draws: a render function, or a larger outer radius. */
596
+ activeShape?: ((props: ChartSectorProps) => unknown) | { outerRadius?: number };
597
+ shape?: (props: ChartSectorProps) => unknown;
598
+ /** Content at the centre (a donut's total), shadcn's `<Label content>`. */
599
+ center?: (box: ChartPolarBox) => unknown;
600
+ class?: string;
601
+ }
602
+
603
+ function polarPlot(o: ChartBaseOptions, W: number, H: number) {
604
+ const m = resolveMargin(o.margin);
605
+ const legendH = o.legend ? o.legend.height ?? 28 : 0;
606
+ const top = m.top + (o.legend && o.legend.verticalAlign === 'top' ? legendH : 0);
607
+ return { m, x: m.left, y: top, w: W - m.left - m.right, h: H - m.top - m.bottom - legendH };
608
+ }
609
+
610
+ function anchorOf(x: number, cx: number): 'start' | 'middle' | 'end' {
611
+ return x > cx ? 'start' : x < cx ? 'end' : 'middle';
612
+ }
613
+
614
+ /** A pie or donut chart: Recharts' `<PieChart>` with its `<Pie>`s. */
615
+ export function pieChart(o: ChartBaseOptions & { pies: ChartPieOptions[] }) {
616
+ const W = o.width ?? 250;
617
+ const H = o.height ?? W;
618
+ const plot = polarPlot(o, W, H);
619
+ const maxR = Math.min(plot.w, plot.h) / 2;
620
+ const points: ChartPoint[] = [];
621
+ const legend: ChartPayloadItem[] = [];
622
+ const rings = o.pies.map((p) => {
623
+ const rows = p.data ?? o.data;
624
+ const cx = plot.x + percent(p.cx, plot.w, plot.w / 2);
625
+ const cy = plot.y + percent(p.cy, plot.h, plot.h / 2);
626
+ const innerRadius = percent(p.innerRadius, maxR, 0);
627
+ const outerRadius = percent(p.outerRadius, maxR, maxR * 0.8);
628
+ const start = p.startAngle ?? 0;
629
+ const end = p.endAngle ?? 360;
630
+ const delta = Math.sign(end - start) * Math.min(Math.abs(end - start), 360);
631
+ const values = rows.map((d) => num(d[p.dataKey]));
632
+ const nonZero = values.filter((v) => v !== 0).length;
633
+ const padding = rows.length <= 1 ? 0 : p.paddingAngle ?? 0;
634
+ const real = Math.abs(delta) - (Math.abs(delta) >= 360 ? nonZero : nonZero - 1) * padding;
635
+ const sum = values.reduce((s, v) => s + v, 0);
636
+ let prevEnd = start;
637
+ const sectors = rows.map((d, i) => {
638
+ const v = values[i];
639
+ const s0 = i ? prevEnd + Math.sign(delta) * padding * (v !== 0 ? 1 : 0) : start;
640
+ const s1 = s0 + Math.sign(delta) * (sum > 0 ? (v / sum) * real : 0);
641
+ prevEnd = s1;
642
+ const fill = typeof d.fill === 'string' ? d.fill : p.fill ?? '#808080';
643
+ return { cx, cy, innerRadius, outerRadius, startAngle: s0, endAngle: s1, cornerRadius: p.cornerRadius, fill, stroke: p.stroke ?? '#fff', strokeWidth: p.strokeWidth ?? 1, datum: d, index: i, value: v, midAngle: (s0 + s1) / 2 };
644
+ });
645
+ const base = points.length;
646
+ sectors.forEach((s) => {
647
+ const name = String(p.nameKey ? s.datum[p.nameKey] ?? s.index : s.index);
648
+ const item = { dataKey: p.dataKey, name, value: s.datum[p.dataKey], color: s.fill, payload: s.datum };
649
+ const at = polarToCartesian(s.cx, s.cy, (s.innerRadius + s.outerRadius) / 2, s.midAngle);
650
+ points.push({ label: name, payload: [item], x: at.x, y: at.y });
651
+ if (p === o.pies[0]) legend.push({ ...item, value: name });
652
+ });
653
+ const drawn = sectors.map((s) => {
654
+ const isActive = p.activeIndex === s.index && p.activeShape;
655
+ let shape: unknown;
656
+ if (isActive && typeof p.activeShape === 'function') shape = p.activeShape(s);
657
+ else if (isActive && typeof p.activeShape === 'object') shape = chartSector({ ...s, outerRadius: p.activeShape.outerRadius ?? s.outerRadius });
658
+ else shape = p.shape ? p.shape(s) : chartSector(s);
659
+ return svg`<g class="ui-chart-layer ui-chart-pie-sector" data-index=${String(base + s.index)}>${shape}</g>`;
660
+ });
661
+ let labels: unknown = '';
662
+ if (p.label) {
663
+ const lo = typeof p.label === 'object' ? p.label : {};
664
+ const offsetR = lo.offsetRadius ?? 20;
665
+ labels = svg`<g class="ui-chart-layer ui-chart-pie-labels">${sectors.map((s) => {
666
+ const e = polarToCartesian(s.cx, s.cy, s.outerRadius + offsetR, s.midAngle);
667
+ const a = polarToCartesian(s.cx, s.cy, s.outerRadius, s.midAngle);
668
+ const anchor = anchorOf(e.x, s.cx);
669
+ const line = p.labelLine === false ? '' : svg`<path class="ui-chart-curve ui-chart-pie-label-line" fill="none" stroke=${s.fill} d=${`M${fmt(a.x, 3)},${fmt(a.y, 3)}L${fmt(e.x, 3)},${fmt(e.y, 3)}`}></path>`;
670
+ const text = typeof p.label === 'function'
671
+ ? p.label({ x: e.x, y: e.y, cx: s.cx, cy: s.cy, midAngle: s.midAngle, innerRadius: s.innerRadius, outerRadius: s.outerRadius, value: s.datum[p.dataKey], datum: s.datum, index: s.index, fill: s.fill, textAnchor: anchor })
672
+ : chartText(e.x, e.y, s.datum[p.dataKey], { anchor, fill: lo.fill ?? s.fill, stroke: 'none', fontSize: lo.fontSize, baseline: 'middle', class: cn('ui-chart-pie-label-text', lo.class) });
673
+ return svg`<g class="ui-chart-layer">${line}${text}</g>`;
674
+ })}</g>`;
675
+ }
676
+ const lists = (p.labels ?? []).map((l) => svg`<g class="ui-chart-label-list">${sectors.map((s) => {
677
+ const raw = s.datum[l.dataKey ?? p.dataKey];
678
+ const value = l.formatter ? l.formatter(raw, s.datum, s.index) : raw;
679
+ const outside = l.position === 'outside';
680
+ const at = polarToCartesian(s.cx, s.cy, outside ? s.outerRadius + (l.offset ?? 5) : (s.innerRadius + s.outerRadius) / 2, s.midAngle);
681
+ if (l.content) return l.content({ x: at.x, y: at.y, width: 0, height: 0, value, datum: s.datum, index: s.index });
682
+ return chartText(at.x, at.y, value, { anchor: outside ? (at.x >= s.cx ? 'start' : 'end') : 'middle', vAnchor: 'middle', class: cn('ui-chart-label', l.class), fill: l.fill, stroke: l.stroke, fontSize: l.fontSize });
683
+ })}</g>`);
684
+ const center = p.center ? p.center({ cx, cy, innerRadius, outerRadius }) : '';
685
+ return svg`<g class=${cn('ui-chart-layer ui-chart-pie', p.class)}><g class="ui-chart-layer">${drawn}</g>${labels}${lists}${center}</g>`;
686
+ });
687
+ return frame(o, { width: W, height: H, hit: 'target', body: rings, points, legend, margin: plot.m, plot });
688
+ }
689
+
690
+ export interface ChartRadarOptions {
691
+ dataKey: string;
692
+ name?: string;
693
+ fill?: string;
694
+ fillOpacity?: number;
695
+ stroke?: string;
696
+ strokeWidth?: number;
697
+ dot?: boolean | ChartDotOptions | ChartDotRender;
698
+ activeDot?: boolean | ChartDotOptions;
699
+ labels?: ChartLabelOptions[];
700
+ class?: string;
701
+ }
702
+
703
+ export interface ChartPolarGridOptions {
704
+ gridType?: 'polygon' | 'circle';
705
+ radialLines?: boolean;
706
+ /** The ring radii, else the radius axis ticks. */
707
+ polarRadius?: number[];
708
+ class?: string;
709
+ fill?: string;
710
+ stroke?: string;
711
+ strokeWidth?: number;
712
+ }
713
+
714
+ export interface ChartPolarAngleAxisOptions {
715
+ dataKey?: string;
716
+ tickFormatter?: (value: unknown, index: number) => unknown;
717
+ /** false hides the labels; a function draws each one. */
718
+ tick?: boolean | ((props: { x: number; y: number; textAnchor: 'start' | 'middle' | 'end'; value: unknown; label: unknown; index: number; datum: ChartDatum }) => unknown);
719
+ class?: string;
720
+ }
721
+
722
+ export interface ChartPolarRadiusAxisOptions {
723
+ angle?: number;
724
+ orientation?: 'left' | 'right' | 'middle';
725
+ tickCount?: number;
726
+ domain?: [number, number];
727
+ tick?: boolean;
728
+ tickFormatter?: (value: unknown, index: number) => unknown;
729
+ class?: string;
730
+ stroke?: string;
731
+ }
732
+
733
+ function polarGrid(g: ChartPolarGridOptions | false | undefined, cx: number, cy: number, radii: number[], angles: number[], outer: number, inner = 0) {
734
+ if (!g) return '';
735
+ const ring = (r: number) => (g.gridType === 'circle'
736
+ ? svg`<circle class=${cn('ui-chart-polar-grid-concentric-circle', g.class)} stroke=${g.stroke ?? '#ccc'} stroke-width=${g.strokeWidth ?? nothing} fill=${g.fill ?? 'none'} cx=${fmt(cx)} cy=${fmt(cy)} r=${fmt(r)}></circle>`
737
+ : svg`<path class=${cn('ui-chart-polar-grid-concentric-polygon', g.class)} stroke=${g.stroke ?? '#ccc'} stroke-width=${g.strokeWidth ?? nothing} fill=${g.fill ?? 'none'} d=${polygonPath(angles.map((a) => polarToCartesian(cx, cy, r, a)))}></path>`);
738
+ return svg`<g class="ui-chart-polar-grid"><g class="ui-chart-polar-grid-concentric">${radii.map(ring)}</g>${g.radialLines === false ? '' : svg`<g class="ui-chart-polar-grid-angle">${angles.map((a) => {
739
+ const b = polarToCartesian(cx, cy, inner, a);
740
+ const e = polarToCartesian(cx, cy, outer, a);
741
+ return svg`<line class=${cn('ui-chart-polar-grid-angle-line', g.class)} stroke=${g.stroke ?? '#ccc'} x1=${fmt(b.x)} y1=${fmt(b.y)} x2=${fmt(e.x)} y2=${fmt(e.y)}></line>`;
742
+ })}</g>`}</g>`;
743
+ }
744
+
745
+ /** A radar chart: Recharts' `<RadarChart>` with its `<Radar>`s, grid and axes. */
746
+ export function radarChart(o: ChartBaseOptions & {
747
+ radars: ChartRadarOptions[];
748
+ cx?: number | string;
749
+ cy?: number | string;
750
+ innerRadius?: number | string;
751
+ outerRadius?: number | string;
752
+ startAngle?: number;
753
+ endAngle?: number;
754
+ grid?: ChartPolarGridOptions | false;
755
+ angleAxis?: ChartPolarAngleAxisOptions | false;
756
+ radiusAxis?: ChartPolarRadiusAxisOptions | false;
757
+ }) {
758
+ const W = o.width ?? 250;
759
+ const H = o.height ?? W;
760
+ const plot = polarPlot(o, W, H);
761
+ const maxR = Math.min(plot.w, plot.h) / 2;
762
+ // The centre is a share of the whole chart (Recharts' selectPolarViewBox),
763
+ // while the radius comes from the plot inside the margins.
764
+ const cx = percent(o.cx, W, W / 2);
765
+ const cy = percent(o.cy, H, H / 2);
766
+ const inner = percent(o.innerRadius, maxR, 0);
767
+ const outer = percent(o.outerRadius, maxR, maxR * 0.8);
768
+ const data = o.data;
769
+ const n = data.length;
770
+ const start = o.startAngle ?? 90;
771
+ const end = o.endAngle ?? -270;
772
+ const angles = data.map((_, i) => start + (i * (end - start)) / (n || 1));
773
+ const aSpec = o.angleAxis || undefined;
774
+ const rSpec = o.radiusAxis || undefined;
775
+ const all = o.radars.flatMap((r) => data.map((d) => num(d[r.dataKey])));
776
+ const ticks = rSpec?.domain ? niceTicks(rSpec.domain[0], rSpec.domain[1], rSpec.tickCount ?? 5).filter((t) => t >= rSpec.domain![0] && t <= rSpec.domain![1]) : niceTicks(Math.min(0, ...all), Math.max(...all, 0), rSpec?.tickCount ?? 5);
777
+ const domain: [number, number] = rSpec?.domain ?? [ticks[0], ticks[ticks.length - 1]];
778
+ const rScale = linearScale(domain, [inner, outer]);
779
+ const defaultIndex = o.tooltip ? o.tooltip.defaultIndex : undefined;
780
+ const at = (r: ChartRadarOptions, i: number) => polarToCartesian(cx, cy, rScale(num(data[i][r.dataKey])), angles[i]);
781
+ const colorOf = (r: ChartRadarOptions) => r.stroke ?? r.fill ?? '#808080';
782
+ const grid = polarGrid(o.grid, cx, cy, o.grid && o.grid.polarRadius ? o.grid.polarRadius : ticks.map(rScale), angles, outer);
783
+ const radars = o.radars.map((r) => {
784
+ const pts = data.map((_, i) => at(r, i));
785
+ const dot = dotProps(r.dot, false);
786
+ return svg`<g class=${cn('ui-chart-layer ui-chart-radar', r.class)}><g class="ui-chart-layer ui-chart-radar-polygon"><path class="ui-chart-polygon" fill=${r.fill ?? nothing} fill-opacity=${r.fillOpacity ?? nothing} stroke=${r.stroke ?? nothing} stroke-width=${r.strokeWidth ?? nothing} d=${polygonPath(pts)}></path></g>${dot ? svg`<g class="ui-chart-layer ui-chart-radar-dots">${pts.map((q, i) => drawDot(dot, { cx: q.x, cy: q.y, r: 3, fill: r.fill ?? '#808080', stroke: r.stroke ?? 'none', strokeWidth: r.strokeWidth ?? 1 }, data[i], i, r.dataKey, 'ui-chart-radar-dot'))}</g>` : ''}${labelList(r.labels, r.dataKey, pts.map((q, i) => ({ box: { x: q.x, y: q.y, width: 0, height: 0 }, datum: data[i], index: i })), 'top')}</g>`;
787
+ });
788
+ const angleAxis = aSpec ? svg`<g class=${cn('ui-chart-layer ui-chart-polar-angle-axis', aSpec.class)}>${aSpec.tick === false ? '' : data.map((d, i) => {
789
+ const value = aSpec.dataKey ? d[aSpec.dataKey] : i;
790
+ const label = aSpec.tickFormatter ? aSpec.tickFormatter(value, i) : value;
791
+ const p = polarToCartesian(cx, cy, outer + 8, angles[i]);
792
+ const cos = Math.cos(-RADIAN * angles[i]);
793
+ const anchor = cos > 1e-5 ? 'start' : cos < -1e-5 ? 'end' : 'middle';
794
+ if (typeof aSpec.tick === 'function') return aSpec.tick({ x: p.x, y: p.y, textAnchor: anchor, value, label, index: i, datum: d });
795
+ const sin = Math.sin(-RADIAN * angles[i]);
796
+ const vAnchor: VAnchor = anchor !== 'middle' ? 'middle' : sin < 0 ? 'end' : 'start';
797
+ return svg`<g class="ui-chart-layer ui-chart-polar-angle-axis-tick">${chartText(p.x, p.y, label, { anchor, vAnchor, fill: '#808080', class: 'ui-chart-polar-angle-axis-tick-value' })}</g>`;
798
+ })}</g>` : '';
799
+ const radiusAxis = rSpec && rSpec.tick !== false ? svg`<g class=${cn('ui-chart-layer ui-chart-polar-radius-axis', rSpec.class)}>${ticks.map((t, i) => {
800
+ const p = polarToCartesian(cx, cy, rScale(t), rSpec.angle ?? 0);
801
+ const anchor = rSpec.orientation === 'left' ? 'end' : rSpec.orientation === 'middle' ? 'middle' : 'start';
802
+ return chartText(p.x, p.y, rSpec.tickFormatter ? rSpec.tickFormatter(t, i) : t, { anchor, vAnchor: 'middle', fill: rSpec.stroke ?? '#808080', class: 'ui-chart-polar-radius-axis-tick-value' });
803
+ })}</g>` : '';
804
+ const cursors = o.tooltip && o.tooltip.cursor !== false
805
+ ? angles.map((a) => { const e = polarToCartesian(cx, cy, outer, a); return svg`<path class="ui-chart-curve ui-chart-tooltip-cursor" stroke="#ccc" fill="none" d=${`M${fmt(cx)},${fmt(cy)}L${fmt(e.x)},${fmt(e.y)}`}></path>`; })
806
+ : [];
807
+ const actives = o.tooltip
808
+ ? data.map((_, i) => o.radars.map((r) => {
809
+ if (r.activeDot === false) return '';
810
+ const q = at(r, i);
811
+ return drawDot(typeof r.activeDot === 'object' ? r.activeDot : {}, { cx: q.x, cy: q.y, r: 4, fill: colorOf(r), stroke: '#fff', strokeWidth: 2 }, data[i], i, r.dataKey, 'ui-chart-active-dot');
812
+ }))
813
+ : [];
814
+ // Hit wedges: each point owns the angles nearer to it than to its neighbours.
815
+ const step = n ? (end - start) / n : 0;
816
+ const hits = o.tooltip ? svg`<g data-slot="chart-hit">${angles.map((a, i) => svg`<path data-index=${String(i)} fill="transparent" d=${sectorPath({ cx, cy, innerRadius: 0, outerRadius: outer + 8, startAngle: a - step / 2, endAngle: a + step / 2 })}></path>`)}</g>` : '';
817
+ const points: ChartPoint[] = data.map((d, i) => {
818
+ const q = o.radars.length ? at(o.radars[0], i) : { x: cx, y: cy };
819
+ return { label: aSpec && aSpec.dataKey ? d[aSpec.dataKey] : i, payload: o.radars.map((r) => ({ dataKey: r.dataKey, name: r.name ?? r.dataKey, value: d[r.dataKey], color: colorOf(r), payload: d })), x: q.x, y: q.y };
820
+ });
821
+ const body = svg`${grid}${overlay('chart-cursor', cursors, defaultIndex)}${radars}${angleAxis}${radiusAxis}${actives.length ? overlay('chart-active', actives, defaultIndex) : ''}${hits}`;
822
+ return frame(o, { width: W, height: H, hit: 'target', body, points, margin: plot.m, plot, legend: o.radars.map((r) => ({ dataKey: r.dataKey, name: r.name ?? r.dataKey, value: r.name ?? r.dataKey, color: colorOf(r), payload: {} })) });
823
+ }
824
+
825
+ export interface ChartRadialBarOptions {
826
+ dataKey: string;
827
+ name?: string;
828
+ fill?: string;
829
+ stackId?: string;
830
+ cornerRadius?: number;
831
+ /** The track behind each bar: true, or its fill and classes. */
832
+ background?: boolean | { fill?: string; class?: string };
833
+ /** LabelLists along the bars (`insideStart`, `insideEnd`, `end`, or a cartesian position at the bar's middle). */
834
+ labels?: ChartLabelOptions[];
835
+ class?: string;
836
+ }
837
+
838
+ let radialLabelSeq = 0;
839
+
840
+ /** A radial bar chart: Recharts' `<RadialBarChart>` with its `<RadialBar>`s. */
841
+ export function radialBarChart(o: ChartBaseOptions & {
842
+ bars: ChartRadialBarOptions[];
843
+ cx?: number | string;
844
+ cy?: number | string;
845
+ innerRadius?: number | string;
846
+ outerRadius?: number | string;
847
+ startAngle?: number;
848
+ endAngle?: number;
849
+ barSize?: number;
850
+ barGap?: number | string;
851
+ barCategoryGap?: number | string;
852
+ /** The value axis's extent, default `[0, largest value]`. */
853
+ domain?: [number, number];
854
+ grid?: ChartPolarGridOptions | false;
855
+ center?: (box: ChartPolarBox) => unknown;
856
+ }) {
857
+ const W = o.width ?? 250;
858
+ const H = o.height ?? W;
859
+ const plot = polarPlot(o, W, H);
860
+ const maxR = Math.min(plot.w, plot.h) / 2;
861
+ // The centre is a share of the whole chart (Recharts' selectPolarViewBox),
862
+ // while the radius comes from the plot inside the margins.
863
+ const cx = percent(o.cx, W, W / 2);
864
+ const cy = percent(o.cy, H, H / 2);
865
+ const inner = percent(o.innerRadius, maxR, 0);
866
+ const outer = percent(o.outerRadius, maxR, maxR * 0.8);
867
+ const start = o.startAngle ?? 0;
868
+ const end = o.endAngle ?? 360;
869
+ const data = o.data;
870
+ const stacks = new Map<string, string[]>();
871
+ for (const s of o.bars) if (s.stackId != null) stacks.set(s.stackId, [...(stacks.get(s.stackId) ?? []), s.dataKey]);
872
+ const bands: Record<string, Array<[number, number]>> = {};
873
+ for (const keys of stacks.values()) Object.assign(bands, stackData(data, keys));
874
+ for (const s of o.bars) if (s.stackId == null) bands[s.dataKey] = data.map((d) => [0, num(d[s.dataKey])]);
875
+ const max = Math.max(0, ...o.bars.flatMap((s) => bands[s.dataKey].map((b) => b[1])));
876
+ const aScale = linearScale(o.domain ?? [0, max], [start, end]);
877
+ const band = bandScale(data.length, inner, outer);
878
+ const slotIds: string[] = [];
879
+ for (const s of o.bars) { const id = s.stackId != null ? `stack:${s.stackId}` : `bar:${s.dataKey}`; if (!slotIds.includes(id)) slotIds.push(id); }
880
+ const positions = barPositions(band.bandwidth, slotIds.map(() => ({ barSize: o.barSize })), o.barGap, o.barCategoryGap);
881
+ const seq = ++radialLabelSeq;
882
+ const points: ChartPoint[] = data.map((d, i) => ({ label: i, payload: [], x: cx, y: cy }));
883
+ const backgrounds: unknown[] = [];
884
+ const bars = o.bars.map((s, si) => {
885
+ const pos = positions[slotIds.indexOf(s.stackId != null ? `stack:${s.stackId}` : `bar:${s.dataKey}`)];
886
+ const sectors = data.map((d, i) => {
887
+ const r0 = band.at(i) + pos.offset;
888
+ const [lo, hi] = bands[s.dataKey][i];
889
+ const fill = typeof d.fill === 'string' ? d.fill : s.fill ?? '#808080';
890
+ const sec = { cx, cy, innerRadius: r0, outerRadius: r0 + pos.size, startAngle: aScale(lo), endAngle: aScale(hi), cornerRadius: s.cornerRadius, fill, datum: d, index: i };
891
+ points[i].payload.push({ dataKey: s.dataKey, name: s.name ?? s.dataKey, value: d[s.dataKey], color: fill, payload: d });
892
+ if (si === 0) {
893
+ const q = polarToCartesian(cx, cy, r0 + pos.size / 2, (sec.startAngle + sec.endAngle) / 2);
894
+ points[i].x = q.x;
895
+ points[i].y = q.y;
896
+ }
897
+ if (s.background) {
898
+ const bg = typeof s.background === 'object' ? s.background : {};
899
+ backgrounds.push(svg`<g class="ui-chart-layer" data-index=${String(i)}><path class=${cn('ui-chart-sector ui-chart-radial-bar-background-sector', bg.class)} fill=${bg.fill ?? '#eee'} d=${sectorPath({ ...sec, startAngle: start, endAngle: end })}></path></g>`);
900
+ }
901
+ return sec;
902
+ });
903
+ const labels = (s.labels ?? []).map((l, li) => svg`<g class="ui-chart-label-list">${sectors.map((sec) => {
904
+ const raw = sec.datum[l.dataKey ?? s.dataKey];
905
+ const value = l.formatter ? l.formatter(raw, sec.datum, sec.index) : raw;
906
+ const pos2 = l.position ?? 'insideStart';
907
+ const offset = l.offset ?? 5;
908
+ const r = (sec.innerRadius + sec.outerRadius) / 2;
909
+ if (pos2 === 'insideStart' || pos2 === 'insideEnd' || pos2 === 'end') {
910
+ const sign = sec.endAngle - sec.startAngle >= 0 ? 1 : -1;
911
+ const angle = pos2 === 'insideStart' ? sec.startAngle + sign * offset : pos2 === 'insideEnd' ? sec.endAngle - sign * offset : sec.endAngle + sign * offset;
912
+ let dir = pos2 === 'insideEnd';
913
+ dir = sign <= 0 ? dir : !dir;
914
+ const p0 = polarToCartesian(cx, cy, r, angle);
915
+ const p1 = polarToCartesian(cx, cy, r, angle + (dir ? 1 : -1) * 359);
916
+ const id = `ui-chart-radial-label-${seq}-${si}-${li}-${sec.index}`;
917
+ return svg`<text dominant-baseline="central" fill=${l.fill ?? nothing} font-size=${l.fontSize != null ? `${fmt(l.fontSize / 12)}em` : nothing} class=${cn('ui-chart-radial-bar-label', l.class)}><defs><path id=${id} d=${`M${fmt(p0.x)},${fmt(p0.y)} A${fmt(r)},${fmt(r)},0,1,${dir ? 0 : 1},${fmt(p1.x)},${fmt(p1.y)}`}></path></defs><textPath href=${`#${id}`}>${value}</textPath></text>`;
918
+ }
919
+ const q = polarToCartesian(cx, cy, r, (sec.startAngle + sec.endAngle) / 2);
920
+ return chartText(q.x, q.y, value, { anchor: 'middle', vAnchor: 'middle', class: cn('ui-chart-label', l.class), fill: l.fill, fontSize: l.fontSize });
921
+ })}</g>`);
922
+ return svg`<g class=${cn('ui-chart-layer ui-chart-radial-bar', s.class)}><g class="ui-chart-layer ui-chart-radial-bar-sectors">${sectors.map((sec) => svg`<g class="ui-chart-layer" data-index=${String(sec.index)}>${chartSector(sec, 'ui-chart-radial-bar-sector')}</g>`)}</g>${labels}</g>`;
923
+ });
924
+ // The rings sit on the bands, the spokes on the value axis's d3 ticks.
925
+ const spokes = linearTicks(...(o.domain ?? [0, max])).map((t) => aScale(t));
926
+ const grid = polarGrid(o.grid, cx, cy, o.grid && o.grid.polarRadius ? o.grid.polarRadius : data.map((_, i) => band.at(i) + band.bandwidth / 2), spokes, outer, inner);
927
+ const center = o.center ? o.center({ cx, cy, innerRadius: inner, outerRadius: outer }) : '';
928
+ return frame(o, {
929
+ width: W, height: H, hit: 'target', body: svg`${grid}<g class="ui-chart-layer ui-chart-radial-bar-background">${backgrounds}</g>${bars}${center}`, points, margin: plot.m, plot,
930
+ legend: data.map((d, i) => ({ dataKey: o.bars[0]?.dataKey ?? 'value', name: String(i), value: String(i), color: typeof d.fill === 'string' ? d.fill : o.bars[0]?.fill ?? '#808080', payload: d })),
931
+ });
932
+ }