@keyfunc/keybricks-ds 1.0.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 (418) hide show
  1. package/DEPLOY.md +204 -0
  2. package/DESIGN.md +269 -0
  3. package/INTEGRATION.md +417 -0
  4. package/SKILL.md +32 -0
  5. package/_adherence.oxlintrc.json +1469 -0
  6. package/_scripts/build-index.mjs +80 -0
  7. package/_scripts/build-tokens.mjs +135 -0
  8. package/assets/icons/icon-index.js +1 -0
  9. package/assets/icons/icon-index.json +1 -0
  10. package/assets/icons/unicons-index.js +1 -0
  11. package/assets/icons/unicons-index.json +1 -0
  12. package/assets/keybricks-logo.png +0 -0
  13. package/components/_cards/accordion.card.html +32 -0
  14. package/components/_cards/action-chip.card.html +22 -0
  15. package/components/_cards/activity.card.html +34 -0
  16. package/components/_cards/alert.card.html +32 -0
  17. package/components/_cards/app-chrome.card.html +36 -0
  18. package/components/_cards/banner.card.html +21 -0
  19. package/components/_cards/brand-visual.card.html +43 -0
  20. package/components/_cards/breadcrumb.card.html +23 -0
  21. package/components/_cards/button-1-base.card.html +39 -0
  22. package/components/_cards/button-2-states.card.html +56 -0
  23. package/components/_cards/button-3-sizes-icons.card.html +32 -0
  24. package/components/_cards/button-4-group.card.html +47 -0
  25. package/components/_cards/canvas-zoom-controls.card.html +19 -0
  26. package/components/_cards/cards-indicators.card.html +59 -0
  27. package/components/_cards/charts-1-sparkline.card.html +55 -0
  28. package/components/_cards/charts-10-gauge.card.html +31 -0
  29. package/components/_cards/charts-11-funnel.card.html +28 -0
  30. package/components/_cards/charts-12-treemap.card.html +23 -0
  31. package/components/_cards/charts-14-radar.card.html +31 -0
  32. package/components/_cards/charts-15-distribution.card.html +32 -0
  33. package/components/_cards/charts-16-pareto.card.html +25 -0
  34. package/components/_cards/charts-17-candlestick.card.html +34 -0
  35. package/components/_cards/charts-18-sankey.card.html +29 -0
  36. package/components/_cards/charts-19-chord.card.html +31 -0
  37. package/components/_cards/charts-2-line.card.html +46 -0
  38. package/components/_cards/charts-20-sunburst.card.html +38 -0
  39. package/components/_cards/charts-21-worldmap.card.html +33 -0
  40. package/components/_cards/charts-22-gantt.card.html +37 -0
  41. package/components/_cards/charts-23-bar3d.card.html +26 -0
  42. package/components/_cards/charts-24-pie3d.card.html +32 -0
  43. package/components/_cards/charts-25-surface3d.card.html +24 -0
  44. package/components/_cards/charts-26-scatter3d.card.html +24 -0
  45. package/components/_cards/charts-27-ribbon3d.card.html +23 -0
  46. package/components/_cards/charts-3-bar.card.html +71 -0
  47. package/components/_cards/charts-4-barlist.card.html +30 -0
  48. package/components/_cards/charts-5-donut.card.html +35 -0
  49. package/components/_cards/charts-6-scatter.card.html +26 -0
  50. package/components/_cards/charts-7-heatmap.card.html +28 -0
  51. package/components/_cards/charts-8-waterfall.card.html +25 -0
  52. package/components/_cards/charts-9-bullet.card.html +31 -0
  53. package/components/_cards/charts-99-fullpage.card.html +194 -0
  54. package/components/_cards/checkbox.card.html +34 -0
  55. package/components/_cards/codeblock.card.html +19 -0
  56. package/components/_cards/combobox.card.html +25 -0
  57. package/components/_cards/context-menu.card.html +21 -0
  58. package/components/_cards/datatable.card.html +48 -0
  59. package/components/_cards/date-picker.card.html +31 -0
  60. package/components/_cards/diffvalue.card.html +21 -0
  61. package/components/_cards/divider.card.html +24 -0
  62. package/components/_cards/drawer.card.html +30 -0
  63. package/components/_cards/dropdown-menu.card.html +38 -0
  64. package/components/_cards/dropzone.card.html +22 -0
  65. package/components/_cards/empty-state.card.html +29 -0
  66. package/components/_cards/filter-bar.card.html +34 -0
  67. package/components/_cards/icon-button.card.html +27 -0
  68. package/components/_cards/icons-1-core.card.html +49 -0
  69. package/components/_cards/icons-2-feather.card.html +39 -0
  70. package/components/_cards/icons-3-unicons.card.html +44 -0
  71. package/components/_cards/input.card.html +25 -0
  72. package/components/_cards/kbd.card.html +20 -0
  73. package/components/_cards/keyvalue.card.html +23 -0
  74. package/components/_cards/listrow.card.html +30 -0
  75. package/components/_cards/loading.card.html +33 -0
  76. package/components/_cards/log-viewer.card.html +23 -0
  77. package/components/_cards/modal.card.html +35 -0
  78. package/components/_cards/multi-select.card.html +26 -0
  79. package/components/_cards/number-stepper.card.html +24 -0
  80. package/components/_cards/pagination.card.html +25 -0
  81. package/components/_cards/people.card.html +46 -0
  82. package/components/_cards/pipeline-node.card.html +76 -0
  83. package/components/_cards/pipeline-palette.card.html +54 -0
  84. package/components/_cards/search.card.html +23 -0
  85. package/components/_cards/select.card.html +23 -0
  86. package/components/_cards/slider.card.html +25 -0
  87. package/components/_cards/smart-table.card.html +37 -0
  88. package/components/_cards/spreadsheet.card.html +31 -0
  89. package/components/_cards/stepper.card.html +25 -0
  90. package/components/_cards/tabs.card.html +25 -0
  91. package/components/_cards/tag.card.html +26 -0
  92. package/components/_cards/textarea.card.html +27 -0
  93. package/components/_cards/timeline.card.html +32 -0
  94. package/components/_cards/toast.card.html +33 -0
  95. package/components/_cards/toggles.card.html +68 -0
  96. package/components/_cards/tooltip.card.html +22 -0
  97. package/components/_cards/tree.card.html +150 -0
  98. package/components/_shared/frame-theme.js +16 -0
  99. package/components/_shared/src-brand.js +1 -0
  100. package/components/_shared/src-buttons.js +1 -0
  101. package/components/_shared/src-charts.js +1 -0
  102. package/components/_shared/src-data.js +1 -0
  103. package/components/_shared/src-display.js +1 -0
  104. package/components/_shared/src-feedback.js +1 -0
  105. package/components/_shared/src-forms.js +1 -0
  106. package/components/_shared/src-icons.js +1 -0
  107. package/components/_shared/src-navigation.js +1 -0
  108. package/components/_shared/src-pipeline.js +1 -0
  109. package/components/_shared/src-tabs.js +1 -0
  110. package/components/_shared/svg-download.js +19 -0
  111. package/components/_shared/view-source.js +185 -0
  112. package/components/_vue/index.mjs +114 -0
  113. package/components/brand/BrandVisual.d.ts +17 -0
  114. package/components/brand/BrandVisual.jsx +29 -0
  115. package/components/brand/BrandVisual.vue +34 -0
  116. package/components/brand/_dir.card.html +13 -0
  117. package/components/brand/brandMotifs.js +210 -0
  118. package/components/buttons/ActionChip.d.ts +10 -0
  119. package/components/buttons/ActionChip.jsx +20 -0
  120. package/components/buttons/ActionChip.vue +18 -0
  121. package/components/buttons/Button.d.ts +40 -0
  122. package/components/buttons/Button.jsx +101 -0
  123. package/components/buttons/Button.vue +87 -0
  124. package/components/buttons/IconButton.d.ts +26 -0
  125. package/components/buttons/IconButton.jsx +28 -0
  126. package/components/buttons/IconButton.vue +21 -0
  127. package/components/buttons/_dir.card.html +13 -0
  128. package/components/buttons/buttons.prompt.md +19 -0
  129. package/components/charts/Bar3D.d.ts +15 -0
  130. package/components/charts/Bar3D.jsx +93 -0
  131. package/components/charts/Bar3D.vue +79 -0
  132. package/components/charts/BarChart.d.ts +23 -0
  133. package/components/charts/BarChart.jsx +135 -0
  134. package/components/charts/BarChart.vue +139 -0
  135. package/components/charts/BarList.d.ts +13 -0
  136. package/components/charts/BarList.jsx +31 -0
  137. package/components/charts/BarList.vue +25 -0
  138. package/components/charts/BoxPlot.d.ts +18 -0
  139. package/components/charts/BoxPlot.jsx +62 -0
  140. package/components/charts/BoxPlot.vue +58 -0
  141. package/components/charts/BulletChart.d.ts +12 -0
  142. package/components/charts/BulletChart.jsx +41 -0
  143. package/components/charts/BulletChart.vue +33 -0
  144. package/components/charts/CandlestickChart.d.ts +15 -0
  145. package/components/charts/CandlestickChart.jsx +63 -0
  146. package/components/charts/CandlestickChart.vue +62 -0
  147. package/components/charts/ChartKit.d.ts +8 -0
  148. package/components/charts/ChartKit.jsx +76 -0
  149. package/components/charts/ChartKit.vue +47 -0
  150. package/components/charts/ChordChart.d.ts +13 -0
  151. package/components/charts/ChordChart.jsx +55 -0
  152. package/components/charts/ChordChart.vue +54 -0
  153. package/components/charts/DonutChart.d.ts +15 -0
  154. package/components/charts/DonutChart.jsx +73 -0
  155. package/components/charts/DonutChart.vue +61 -0
  156. package/components/charts/FunnelChart.d.ts +11 -0
  157. package/components/charts/FunnelChart.jsx +37 -0
  158. package/components/charts/FunnelChart.vue +29 -0
  159. package/components/charts/GanttChart.d.ts +20 -0
  160. package/components/charts/GanttChart.jsx +80 -0
  161. package/components/charts/GanttChart.vue +60 -0
  162. package/components/charts/GaugeChart.d.ts +13 -0
  163. package/components/charts/GaugeChart.jsx +33 -0
  164. package/components/charts/GaugeChart.vue +29 -0
  165. package/components/charts/HeatmapChart.d.ts +13 -0
  166. package/components/charts/HeatmapChart.jsx +62 -0
  167. package/components/charts/HeatmapChart.vue +38 -0
  168. package/components/charts/Histogram.d.ts +16 -0
  169. package/components/charts/Histogram.jsx +56 -0
  170. package/components/charts/Histogram.vue +53 -0
  171. package/components/charts/LineChart.d.ts +17 -0
  172. package/components/charts/LineChart.jsx +84 -0
  173. package/components/charts/LineChart.vue +86 -0
  174. package/components/charts/ParetoChart.d.ts +13 -0
  175. package/components/charts/ParetoChart.jsx +54 -0
  176. package/components/charts/ParetoChart.vue +55 -0
  177. package/components/charts/Pie3D.d.ts +17 -0
  178. package/components/charts/Pie3D.jsx +145 -0
  179. package/components/charts/Pie3D.vue +115 -0
  180. package/components/charts/RadarChart.d.ts +14 -0
  181. package/components/charts/RadarChart.jsx +79 -0
  182. package/components/charts/RadarChart.vue +73 -0
  183. package/components/charts/Ribbon3D.d.ts +13 -0
  184. package/components/charts/Ribbon3D.jsx +89 -0
  185. package/components/charts/Ribbon3D.vue +79 -0
  186. package/components/charts/SankeyChart.d.ts +15 -0
  187. package/components/charts/SankeyChart.jsx +59 -0
  188. package/components/charts/SankeyChart.vue +53 -0
  189. package/components/charts/Scatter3D.d.ts +12 -0
  190. package/components/charts/Scatter3D.jsx +69 -0
  191. package/components/charts/Scatter3D.vue +59 -0
  192. package/components/charts/ScatterChart.d.ts +12 -0
  193. package/components/charts/ScatterChart.jsx +53 -0
  194. package/components/charts/ScatterChart.vue +57 -0
  195. package/components/charts/Sparkline.d.ts +19 -0
  196. package/components/charts/Sparkline.jsx +63 -0
  197. package/components/charts/Sparkline.vue +54 -0
  198. package/components/charts/SunburstChart.d.ts +12 -0
  199. package/components/charts/SunburstChart.jsx +61 -0
  200. package/components/charts/SunburstChart.vue +60 -0
  201. package/components/charts/Surface3D.d.ts +13 -0
  202. package/components/charts/Surface3D.jsx +70 -0
  203. package/components/charts/Surface3D.vue +63 -0
  204. package/components/charts/TreemapChart.d.ts +11 -0
  205. package/components/charts/TreemapChart.jsx +35 -0
  206. package/components/charts/TreemapChart.vue +27 -0
  207. package/components/charts/WaterfallChart.d.ts +11 -0
  208. package/components/charts/WaterfallChart.jsx +59 -0
  209. package/components/charts/WaterfallChart.vue +46 -0
  210. package/components/charts/WorldMap.d.ts +21 -0
  211. package/components/charts/WorldMap.jsx +107 -0
  212. package/components/charts/WorldMap.vue +73 -0
  213. package/components/charts/_dir.card.html +7 -0
  214. package/components/charts/chart-3d.js +84 -0
  215. package/components/charts/chart-geometry.js +223 -0
  216. package/components/charts/charts.prompt.md +24 -0
  217. package/components/data/CodeBlock.d.ts +9 -0
  218. package/components/data/CodeBlock.jsx +19 -0
  219. package/components/data/CodeBlock.vue +22 -0
  220. package/components/data/DataTable.d.ts +28 -0
  221. package/components/data/DataTable.jsx +32 -0
  222. package/components/data/DataTable.vue +25 -0
  223. package/components/data/DiffValue.d.ts +11 -0
  224. package/components/data/DiffValue.jsx +15 -0
  225. package/components/data/DiffValue.vue +13 -0
  226. package/components/data/FilterBar.d.ts +16 -0
  227. package/components/data/FilterBar.jsx +22 -0
  228. package/components/data/FilterBar.vue +23 -0
  229. package/components/data/KeyValue.d.ts +16 -0
  230. package/components/data/KeyValue.jsx +25 -0
  231. package/components/data/KeyValue.vue +26 -0
  232. package/components/data/ListRow.d.ts +14 -0
  233. package/components/data/ListRow.jsx +25 -0
  234. package/components/data/ListRow.vue +25 -0
  235. package/components/data/Pagination.d.ts +12 -0
  236. package/components/data/Pagination.jsx +27 -0
  237. package/components/data/Pagination.vue +24 -0
  238. package/components/data/SmartTable.d.ts +30 -0
  239. package/components/data/SmartTable.jsx +73 -0
  240. package/components/data/SmartTable.vue +74 -0
  241. package/components/data/Spreadsheet.d.ts +33 -0
  242. package/components/data/Spreadsheet.jsx +110 -0
  243. package/components/data/Spreadsheet.vue +95 -0
  244. package/components/data/_dir.card.html +13 -0
  245. package/components/data/data.prompt.md +33 -0
  246. package/components/display/Avatar.d.ts +21 -0
  247. package/components/display/Avatar.jsx +31 -0
  248. package/components/display/Avatar.vue +34 -0
  249. package/components/display/Badge.d.ts +27 -0
  250. package/components/display/Badge.jsx +40 -0
  251. package/components/display/Badge.vue +47 -0
  252. package/components/display/Card.d.ts +27 -0
  253. package/components/display/Card.jsx +27 -0
  254. package/components/display/Card.vue +28 -0
  255. package/components/display/Divider.d.ts +8 -0
  256. package/components/display/Divider.jsx +14 -0
  257. package/components/display/Divider.vue +13 -0
  258. package/components/display/Kbd.d.ts +7 -0
  259. package/components/display/Kbd.jsx +12 -0
  260. package/components/display/Kbd.vue +9 -0
  261. package/components/display/ProgressBar.d.ts +42 -0
  262. package/components/display/ProgressBar.jsx +70 -0
  263. package/components/display/ProgressBar.vue +69 -0
  264. package/components/display/Tag.d.ts +9 -0
  265. package/components/display/Tag.jsx +15 -0
  266. package/components/display/Tag.vue +15 -0
  267. package/components/display/_dir.card.html +13 -0
  268. package/components/display/display.prompt.md +30 -0
  269. package/components/feedback/Accordion.d.ts +13 -0
  270. package/components/feedback/Accordion.jsx +21 -0
  271. package/components/feedback/Accordion.vue +22 -0
  272. package/components/feedback/ActivityItem.d.ts +25 -0
  273. package/components/feedback/ActivityItem.jsx +39 -0
  274. package/components/feedback/ActivityItem.vue +46 -0
  275. package/components/feedback/Alert.d.ts +36 -0
  276. package/components/feedback/Alert.jsx +48 -0
  277. package/components/feedback/Alert.vue +59 -0
  278. package/components/feedback/Banner.d.ts +15 -0
  279. package/components/feedback/Banner.jsx +25 -0
  280. package/components/feedback/Banner.vue +25 -0
  281. package/components/feedback/ContextMenu.d.ts +8 -0
  282. package/components/feedback/ContextMenu.jsx +18 -0
  283. package/components/feedback/ContextMenu.vue +19 -0
  284. package/components/feedback/Drawer.d.ts +18 -0
  285. package/components/feedback/Drawer.jsx +26 -0
  286. package/components/feedback/Drawer.vue +24 -0
  287. package/components/feedback/Menu.d.ts +10 -0
  288. package/components/feedback/Menu.jsx +24 -0
  289. package/components/feedback/Menu.vue +21 -0
  290. package/components/feedback/Modal.d.ts +38 -0
  291. package/components/feedback/Modal.jsx +69 -0
  292. package/components/feedback/Modal.vue +65 -0
  293. package/components/feedback/Stepper.d.ts +9 -0
  294. package/components/feedback/Stepper.jsx +24 -0
  295. package/components/feedback/Stepper.vue +18 -0
  296. package/components/feedback/Timeline.d.ts +15 -0
  297. package/components/feedback/Timeline.jsx +27 -0
  298. package/components/feedback/Timeline.vue +21 -0
  299. package/components/feedback/_dir.card.html +13 -0
  300. package/components/feedback/feedback.prompt.md +35 -0
  301. package/components/forms/Checkbox.d.ts +13 -0
  302. package/components/forms/Checkbox.jsx +17 -0
  303. package/components/forms/Checkbox.vue +18 -0
  304. package/components/forms/Combobox.d.ts +13 -0
  305. package/components/forms/Combobox.jsx +34 -0
  306. package/components/forms/Combobox.vue +35 -0
  307. package/components/forms/DatePicker.d.ts +12 -0
  308. package/components/forms/DatePicker.jsx +48 -0
  309. package/components/forms/DatePicker.vue +46 -0
  310. package/components/forms/Dropzone.d.ts +20 -0
  311. package/components/forms/Dropzone.jsx +36 -0
  312. package/components/forms/Dropzone.vue +28 -0
  313. package/components/forms/Field.d.ts +13 -0
  314. package/components/forms/Field.jsx +13 -0
  315. package/components/forms/Field.vue +12 -0
  316. package/components/forms/Input.d.ts +18 -0
  317. package/components/forms/Input.jsx +35 -0
  318. package/components/forms/Input.vue +38 -0
  319. package/components/forms/MultiSelect.d.ts +14 -0
  320. package/components/forms/MultiSelect.jsx +33 -0
  321. package/components/forms/MultiSelect.vue +36 -0
  322. package/components/forms/NumberStepper.d.ts +13 -0
  323. package/components/forms/NumberStepper.jsx +15 -0
  324. package/components/forms/NumberStepper.vue +17 -0
  325. package/components/forms/Radio.vue +16 -0
  326. package/components/forms/RadioGroup.vue +11 -0
  327. package/components/forms/SearchInput.d.ts +12 -0
  328. package/components/forms/SearchInput.jsx +17 -0
  329. package/components/forms/SearchInput.vue +14 -0
  330. package/components/forms/Select.d.ts +14 -0
  331. package/components/forms/Select.jsx +17 -0
  332. package/components/forms/Select.vue +16 -0
  333. package/components/forms/Slider.d.ts +15 -0
  334. package/components/forms/Slider.jsx +21 -0
  335. package/components/forms/Slider.vue +23 -0
  336. package/components/forms/Switch.d.ts +31 -0
  337. package/components/forms/Switch.jsx +40 -0
  338. package/components/forms/Switch.vue +16 -0
  339. package/components/forms/_dir.card.html +13 -0
  340. package/components/forms/forms.prompt.md +23 -0
  341. package/components/icons/Icon.d.ts +19 -0
  342. package/components/icons/Icon.jsx +93 -0
  343. package/components/icons/Icon.prompt.md +8 -0
  344. package/components/icons/Icon.vue +12 -0
  345. package/components/icons/Unicon.d.ts +12 -0
  346. package/components/icons/Unicon.jsx +16 -0
  347. package/components/icons/Unicon.vue +15 -0
  348. package/components/icons/_dir.card.html +13 -0
  349. package/components/icons/feather-paths.js +2 -0
  350. package/components/icons/icon-paths.js +73 -0
  351. package/components/index.d.ts +87 -0
  352. package/components/index.js +110 -0
  353. package/components/navigation/Breadcrumb.d.ts +24 -0
  354. package/components/navigation/Breadcrumb.jsx +120 -0
  355. package/components/navigation/Breadcrumb.vue +94 -0
  356. package/components/navigation/DocTree.d.ts +18 -0
  357. package/components/navigation/DocTree.jsx +37 -0
  358. package/components/navigation/DocTree.vue +25 -0
  359. package/components/navigation/Sidebar.d.ts +29 -0
  360. package/components/navigation/Sidebar.jsx +75 -0
  361. package/components/navigation/Sidebar.vue +54 -0
  362. package/components/navigation/TopBar.d.ts +27 -0
  363. package/components/navigation/TopBar.jsx +44 -0
  364. package/components/navigation/TopBar.vue +51 -0
  365. package/components/navigation/Tree.d.ts +83 -0
  366. package/components/navigation/Tree.jsx +347 -0
  367. package/components/navigation/Tree.vue +258 -0
  368. package/components/navigation/_dir.card.html +13 -0
  369. package/components/navigation/navigation.prompt.md +22 -0
  370. package/components/pipeline/ConfigPanel.d.ts +18 -0
  371. package/components/pipeline/ConfigPanel.jsx +30 -0
  372. package/components/pipeline/ConfigPanel.vue +32 -0
  373. package/components/pipeline/DataPreview.d.ts +14 -0
  374. package/components/pipeline/DataPreview.jsx +24 -0
  375. package/components/pipeline/DataPreview.vue +21 -0
  376. package/components/pipeline/NodeCard.d.ts +47 -0
  377. package/components/pipeline/NodeCard.jsx +97 -0
  378. package/components/pipeline/NodeCard.vue +75 -0
  379. package/components/pipeline/PaletteItem.vue +15 -0
  380. package/components/pipeline/PaletteListItem.vue +15 -0
  381. package/components/pipeline/PalettePanel.d.ts +20 -0
  382. package/components/pipeline/PalettePanel.jsx +74 -0
  383. package/components/pipeline/PalettePanel.vue +45 -0
  384. package/components/pipeline/PipelineCanvas.d.ts +25 -0
  385. package/components/pipeline/PipelineCanvas.jsx +94 -0
  386. package/components/pipeline/PipelineCanvas.vue +73 -0
  387. package/components/pipeline/ZoomControls.d.ts +9 -0
  388. package/components/pipeline/ZoomControls.jsx +15 -0
  389. package/components/pipeline/ZoomControls.vue +15 -0
  390. package/components/pipeline/_dir.card.html +13 -0
  391. package/components/pipeline/pipeline.prompt.md +22 -0
  392. package/components/tabs/SegmentedControl.d.ts +11 -0
  393. package/components/tabs/SegmentedControl.jsx +21 -0
  394. package/components/tabs/SegmentedControl.vue +15 -0
  395. package/components/tabs/Tabs.d.ts +13 -0
  396. package/components/tabs/Tabs.jsx +52 -0
  397. package/components/tabs/Tabs.vue +39 -0
  398. package/components/tabs/ViewToggle.d.ts +8 -0
  399. package/components/tabs/ViewToggle.jsx +12 -0
  400. package/components/tabs/ViewToggle.vue +12 -0
  401. package/components/tabs/_dir.card.html +13 -0
  402. package/components/tabs/tabs.prompt.md +14 -0
  403. package/package.json +76 -0
  404. package/readme.md +150 -0
  405. package/styles.css +10 -0
  406. package/tokens/alpha.css +27 -0
  407. package/tokens/charts.css +55 -0
  408. package/tokens/colors-extended.css +40 -0
  409. package/tokens/colors.css +39 -0
  410. package/tokens/dark.css +96 -0
  411. package/tokens/fonts.css +4 -0
  412. package/tokens/interaction.css +48 -0
  413. package/tokens/layout.css +30 -0
  414. package/tokens/motion.css +22 -0
  415. package/tokens/tokens.d.ts +680 -0
  416. package/tokens/tokens.js +1018 -0
  417. package/tokens/tokens.json +2928 -0
  418. package/tokens/typography.css +22 -0
@@ -0,0 +1,56 @@
1
+ import React from "react";
2
+ import { fmtNum, niceTicks, scaleLin } from "./chart-geometry.js";
3
+ import { ChartTooltip, PlotGrid, wrapStyle } from "./ChartKit.jsx";
4
+
5
+ const bucketize = (values, bins) => {
6
+ const lo = Math.min(...values), hi = Math.max(...values);
7
+ const w = (hi - lo) / bins || 1;
8
+ const out = [...Array(bins)].map((_, i) => ({ from: lo + i * w, to: lo + (i + 1) * w, n: 0 }));
9
+ values.forEach((v) => { out[Math.max(0, Math.min(bins - 1, Math.floor((v - lo) / w)))].n++; });
10
+ return out;
11
+ };
12
+
13
+ /**
14
+ * Histogram — the distribution of one measure. Pass raw `values` (auto-bucketed into
15
+ * `bins`) or pre-computed `buckets: [{from, to, n}]`. mean/median guides are optional.
16
+ */
17
+ export function Histogram({ values = [], buckets, bins = 12, width = 560, height = 240, axes = true, showMean = false, valueFormat = fmtNum, countFormat = fmtNum, interactive = true, color = "var(--series-1)", style }) {
18
+ const [hover, setHover] = React.useState(null);
19
+ const bkt = buckets || bucketize(values, bins);
20
+ const pad = { l: axes ? 40 : 4, r: 8, t: showMean ? 22 : 10, b: axes ? 26 : 4 };
21
+ const { ticks, lo, hi } = niceTicks(0, Math.max(1, ...bkt.map((b) => b.n)), 4);
22
+ const sy = scaleLin(lo, hi, height - pad.b, pad.t);
23
+ const slot = (width - pad.l - pad.r) / bkt.length;
24
+ const mean = showMean && values.length ? values.reduce((s, v) => s + v, 0) / values.length : null;
25
+ const sxVal = scaleLin(bkt[0].from, bkt[bkt.length - 1].to, pad.l, width - pad.r);
26
+ const move = (e) => {
27
+ if (!interactive) return;
28
+ const r = e.currentTarget.getBoundingClientRect();
29
+ const i = Math.floor((e.clientX - r.left - pad.l) / slot);
30
+ setHover(i >= 0 && i < bkt.length ? i : null);
31
+ };
32
+ const total = bkt.reduce((s, b) => s + b.n, 0) || 1;
33
+ return (
34
+ <div style={{ ...wrapStyle, ...style }}>
35
+ <svg width={width} height={height} onMouseMove={move} onMouseLeave={() => setHover(null)} role="img">
36
+ {axes ? <PlotGrid w={width} h={height} pad={pad} ticks={ticks} sy={sy} xLabels={[]} sxLabel={() => 0} yFormat={countFormat} /> : null}
37
+ {bkt.map((b, i) => (
38
+ <rect key={i} x={pad.l + slot * i + 0.5} y={sy(b.n)} width={Math.max(1, slot - 1.5)} height={Math.max(1, sy(0) - sy(b.n))} rx="2"
39
+ fill={color} opacity={hover == null ? 0.9 : hover === i ? 1 : 0.4} style={{ transition: "opacity var(--dur-fast) var(--ease-out)" }} />
40
+ ))}
41
+ {mean != null ? <g>
42
+ <line x1={sxVal(mean)} x2={sxVal(mean)} y1={pad.t} y2={height - pad.b} stroke="var(--chart-guide)" strokeWidth="1" strokeDasharray="3 3" />
43
+ <text x={Math.min(sxVal(mean) + 4, width - 60)} y={pad.t - 8} style={{ fontFamily: "var(--font-mono)", fontSize: 10, fill: "var(--chart-label)" }}>mean {valueFormat(mean)}</text>
44
+ </g> : null}
45
+ {axes ? [0, Math.floor(bkt.length / 2), bkt.length - 1].map((i) => (
46
+ <text key={i} x={pad.l + slot * i + slot / 2} y={height - pad.b + 16} textAnchor="middle" style={{ fontFamily: "var(--font-mono)", fontSize: 10, fill: "var(--chart-label)" }}>{valueFormat(bkt[i].from)}</text>
47
+ )) : null}
48
+ </svg>
49
+ {hover != null && interactive ? (
50
+ <ChartTooltip x={pad.l + slot * hover + slot / 2} y={height / 2 - 10} flip={pad.l + slot * hover > width * 0.62}
51
+ title={valueFormat(bkt[hover].from) + " – " + valueFormat(bkt[hover].to)}
52
+ rows={[{ label: "count", value: countFormat(bkt[hover].n), color }, { label: "share", value: ((bkt[hover].n / total) * 100).toFixed(1) + "%" }]} />
53
+ ) : null}
54
+ </div>
55
+ );
56
+ }
@@ -0,0 +1,53 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ import { fmtNum, niceTicks, scaleLin } from './chart-geometry.js'
4
+ // Distribution of one measure — raw values are bucketed for you.
5
+ const props = defineProps({ values:{type:Array,default:()=>[]}, buckets:Array, bins:{type:Number,default:12}, width:{type:Number,default:560}, height:{type:Number,default:240}, axes:{type:Boolean,default:true}, showMean:Boolean, valueFormat:{type:Function,default:fmtNum}, countFormat:{type:Function,default:fmtNum}, interactive:{type:Boolean,default:true}, color:{type:String,default:'var(--series-1)'}, style:Object })
6
+ const hover = ref(null)
7
+ const g = computed(() => {
8
+ let bkt = props.buckets
9
+ if (!bkt) {
10
+ const lo = Math.min(...props.values), hi = Math.max(...props.values)
11
+ const w = (hi-lo)/props.bins || 1
12
+ bkt = [...Array(props.bins)].map((_,i)=>({ from: lo+i*w, to: lo+(i+1)*w, n: 0 }))
13
+ props.values.forEach(v=>{ bkt[Math.max(0,Math.min(props.bins-1,Math.floor((v-lo)/w)))].n++ })
14
+ }
15
+ const pad = { l: props.axes?40:4, r:8, t: props.showMean?22:10, b: props.axes?26:4 }
16
+ const { ticks, lo, hi } = niceTicks(0, Math.max(1,...bkt.map(b=>b.n)), 4)
17
+ const sy = scaleLin(lo, hi, props.height-pad.b, pad.t)
18
+ const slot = (props.width-pad.l-pad.r)/bkt.length
19
+ const mean = props.showMean && props.values.length ? props.values.reduce((s,v)=>s+v,0)/props.values.length : null
20
+ const sxVal = scaleLin(bkt[0].from, bkt[bkt.length-1].to, pad.l, props.width-pad.r)
21
+ return { bkt, pad, ticks, sy, slot, mean, meanX: mean!=null ? Math.min(sxVal(mean)+4, props.width-60) : 0, meanLine: mean!=null ? sxVal(mean) : 0,
22
+ total: bkt.reduce((s,b)=>s+b.n,0)||1, labels:[0, Math.floor(bkt.length/2), bkt.length-1] }
23
+ })
24
+ function move(e){
25
+ if(!props.interactive) return
26
+ const r = e.currentTarget.getBoundingClientRect()
27
+ const i = Math.floor((e.clientX-r.left-g.value.pad.l)/g.value.slot)
28
+ hover.value = i>=0 && i<g.value.bkt.length ? i : null
29
+ }
30
+ </script>
31
+ <template>
32
+ <div :style="{position:'relative',display:'inline-block',lineHeight:0,...style}">
33
+ <svg :width="width" :height="height" @mousemove="move" @mouseleave="hover=null" role="img">
34
+ <template v-if="axes">
35
+ <g v-for="t in g.ticks" :key="t">
36
+ <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sy(t)" :y2="g.sy(t)" :stroke="t===0?'var(--chart-axis)':'var(--chart-grid)'" stroke-width="1"/>
37
+ <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">{{ countFormat(t) }}</text>
38
+ </g>
39
+ </template>
40
+ <rect v-for="(b,i) in g.bkt" :key="i" :x="g.pad.l+g.slot*i+0.5" :y="g.sy(b.n)" :width="Math.max(1,g.slot-1.5)" :height="Math.max(1,g.sy(0)-g.sy(b.n))" rx="2"
41
+ :fill="color" :opacity="hover==null?0.9:(hover===i?1:0.4)" style="transition:opacity var(--dur-fast) var(--ease-out)"/>
42
+ <template v-if="g.mean!=null">
43
+ <line :x1="g.meanLine" :x2="g.meanLine" :y1="g.pad.t" :y2="height-g.pad.b" stroke="var(--chart-guide)" stroke-width="1" stroke-dasharray="3 3"/>
44
+ <text :x="g.meanX" :y="g.pad.t-8" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">mean {{ valueFormat(g.mean) }}</text>
45
+ </template>
46
+ <text v-if="axes" v-for="i in g.labels" :key="'l'+i" :x="g.pad.l+g.slot*i+g.slot/2" :y="height-g.pad.b+16" text-anchor="middle" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">{{ valueFormat(g.bkt[i].from) }}</text>
47
+ </svg>
48
+ <div v-if="hover!=null && interactive" :style="{position:'absolute',left:(g.pad.l+g.slot*hover+g.slot/2)+'px',top:(height/2-10)+'px',transform:(g.pad.l+g.slot*hover)>width*0.62?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
49
+ <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ valueFormat(g.bkt[hover].from) }} – {{ valueFormat(g.bkt[hover].to) }}</div>
50
+ <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ countFormat(g.bkt[hover].n) }} · {{ ((g.bkt[hover].n/g.total)*100).toFixed(1) }}%</div>
51
+ </div>
52
+ </div>
53
+ </template>
@@ -0,0 +1,17 @@
1
+ import * as React from "react";
2
+ export interface LineSeries { name: string; values: number[]; color?: string; }
3
+ /** Line / area / stacked-area over shared x labels. Hover guideline + tooltip; legend click mutes a series. */
4
+ export interface LineChartProps {
5
+ series?: LineSeries[];
6
+ xLabels?: string[];
7
+ width?: number; height?: number;
8
+ /** Fill under each line */ area?: boolean;
9
+ /** Stack series (implies area). Bands fill to the baseline so the gradients overlap. */ stacked?: boolean;
10
+ /** Line shape: straight, monotone-cubic smooth, or stepped */ curve?: "linear" | "smooth" | "step";
11
+ /** false = sm tile tier: no grid, no tick labels */ axes?: boolean;
12
+ legend?: boolean; interactive?: boolean;
13
+ yFormat?: (v: number) => string;
14
+ /** Dashed reference line */ target?: number; targetLabel?: string;
15
+ style?: React.CSSProperties;
16
+ }
17
+ export declare function LineChart(props: LineChartProps): JSX.Element;
@@ -0,0 +1,84 @@
1
+ import React from "react";
2
+ import { fmtNum, extent, scaleLin, shapePath, closeToBaseline, stack, niceTicks, seriesColor } from "./chart-geometry.js";
3
+ import { ChartLegend, ChartTooltip, PlotGrid, thinLabels, wrapStyle } from "./ChartKit.jsx";
4
+
5
+ /**
6
+ * Line / area / stacked-area chart. series: [{name, values, color?}] over shared xLabels.
7
+ * curve: "linear" (default) | "smooth" (monotone cubic, never overshoots a real value) | "step".
8
+ * Interactive by default at every size: hover guideline + tooltip, click legend to mute a series.
9
+ * sm tier: axes={false} legend={false} for a tile-sized trend — the tooltip stays.
10
+ */
11
+ export function LineChart({ series = [], xLabels = [], width = 560, height = 240, area = false, stacked = false, curve = "linear", axes = true, legend = true, interactive = true, yFormat = fmtNum, target, targetLabel, style }) {
12
+ const [hidden, setHidden] = React.useState([]);
13
+ const [hover, setHover] = React.useState(null);
14
+ const shown = series.filter((s) => !hidden.includes(s.name));
15
+ const pad = { l: axes ? 44 : 4, r: 8, t: 10, b: axes ? 24 : 4 };
16
+ const vals = shown.map((s) => s.values);
17
+ const bands = stacked && shown.length ? stack(vals) : null;
18
+ const [lo0, hi0] = stacked ? [0, Math.max(1, ...(bands[bands.length - 1] || []).map((b) => b[1]))] : extent(vals.concat(target != null ? [[target]] : []));
19
+ const { ticks, lo, hi } = niceTicks(lo0, hi0, 4);
20
+ const n = Math.max(...series.map((s) => s.values.length), 2);
21
+ const sx = scaleLin(0, n - 1, pad.l + 4, width - pad.r - 4);
22
+ const sy = scaleLin(lo, hi, height - pad.b, pad.t);
23
+ const colorOf = (s) => s.color || seriesColor(series.indexOf(s));
24
+ const base = sy(Math.max(lo, 0));
25
+ const move = (e) => {
26
+ if (!interactive) return;
27
+ const r = e.currentTarget.getBoundingClientRect();
28
+ const x = e.clientX - r.left;
29
+ setHover(Math.max(0, Math.min(n - 1, Math.round(((x - pad.l - 4) / (width - pad.l - pad.r - 8)) * (n - 1)))));
30
+ };
31
+ const gid = React.useId ? React.useId().replace(/:/g, "") : "kbline";
32
+ /* Stacked bands are closed against the band below, not the baseline: overlapping
33
+ translucent fills stack up into mud. Each band gets its own gradient, scaled to that
34
+ band's own box (objectBoundingBox), so the fade reads the same at any thickness. */
35
+ const layers = shown.map((s, si) => {
36
+ const pts = s.values.map((v, i) => [sx(i), sy(stacked ? bands[si][i][1] : v)]);
37
+ const d = shapePath(pts, curve);
38
+ const under = stacked
39
+ ? [...s.values.keys()].reverse().map((i) => sx(i).toFixed(1) + " " + sy(bands[si][i][0]).toFixed(1)).join(" L")
40
+ : null;
41
+ return { s, si, pts, d, color: colorOf(s), fill: stacked ? d + " L" + under + " Z" : closeToBaseline(d, pts, base) };
42
+ });
43
+ const painted = layers;
44
+ return (
45
+ <div style={{ ...wrapStyle, ...style }}>
46
+ <svg width={width} height={height} onMouseMove={move} onMouseLeave={() => setHover(null)} role="img">
47
+ <defs>
48
+ {series.map((s, i) => {
49
+ const c = s.color || seriesColor(i);
50
+ return (
51
+ <linearGradient key={i} id={gid + "-" + i} x1="0" y1="0" x2="0" y2="1">
52
+ <stop offset="0%" stopColor={c} stopOpacity={stacked ? "0.30" : "0.34"} />
53
+ <stop offset="100%" stopColor={c} stopOpacity={stacked ? "0.10" : "0.02"} />
54
+ </linearGradient>
55
+ );
56
+ })}
57
+ </defs>
58
+ {axes ? <PlotGrid w={width} h={height} pad={pad} ticks={ticks} sy={sy} xLabels={thinLabels(xLabels)} sxLabel={sx} yFormat={yFormat} /> : null}
59
+ {target != null ? <g>
60
+ <line x1={pad.l} x2={width - pad.r} y1={sy(target)} y2={sy(target)} stroke="var(--chart-guide)" strokeWidth="1" strokeDasharray="3 3" />
61
+ <text x={width - pad.r} y={sy(target) - 4} textAnchor="end" style={{ fontFamily: "var(--font-sans)", fontSize: 10, fill: "var(--chart-label)" }}>{targetLabel || "Target " + yFormat(target)}</text>
62
+ </g> : null}
63
+ {painted.map((L) => (
64
+ <g key={L.s.name}>
65
+ {(area || stacked) ? <path d={L.fill} fill={"url(#" + gid + "-" + series.indexOf(L.s) + ")"} /> : null}
66
+ <path d={L.d} fill="none" stroke={L.color} strokeWidth="1.5" strokeLinejoin="round" strokeLinecap="round" />
67
+ </g>
68
+ ))}
69
+ {hover != null && interactive ? <line x1={sx(hover)} x2={sx(hover)} y1={pad.t} y2={height - pad.b} stroke="var(--chart-guide)" strokeWidth="1" strokeDasharray="2 3" /> : null}
70
+ {hover != null ? layers.map((L) => (L.s.values[hover] == null ? null : (
71
+ <circle key={L.s.name} cx={sx(hover)} cy={sy(stacked ? bands[L.si][hover][1] : L.s.values[hover])} r="3" fill={L.color} stroke="var(--bg-surface)" strokeWidth="1.5" />
72
+ ))) : null}
73
+ </svg>
74
+ {hover != null && interactive ? (
75
+ <ChartTooltip x={sx(hover)} y={height / 2 - 10} flip={sx(hover) > width * 0.62} title={xLabels[hover]}
76
+ rows={shown.map((s) => ({ label: s.name, value: yFormat(s.values[hover]), color: colorOf(s) }))} />
77
+ ) : null}
78
+ {legend && series.length > 1 ? (
79
+ <ChartLegend style={{ paddingTop: 8 }} items={series.map((s) => ({ label: s.name, color: colorOf(s) }))} hidden={hidden}
80
+ onToggle={(l) => setHidden(hidden.includes(l) ? hidden.filter((x) => x !== l) : hidden.length + 1 < series.length ? [...hidden, l] : hidden)} />
81
+ ) : null}
82
+ </div>
83
+ );
84
+ }
@@ -0,0 +1,86 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ import { fmtNum, extent, scaleLin, shapePath, closeToBaseline, stack, niceTicks, SERIES } from './chart-geometry.js'
4
+ // Line / area / stacked-area over shared x labels. Hover guideline + tooltip; legend click mutes.
5
+ const props = defineProps({ series:{type:Array,default:()=>[]}, xLabels:{type:Array,default:()=>[]}, width:{type:Number,default:560}, height:{type:Number,default:240}, area:Boolean, stacked:Boolean, axes:{type:Boolean,default:true}, legend:{type:Boolean,default:true}, interactive:{type:Boolean,default:true}, yFormat:{type:Function,default:fmtNum}, target:Number, targetLabel:String, style:Object })
6
+ const hidden = ref([]); const hover = ref(null)
7
+ const uid = 'kbline-' + Math.random().toString(36).slice(2, 8)
8
+ const g = computed(() => {
9
+ const shown = props.series.filter(s=>!hidden.value.includes(s.name))
10
+ const pad = { l: props.axes?44:4, r:8, t:10, b: props.axes?24:4 }
11
+ const vals = shown.map(s=>s.values)
12
+ const bands = props.stacked && shown.length ? stack(vals) : null
13
+ const [lo0,hi0] = props.stacked ? [0, Math.max(1,...(bands[bands.length-1]||[]).map(b=>b[1]))] : extent(vals.concat(props.target!=null?[[props.target]]:[]))
14
+ const { ticks, lo, hi } = niceTicks(lo0, hi0, 4)
15
+ const n = Math.max(...props.series.map(s=>s.values.length), 2)
16
+ const sx = scaleLin(0, n-1, pad.l+4, props.width-pad.r-4)
17
+ const sy = scaleLin(lo, hi, props.height-pad.b, pad.t)
18
+ const colorOf = s => s.color || SERIES[props.series.indexOf(s)%SERIES.length]
19
+ /* Stacked bands close against the band below, not the baseline — overlapping
20
+ translucent fills stack up into mud. */
21
+ const base = sy(Math.max(lo, 0))
22
+ const lines = shown.map((s,si)=>{
23
+ const pts = s.values.map((v,i)=>[sx(i), sy(props.stacked?bands[si][i][1]:v)])
24
+ const d = shapePath(pts, props.curve)
25
+ const fill = props.stacked
26
+ ? d + ' L' + [...s.values.keys()].reverse().map(i=>sx(i).toFixed(1)+' '+sy(bands[si][i][0]).toFixed(1)).join(' L') + ' Z'
27
+ : closeToBaseline(d, pts, base)
28
+ return { s, color: colorOf(s), path: d, fill, pts, at:(i)=>[sx(i), sy(props.stacked?bands[si][i][1]:s.values[i])] }
29
+ })
30
+ return { pad, ticks, sx, sy, n, lines, shown, colorOf }
31
+ })
32
+ function move(e){
33
+ if (!props.interactive) return
34
+ const r = e.currentTarget.getBoundingClientRect()
35
+ const gg = g.value
36
+ hover.value = Math.max(0, Math.min(gg.n-1, Math.round(((e.clientX-r.left-gg.pad.l-4)/(props.width-gg.pad.l-gg.pad.r-8))*(gg.n-1))))
37
+ }
38
+ function toggle(name){
39
+ if (hidden.value.includes(name)) hidden.value = hidden.value.filter(x=>x!==name)
40
+ else if (hidden.value.length+1 < props.series.length) hidden.value = [...hidden.value, name]
41
+ }
42
+ </script>
43
+ <template>
44
+ <div :style="{position:'relative',display:'inline-block',lineHeight:0,...style}">
45
+ <svg :width="width" :height="height" @mousemove="move" @mouseleave="hover=null" role="img">
46
+ <defs>
47
+ <linearGradient v-for="(s,i) in series" :key="'grad'+i" :id="uid+'-'+i" x1="0" y1="0" x2="0" y2="1">
48
+ <stop offset="0%" :stop-color="s.color||SERIES[i%SERIES.length]" :stop-opacity="stacked ? 0.30 : 0.34"/>
49
+ <stop offset="100%" :stop-color="s.color||SERIES[i%SERIES.length]" :stop-opacity="stacked ? 0.10 : 0.02"/>
50
+ </linearGradient>
51
+ </defs>
52
+ <template v-if="axes">
53
+ <g v-for="t in g.ticks" :key="t">
54
+ <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sy(t)" :y2="g.sy(t)" :stroke="t===0?'var(--chart-axis)':'var(--chart-grid)'" stroke-width="1"/>
55
+ <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">{{ yFormat(t) }}</text>
56
+ </g>
57
+ <text v-for="(lb,i) in xLabels" :key="'x'+i" :x="g.sx(i)" :y="height-g.pad.b+16" text-anchor="middle" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ lb }}</text>
58
+ </template>
59
+ <template v-if="target!=null">
60
+ <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sy(target)" :y2="g.sy(target)" stroke="var(--chart-guide)" stroke-width="1" stroke-dasharray="3 3"/>
61
+ <text :x="width-g.pad.r" :y="g.sy(target)-4" text-anchor="end" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ targetLabel || 'Target '+yFormat(target) }}</text>
62
+ </template>
63
+ <g v-for="ln in g.lines" :key="ln.s.name">
64
+ <path v-if="area||stacked" :d="ln.fill" :fill="'url(#'+uid+'-'+series.indexOf(ln.s)+')'"/>
65
+ <path :d="ln.path" fill="none" :stroke="ln.color" stroke-width="1.5" stroke-linejoin="round"/>
66
+ <circle v-if="hover!=null && ln.s.values[hover]!=null" :cx="ln.at(hover)[0]" :cy="ln.at(hover)[1]" r="3" :fill="ln.color" stroke="var(--bg-surface)" stroke-width="1.5"/>
67
+ </g>
68
+ <line v-if="hover!=null && interactive" :x1="g.sx(hover)" :x2="g.sx(hover)" :y1="g.pad.t" :y2="height-g.pad.b" stroke="var(--chart-guide)" stroke-width="1" stroke-dasharray="2 3"/>
69
+ </svg>
70
+ <div v-if="hover!=null && interactive" :style="{position:'absolute',left:g.sx(hover)+'px',top:(height/2-10)+'px',transform:g.sx(hover)>width*0.62?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',minWidth:'90px',lineHeight:'normal'}">
71
+ <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:4px;white-space:nowrap">{{ xLabels[hover] }}</div>
72
+ <div v-for="ln in g.lines" :key="ln.s.name" style="display:flex;align-items:center;gap:6px;padding:1px 0">
73
+ <span :style="{width:'7px',height:'7px',borderRadius:'2px',background:ln.color,flexShrink:0}"/>
74
+ <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary);white-space:nowrap">{{ ln.s.name }}</span>
75
+ <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ yFormat(ln.s.values[hover]) }}</span>
76
+ </div>
77
+ </div>
78
+ <div v-if="legend && series.length>1" style="display:flex;flex-wrap:wrap;gap:4px 14px;padding-top:8px;line-height:normal">
79
+ <span v-for="(s,i) in series" :key="s.name" role="button" tabindex="0" @click="toggle(s.name)"
80
+ :style="{display:'inline-flex',alignItems:'center',gap:'6px',cursor:'pointer',opacity:hidden.includes(s.name)?0.4:1}">
81
+ <span :style="{width:'8px',height:'8px',borderRadius:'2px',background:s.color||SERIES[i%SERIES.length],flexShrink:0}"/>
82
+ <span :style="{fontFamily:'var(--font-sans)',fontSize:'var(--text-xs)',color:'var(--text-secondary)',textDecoration:hidden.includes(s.name)?'line-through':'none'}">{{ s.name }}</span>
83
+ </span>
84
+ </div>
85
+ </div>
86
+ </template>
@@ -0,0 +1,13 @@
1
+ import * as React from "react";
2
+ export interface ParetoItem { label: string; value: number; }
3
+ /** Sorted bars + cumulative % line and the 80% guide — the "vital few" chart. */
4
+ export interface ParetoChartProps {
5
+ items?: ParetoItem[];
6
+ width?: number; height?: number;
7
+ axes?: boolean;
8
+ /** Cumulative cutoff that separates vital few from trivial many. Default 80 */ threshold?: number;
9
+ valueFormat?: (v: number) => string;
10
+ interactive?: boolean;
11
+ style?: React.CSSProperties;
12
+ }
13
+ export declare function ParetoChart(props: ParetoChartProps): JSX.Element;
@@ -0,0 +1,54 @@
1
+ import React from "react";
2
+ import { fmtNum, niceTicks, scaleLin, linePath } from "./chart-geometry.js";
3
+ import { ChartTooltip, PlotGrid, wrapStyle } from "./ChartKit.jsx";
4
+
5
+ /**
6
+ * Pareto — bars sorted descending with a cumulative % line on the right axis, plus the
7
+ * 80% guide. items: [{label, value}]; the component sorts and accumulates for you.
8
+ */
9
+ export function ParetoChart({ items = [], width = 560, height = 260, axes = true, threshold = 80, valueFormat = fmtNum, interactive = true, style }) {
10
+ const [hover, setHover] = React.useState(null);
11
+ const sorted = items.slice().sort((a, b) => b.value - a.value);
12
+ const total = sorted.reduce((s, i) => s + i.value, 0) || 1;
13
+ let run = 0;
14
+ const rows = sorted.map((it) => { run += it.value; return { ...it, cum: (run / total) * 100 }; });
15
+ const pad = { l: axes ? 44 : 4, r: axes ? 38 : 4, t: 12, b: 30 };
16
+ const { ticks, lo, hi } = niceTicks(0, Math.max(...rows.map((r) => r.value)), 4);
17
+ const sy = scaleLin(lo, hi, height - pad.b, pad.t);
18
+ const sc = scaleLin(0, 100, height - pad.b, pad.t);
19
+ const slot = (width - pad.l - pad.r) / rows.length;
20
+ const bw = Math.min(slot * 0.62, 40);
21
+ const cx = (i) => pad.l + slot * i + slot / 2;
22
+ const move = (e) => {
23
+ if (!interactive) return;
24
+ const r = e.currentTarget.getBoundingClientRect();
25
+ const i = Math.floor((e.clientX - r.left - pad.l) / slot);
26
+ setHover(i >= 0 && i < rows.length ? i : null);
27
+ };
28
+ const vital = rows.findIndex((r) => r.cum >= threshold);
29
+ return (
30
+ <div style={{ ...wrapStyle, ...style }}>
31
+ <svg width={width} height={height} onMouseMove={move} onMouseLeave={() => setHover(null)} role="img">
32
+ {axes ? <PlotGrid w={width} h={height} pad={pad} ticks={ticks} sy={sy} xLabels={[]} sxLabel={() => 0} yFormat={valueFormat} /> : null}
33
+ {hover != null ? <rect x={pad.l + slot * hover} y={pad.t} width={slot} height={height - pad.t - pad.b} fill="var(--bg-hover)" /> : null}
34
+ <line x1={pad.l} x2={width - pad.r} y1={sc(threshold)} y2={sc(threshold)} stroke="var(--chart-guide)" strokeWidth="1" strokeDasharray="3 3" />
35
+ <text x={width - pad.r - 2} y={sc(threshold) - 4} textAnchor="end" style={{ fontFamily: "var(--font-sans)", fontSize: 10, fill: "var(--chart-label)" }}>{threshold}%</text>
36
+ {rows.map((r, i) => (
37
+ <rect key={r.label} x={cx(i) - bw / 2} y={sy(r.value)} width={bw} height={Math.max(1, sy(0) - sy(r.value))} rx="2"
38
+ fill={i <= vital ? "var(--series-1)" : "var(--chart-track)"} stroke={i > vital ? "var(--border-control)" : "none"}
39
+ opacity={hover == null || hover === i ? 1 : 0.5} style={{ transition: "opacity var(--dur-fast) var(--ease-out)" }} />
40
+ ))}
41
+ <path d={linePath(rows.map((r, i) => [cx(i), sc(r.cum)]))} fill="none" stroke="var(--chart-total)" strokeWidth="1.5" />
42
+ {rows.map((r, i) => <circle key={i} cx={cx(i)} cy={sc(r.cum)} r={hover === i ? 3.4 : 2.4} fill="var(--chart-total)" />)}
43
+ {axes ? [0, 25, 50, 75, 100].map((t) => <text key={t} x={width - pad.r + 6} y={sc(t) + 3} style={{ fontFamily: "var(--font-mono)", fontSize: 10, fill: "var(--chart-label)" }}>{t}%</text>) : null}
44
+ {rows.map((r, i) => <text key={"l" + i} x={cx(i)} y={height - pad.b + 15} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontSize: 10, fill: "var(--chart-label)" }}>{r.label}</text>)}
45
+ </svg>
46
+ {hover != null && interactive ? (
47
+ <ChartTooltip x={cx(hover)} y={height / 2 - 10} flip={cx(hover) > width * 0.62} title={rows[hover].label}
48
+ rows={[{ label: "value", value: valueFormat(rows[hover].value), color: "var(--series-1)" },
49
+ { label: "share", value: ((rows[hover].value / total) * 100).toFixed(1) + "%" },
50
+ { label: "cumulative", value: rows[hover].cum.toFixed(1) + "%", color: "var(--chart-total)" }]} />
51
+ ) : null}
52
+ </div>
53
+ );
54
+ }
@@ -0,0 +1,55 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ import { fmtNum, niceTicks, scaleLin, linePath } from './chart-geometry.js'
4
+ // Sorted bars + cumulative % line and the 80% guide — the "vital few" chart.
5
+ const props = defineProps({ items:{type:Array,default:()=>[]}, width:{type:Number,default:560}, height:{type:Number,default:260}, axes:{type:Boolean,default:true}, threshold:{type:Number,default:80}, valueFormat:{type:Function,default:fmtNum}, interactive:{type:Boolean,default:true}, style:Object })
6
+ const hover = ref(null)
7
+ const g = computed(() => {
8
+ const sorted = props.items.slice().sort((a,b)=>b.value-a.value)
9
+ const total = sorted.reduce((s,i)=>s+i.value,0)||1
10
+ let run = 0
11
+ const rows = sorted.map(it=>{ run+=it.value; return Object.assign({}, it, { cum:(run/total)*100 }) })
12
+ const pad = { l: props.axes?44:4, r: props.axes?38:4, t:12, b:30 }
13
+ const { ticks, lo, hi } = niceTicks(0, Math.max(...rows.map(r=>r.value)), 4)
14
+ const sy = scaleLin(lo, hi, props.height-pad.b, pad.t)
15
+ const sc = scaleLin(0, 100, props.height-pad.b, pad.t)
16
+ const slot = (props.width-pad.l-pad.r)/rows.length
17
+ const bw = Math.min(slot*0.62, 40)
18
+ const cx = i => pad.l+slot*i+slot/2
19
+ const vital = rows.findIndex(r=>r.cum>=props.threshold)
20
+ return { rows, total, pad, ticks, sy, sc, slot, bw, cx, vital,
21
+ line: linePath(rows.map((r,i)=>[cx(i), sc(r.cum)])), pct:[0,25,50,75,100] }
22
+ })
23
+ function move(e){
24
+ if(!props.interactive) return
25
+ const r = e.currentTarget.getBoundingClientRect()
26
+ const i = Math.floor((e.clientX-r.left-g.value.pad.l)/g.value.slot)
27
+ hover.value = i>=0 && i<g.value.rows.length ? i : null
28
+ }
29
+ </script>
30
+ <template>
31
+ <div :style="{position:'relative',display:'inline-block',lineHeight:0,...style}">
32
+ <svg :width="width" :height="height" @mousemove="move" @mouseleave="hover=null" role="img">
33
+ <template v-if="axes">
34
+ <g v-for="t in g.ticks" :key="t">
35
+ <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sy(t)" :y2="g.sy(t)" :stroke="t===0?'var(--chart-axis)':'var(--chart-grid)'" stroke-width="1"/>
36
+ <text :x="g.pad.l-8" :y="g.sy(t)+3" text-anchor="end" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">{{ valueFormat(t) }}</text>
37
+ </g>
38
+ </template>
39
+ <rect v-if="hover!=null" :x="g.pad.l+g.slot*hover" :y="g.pad.t" :width="g.slot" :height="height-g.pad.t-g.pad.b" fill="var(--bg-hover)"/>
40
+ <line :x1="g.pad.l" :x2="width-g.pad.r" :y1="g.sc(threshold)" :y2="g.sc(threshold)" stroke="var(--chart-guide)" stroke-width="1" stroke-dasharray="3 3"/>
41
+ <text :x="width-g.pad.r-2" :y="g.sc(threshold)-4" text-anchor="end" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ threshold }}%</text>
42
+ <rect v-for="(r,i) in g.rows" :key="r.label" :x="g.cx(i)-g.bw/2" :y="g.sy(r.value)" :width="g.bw" :height="Math.max(1,g.sy(0)-g.sy(r.value))" rx="2"
43
+ :fill="i<=g.vital?'var(--series-1)':'var(--chart-track)'" :stroke="i>g.vital?'var(--border-control)':'none'"
44
+ :opacity="hover==null||hover===i?1:0.5" style="transition:opacity var(--dur-fast) var(--ease-out)"/>
45
+ <path :d="g.line" fill="none" stroke="var(--chart-total)" stroke-width="1.5"/>
46
+ <circle v-for="(r,i) in g.rows" :key="'c'+i" :cx="g.cx(i)" :cy="g.sc(r.cum)" :r="hover===i?3.4:2.4" fill="var(--chart-total)"/>
47
+ <text v-if="axes" v-for="t in g.pct" :key="'p'+t" :x="width-g.pad.r+6" :y="g.sc(t)+3" style="font-family:var(--font-mono);font-size:10px;fill:var(--chart-label)">{{ t }}%</text>
48
+ <text v-for="(r,i) in g.rows" :key="'l'+i" :x="g.cx(i)" :y="height-g.pad.b+15" text-anchor="middle" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ r.label }}</text>
49
+ </svg>
50
+ <div v-if="hover!=null && interactive" :style="{position:'absolute',left:g.cx(hover)+'px',top:(height/2-10)+'px',transform:g.cx(hover)>width*0.62?'translate(calc(-100% - 10px),-50%)':'translate(10px,-50%)',pointerEvents:'none',zIndex:5,background:'var(--bg-surface)',border:'1px solid var(--border-card)',borderRadius:'var(--radius-md)',boxShadow:'var(--shadow-menu)',padding:'7px 10px',lineHeight:'normal',whiteSpace:'nowrap'}">
51
+ <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ g.rows[hover].label }}</div>
52
+ <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ valueFormat(g.rows[hover].value) }} · {{ ((g.rows[hover].value/g.total)*100).toFixed(1) }}% · cum {{ g.rows[hover].cum.toFixed(1) }}%</div>
53
+ </div>
54
+ </div>
55
+ </template>
@@ -0,0 +1,17 @@
1
+ import * as React from "react";
2
+ export interface Pie3DSegment { label: string; value: number; color?: string; }
3
+ /** Extruded pie/donut. Drag spins it in place (tilt fixed); hover lifts a slice + tooltip. */
4
+ export interface Pie3DProps {
5
+ segments?: Pie3DSegment[];
6
+ size?: number;
7
+ /** Extrusion depth in px */ thickness?: number;
8
+ /** 0 = pie, 0.5+ = donut */ innerRatio?: number;
9
+ /** Vertical squash of the ellipse, 0–1 */ tilt?: number;
10
+ /** Hover lift distance in px */ explode?: number;
11
+ /** Each slice extrudes to its own height by value — the stepped "stairs" pie */ stairs?: boolean;
12
+ /** Initial rotation in degrees */ spin?: number;
13
+ valueFormat?: (v: number) => string;
14
+ legend?: boolean; interactive?: boolean;
15
+ style?: React.CSSProperties;
16
+ }
17
+ export declare function Pie3D(props: Pie3DProps): JSX.Element;
@@ -0,0 +1,145 @@
1
+ import React from "react";
2
+ import { fmtNum, seriesColor } from "./chart-geometry.js";
3
+ import { shade } from "./chart-3d.js";
4
+ import { ChartTooltip, wrapStyle } from "./ChartKit.jsx";
5
+
6
+ /* Intersect the arc [a0,a1] (degrees, a1 > a0) with the window [w0,w1]; returns visible
7
+ sub-arcs. Handles arcs that spin past 360 by testing the window at ±360. */
8
+ function clipArc(a0, a1, w0, w1) {
9
+ const A0 = ((a0 % 360) + 360) % 360, len = a1 - a0, A1 = A0 + len;
10
+ const out = [];
11
+ for (const off of [-360, 0, 360]) {
12
+ const s = Math.max(A0, w0 + off), e = Math.min(A1, w1 + off);
13
+ if (e - s > 0.4) out.push([s, e]);
14
+ }
15
+ return out;
16
+ }
17
+
18
+ /**
19
+ * 3D pie / donut — extruded slices. Drag horizontally to spin in place (tilt stays fixed:
20
+ * free orbit only distorts an extruded pie). stairs={true} extrudes each slice to its own
21
+ * height by value — the stepped presentation pie. Hover lifts a slice and pins a tooltip.
22
+ */
23
+ export function Pie3D({ segments = [], size = 300, thickness = 26, innerRatio = 0, tilt = 0.45, explode = 6, stairs = false, spin: spin0 = 0, valueFormat = fmtNum, legend = true, interactive = true, style }) {
24
+ const [hover, setHover] = React.useState(null);
25
+ const [spin, setSpin] = React.useState(spin0);
26
+ const drag = React.useRef(null);
27
+ const total = segments.reduce((s, x) => s + x.value, 0) || 1;
28
+ const vmax = Math.max(...segments.map((s) => s.value), 1);
29
+ const maxTh = stairs ? Math.round(thickness * 2.1) : thickness;
30
+ const cx = size / 2, cy = size / 2 - maxTh / 2 + 6;
31
+ const rO = size / 2 - 10, rI = rO * innerRatio;
32
+ const pt = (r, deg, dz = 0) => [cx + r * Math.cos(((deg - 90) * Math.PI) / 180), cy + r * Math.sin(((deg - 90) * Math.PI) / 180) * tilt + dz];
33
+ let a = 0;
34
+ const slices = segments.map((s, i) => {
35
+ const a0 = a + spin, a1 = a + (s.value / total) * 360 + spin; a += (s.value / total) * 360;
36
+ const mid = ((((a0 + a1) / 2) % 360) + 360) % 360;
37
+ /* Stairs: bottoms share one plane, each top rises by value — the stepped pie. */
38
+ const h = stairs ? maxTh * (0.35 + 0.65 * (s.value / vmax)) : thickness;
39
+ return { s, i, a0, a1, mid, h, color: s.color || seriesColor(i) };
40
+ });
41
+ const arcPts = (r, a0, a1, dz) => {
42
+ const steps = Math.max(2, Math.ceil(Math.abs(a1 - a0) / 6));
43
+ return [...Array(steps + 1)].map((_, k) => pt(r, a0 + ((a1 - a0) * k) / steps, dz));
44
+ };
45
+ const path = (pts) => "M" + pts.map((p) => p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" L") + " Z";
46
+ const off = (sl) => {
47
+ if (hover !== sl.i || !interactive) return [0, 0];
48
+ return [Math.cos(((sl.mid - 90) * Math.PI) / 180) * explode, Math.sin(((sl.mid - 90) * Math.PI) / 180) * explode * tilt];
49
+ };
50
+ const hoverProps = (sl) => interactive ? {
51
+ onMouseEnter: () => !drag.current && setHover(sl.i),
52
+ onMouseLeave: () => setHover(null),
53
+ } : {};
54
+ const dim = (sl) => ({ opacity: hover == null || hover === sl.i ? 1 : 0.55, style: { transition: "opacity var(--dur-fast) var(--ease-out)" } });
55
+ /* deg 180 is the ellipse edge nearest the viewer. An outer wall is visible only where
56
+ its arc crosses the near window (90–270); an inner wall only where it crosses the far
57
+ window, seen through the hole. Clipping each wall to its visible window means the
58
+ visible pieces never overlap in angle — paint order between them stops mattering,
59
+ and no slice can ever show a gap between its top and its wall. */
60
+ const nearness = (sl) => -Math.cos((sl.mid * Math.PI) / 180);
61
+ const sorted = [...slices].sort((p, q) => nearness(p) - nearness(q));
62
+ /* Bottoms first: any sliver the walls miss reads as dark interior, never as a hole in
63
+ the render. */
64
+ const bottoms = slices.map((sl) => {
65
+ const [dx, dy] = off(sl);
66
+ const d = rI > 0
67
+ ? path([...arcPts(rO, sl.a0, sl.a1, maxTh), ...arcPts(rI, sl.a1, sl.a0, maxTh)].map(([x, y]) => [x + dx, y + dy]))
68
+ : path([[cx + dx, cy + dy + maxTh], ...arcPts(rO, sl.a0, sl.a1, maxTh).map(([x, y]) => [x + dx, y + dy])]);
69
+ return <path key={"b" + sl.i} d={d} fill={shade(sl.color, -0.45)} {...hoverProps(sl)} {...dim(sl)} />;
70
+ });
71
+ /* Each slice is a closed solid: two radial faces, then its full outer wall. Solids paint
72
+ far-to-near, so a taller near slice covers the interior faces behind it and the side
73
+ of the pie is never open at any spin angle. */
74
+ const solids = sorted.map((sl) => {
75
+ const [dx, dy] = off(sl);
76
+ const zTop = maxTh - sl.h;
77
+ const radial = (deg, k) => {
78
+ const pts = [pt(rI || 0.01, deg, zTop), pt(rO, deg, zTop), pt(rO, deg, maxTh), pt(rI || 0.01, deg, maxTh)].map(([x, y]) => [x + dx, y + dy]);
79
+ return <path key={"r" + k + sl.i} d={path(pts)} fill={shade(sl.color, -0.18)} />;
80
+ };
81
+ const top = arcPts(rO, sl.a0, sl.a1, zTop).map(([x, y]) => [x + dx, y + dy]);
82
+ const bot = arcPts(rO, sl.a0, sl.a1, maxTh).map(([x, y]) => [x + dx, y + dy]);
83
+ return (
84
+ <g key={"solid" + sl.i} {...hoverProps(sl)} {...dim(sl)}>
85
+ {radial(sl.a0, "s")}
86
+ {radial(sl.a1, "e")}
87
+ <path d={path([...top, ...bot.reverse()])} fill={shade(sl.color, -0.28)} />
88
+ </g>
89
+ );
90
+ });
91
+ const innerWalls = rI ? slices.flatMap((sl) => clipArc(sl.a0, sl.a1, -90, 90).map(([s, e], k) => {
92
+ const [dx, dy] = off(sl);
93
+ const top = arcPts(rI, s, e, maxTh - sl.h).map(([x, y]) => [x + dx, y + dy]);
94
+ const bot = arcPts(rI, e, s, maxTh).map(([x, y]) => [x + dx, y + dy]);
95
+ return <path key={"iw" + sl.i + "-" + k} d={path([...top, ...bot])} fill={shade(sl.color, -0.38)} {...hoverProps(sl)} {...dim(sl)} />;
96
+ })) : [];
97
+ const tops = sorted.map((sl) => {
98
+ const [dx, dy] = off(sl);
99
+ const zTop = maxTh - sl.h;
100
+ const d = rI > 0
101
+ ? path([...arcPts(rO, sl.a0, sl.a1, zTop), ...arcPts(rI, sl.a1, sl.a0, zTop)].map(([x, y]) => [x + dx, y + dy]))
102
+ : path([[cx + dx, cy + dy + zTop], ...arcPts(rO, sl.a0, sl.a1, zTop).map(([x, y]) => [x + dx, y + dy])]);
103
+ return <path key={"t" + sl.i} d={d} fill={sl.color} stroke="var(--bg-surface)" strokeWidth="1" {...hoverProps(sl)} {...dim(sl)} />;
104
+ });
105
+ const spinHandlers = interactive ? {
106
+ onPointerDown: (e) => { drag.current = { x: e.clientX, s: spin }; e.currentTarget.setPointerCapture && e.currentTarget.setPointerCapture(e.pointerId); },
107
+ onPointerMove: (e) => { if (drag.current) setSpin(drag.current.s + (e.clientX - drag.current.x) * 0.5); },
108
+ onPointerUp: () => { drag.current = null; },
109
+ onPointerLeave: () => { drag.current = null; },
110
+ } : {};
111
+ const h = hover != null ? slices.find((sl) => sl.i === hover) : null;
112
+ const tipAt = h ? pt(rO * 0.8, (h.a0 + h.a1) / 2, (maxTh - h.h) / 2) : null;
113
+ return (
114
+ <div style={{ ...wrapStyle, ...style }}>
115
+ <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
116
+ <div style={{ position: "relative" }}>
117
+ <svg width={size} height={size * 0.72 + maxTh} {...spinHandlers} role="img" style={{ cursor: interactive ? "grab" : "default", touchAction: "none" }}>
118
+ {bottoms}
119
+ {innerWalls}
120
+ {solids}
121
+ {tops}
122
+ </svg>
123
+ {h && interactive ? (
124
+ <ChartTooltip x={tipAt[0]} y={tipAt[1]} flip={tipAt[0] > size * 0.55} title={h.s.label}
125
+ rows={[{ label: "value", value: valueFormat(h.s.value), color: h.color }, { label: "share", value: ((h.s.value / total) * 100).toFixed(1) + "%" }]} />
126
+ ) : null}
127
+ </div>
128
+ {legend ? (
129
+ <div style={{ display: "flex", flexDirection: "column", gap: 6, lineHeight: "normal" }}>
130
+ {slices.map(({ s, i, color }) => (
131
+ <span key={s.label} onMouseEnter={interactive ? () => setHover(i) : undefined} onMouseLeave={interactive ? () => setHover(null) : undefined}
132
+ style={{ display: "inline-flex", alignItems: "center", gap: 7, opacity: hover == null || hover === i ? 1 : 0.45 }}>
133
+ <span style={{ width: 8, height: 8, borderRadius: 2, background: color, flexShrink: 0 }} />
134
+ <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--text-secondary)" }}>{s.label}</span>
135
+ <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: 6 }}>
136
+ {hover === i ? valueFormat(s.value) : ((s.value / total) * 100).toFixed(0) + "%"}
137
+ </span>
138
+ </span>
139
+ ))}
140
+ </div>
141
+ ) : null}
142
+ </div>
143
+ </div>
144
+ );
145
+ }