@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,115 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ import { fmtNum, SERIES } from './chart-geometry.js'
4
+ import { shade } from './chart-3d.js'
5
+ /**
6
+ * 3D pie / donut — extruded slices. Drag horizontally to spin in place (tilt stays fixed:
7
+ * free orbit only distorts an extruded pie). stairs extrudes each slice to its own height
8
+ * by value — the stepped presentation pie. Hover lifts a slice and pins a tooltip.
9
+ */
10
+ const props = defineProps({ segments:{type:Array,default:()=>[]}, size:{type:Number,default:300}, thickness:{type:Number,default:26}, innerRatio:{type:Number,default:0}, tilt:{type:Number,default:0.45}, explode:{type:Number,default:6}, stairs:Boolean, spin:{type:Number,default:0}, valueFormat:{type:Function,default:fmtNum}, legend:{type:Boolean,default:true}, interactive:{type:Boolean,default:true}, style:Object })
11
+ const hover = ref(null)
12
+ const spinNow = ref(props.spin)
13
+ const drag = ref(null)
14
+ function down(e){ drag.value = { x:e.clientX, s:spinNow.value }; e.currentTarget.setPointerCapture && e.currentTarget.setPointerCapture(e.pointerId) }
15
+ function move(e){ if (drag.value) spinNow.value = drag.value.s + (e.clientX-drag.value.x)*0.5 }
16
+ function up(){ drag.value = null }
17
+ const g = computed(() => {
18
+ const total = props.segments.reduce((s2,x)=>s2+x.value,0) || 1
19
+ const vmax = Math.max(...props.segments.map(s2=>s2.value), 1)
20
+ const maxTh = props.stairs ? Math.round(props.thickness*2.1) : props.thickness
21
+ const cx = props.size/2, cy = props.size/2 - maxTh/2 + 6
22
+ const rO = props.size/2-10, rI = rO*props.innerRatio
23
+ const pt = (r,deg,dz=0) => [cx + r*Math.cos(((deg-90)*Math.PI)/180), cy + r*Math.sin(((deg-90)*Math.PI)/180)*props.tilt + dz]
24
+ const arcPts = (r,a0,a1,dz) => { const steps = Math.max(2, Math.ceil(Math.abs(a1-a0)/6)); return [...Array(steps+1)].map((_,k)=>pt(r, a0+((a1-a0)*k)/steps, dz)) }
25
+ const path = pts => 'M' + pts.map(p=>p[0].toFixed(1)+' '+p[1].toFixed(1)).join(' L') + ' Z'
26
+ let a = 0
27
+ const slices = props.segments.map((s2,i)=>{
28
+ const a0 = a + spinNow.value, a1 = a + (s2.value/total)*360 + spinNow.value; a += (s2.value/total)*360
29
+ const mid = ((((a0+a1)/2)%360)+360)%360
30
+ // Stairs: bottoms share one plane, each top rises by value.
31
+ const h = props.stairs ? maxTh*(0.35+0.65*(s2.value/vmax)) : props.thickness
32
+ return { s:s2, i, a0, a1, mid, h, color: s2.color || SERIES[i%SERIES.length] }
33
+ })
34
+ const off = sl => (hover.value===sl.i && props.interactive)
35
+ ? [Math.cos(((sl.mid-90)*Math.PI)/180)*props.explode, Math.sin(((sl.mid-90)*Math.PI)/180)*props.explode*props.tilt]
36
+ : [0,0]
37
+ const nearness = sl => -Math.cos((sl.mid*Math.PI)/180)
38
+ const sorted = [...slices].sort((p,q)=>nearness(p)-nearness(q))
39
+ // Bottoms first: any sliver the walls miss reads as dark interior, never a hole.
40
+ const bottoms = slices.map(sl=>{
41
+ const [dx,dy] = off(sl)
42
+ const d = rI>0
43
+ ? path([...arcPts(rO,sl.a0,sl.a1,maxTh), ...arcPts(rI,sl.a1,sl.a0,maxTh)].map(([x,y])=>[x+dx,y+dy]))
44
+ : path([[cx+dx, cy+dy+maxTh], ...arcPts(rO,sl.a0,sl.a1,maxTh).map(([x,y])=>[x+dx,y+dy])])
45
+ return { key:'b'+sl.i, d, fill: shade(sl.color,-0.45), i:sl.i }
46
+ })
47
+ const innerWalls = rI ? sorted.map(sl=>{
48
+ const [dx,dy] = off(sl)
49
+ const top = arcPts(rI,sl.a0,sl.a1,maxTh-sl.h).map(([x,y])=>[x+dx,y+dy])
50
+ const bot = arcPts(rI,sl.a1,sl.a0,maxTh).map(([x,y])=>[x+dx,y+dy])
51
+ return { key:'iw'+sl.i, d: path([...top,...bot]), fill: shade(sl.color,-0.38), i:sl.i }
52
+ }) : []
53
+ /* Each slice is a closed solid: two radial faces plus its full outer wall, painted
54
+ far-to-near, so the side of the pie is never open at any spin angle. */
55
+ const solids = sorted.map(sl=>{
56
+ const [dx,dy] = off(sl)
57
+ const zTop = maxTh - sl.h
58
+ const radial = deg => path([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]))
59
+ const top = arcPts(rO,sl.a0,sl.a1,zTop).map(([x,y])=>[x+dx,y+dy])
60
+ const bot = arcPts(rO,sl.a0,sl.a1,maxTh).map(([x,y])=>[x+dx,y+dy])
61
+ return { key:'sd'+sl.i, i:sl.i, r0: radial(sl.a0), r1: radial(sl.a1),
62
+ wall: path([...top,...bot.reverse()]), side: shade(sl.color,-0.18), wallFill: shade(sl.color,-0.28) }
63
+ })
64
+ const tops = sorted.map(sl=>{
65
+ const [dx,dy] = off(sl)
66
+ const zTop = maxTh - sl.h
67
+ const d = rI>0
68
+ ? path([...arcPts(rO,sl.a0,sl.a1,zTop), ...arcPts(rI,sl.a1,sl.a0,zTop)].map(([x,y])=>[x+dx,y+dy]))
69
+ : path([[cx+dx, cy+dy+zTop], ...arcPts(rO,sl.a0,sl.a1,zTop).map(([x,y])=>[x+dx,y+dy])])
70
+ return { key:'t'+sl.i, d, fill: sl.color, i:sl.i }
71
+ })
72
+ const h = hover.value!=null ? slices.find(sl=>sl.i===hover.value) : null
73
+ return { total, maxTh, slices, bottoms, innerWalls, solids, tops,
74
+ tip: h ? { p: pt(rO*0.8, (h.a0+h.a1)/2, (maxTh-h.h)/2), s:h.s, color:h.color } : null }
75
+ })
76
+ function dim(i){ return hover.value==null || hover.value===i ? 1 : 0.55 }
77
+ function enter(i){ if (props.interactive && !drag.value) hover.value = i }
78
+ </script>
79
+ <template>
80
+ <div :style="{display:'inline-block',lineHeight:0,...style}">
81
+ <div style="display:flex;align-items:center;gap:16px">
82
+ <div style="position:relative">
83
+ <svg :width="size" :height="size*0.72+g.maxTh" @pointerdown="down" @pointermove="move" @pointerup="up" @pointerleave="up"
84
+ role="img" :style="{cursor:interactive?'grab':'default',touchAction:'none'}">
85
+ <path v-for="b in g.bottoms" :key="b.key" :d="b.d" :fill="b.fill" :opacity="dim(b.i)"
86
+ @mouseenter="enter(b.i)" @mouseleave="interactive && (hover=null)"/>
87
+ <path v-for="w in g.innerWalls" :key="w.key" :d="w.d" :fill="w.fill" :opacity="dim(w.i)"
88
+ @mouseenter="enter(w.i)" @mouseleave="interactive && (hover=null)"/>
89
+ <g v-for="sd in g.solids" :key="sd.key" :opacity="dim(sd.i)"
90
+ @mouseenter="enter(sd.i)" @mouseleave="interactive && (hover=null)"
91
+ style="transition:opacity var(--dur-fast) var(--ease-out)">
92
+ <path :d="sd.r0" :fill="sd.side"/>
93
+ <path :d="sd.r1" :fill="sd.side"/>
94
+ <path :d="sd.wall" :fill="sd.wallFill"/>
95
+ </g>
96
+ <path v-for="t in g.tops" :key="t.key" :d="t.d" :fill="t.fill" stroke="var(--bg-surface)" stroke-width="1" :opacity="dim(t.i)"
97
+ @mouseenter="enter(t.i)" @mouseleave="interactive && (hover=null)"/>
98
+ </svg>
99
+ <div v-if="g.tip && interactive" :style="{position:'absolute',left:g.tip.p[0]+'px',top:g.tip.p[1]+'px',transform:g.tip.p[0]>size*0.55?'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'}">
100
+ <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ g.tip.s.label }}</div>
101
+ <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ valueFormat(g.tip.s.value) }} · {{ ((g.tip.s.value/g.total)*100).toFixed(1) }}%</div>
102
+ </div>
103
+ </div>
104
+ <div v-if="legend" style="display:flex;flex-direction:column;gap:6px;line-height:normal">
105
+ <span v-for="sl in g.slices" :key="sl.s.label" @mouseenter="interactive && (hover=sl.i)" @mouseleave="interactive && (hover=null)"
106
+ :style="{display:'inline-flex',alignItems:'center',gap:'7px',opacity:dim(sl.i)}">
107
+ <span :style="{width:'8px',height:'8px',borderRadius:'2px',background:sl.color,flexShrink:0}"/>
108
+ <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ sl.s.label }}</span>
109
+ <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);margin-left:6px">
110
+ {{ hover===sl.i ? valueFormat(sl.s.value) : ((sl.s.value/g.total)*100).toFixed(0)+'%' }}</span>
111
+ </span>
112
+ </div>
113
+ </div>
114
+ </div>
115
+ </template>
@@ -0,0 +1,14 @@
1
+ import * as React from "react";
2
+ export interface RadarAxis { label: string; /** Axis ceiling; auto-scaled when omitted */ max?: number; }
3
+ export interface RadarSeries { name: string; values: number[]; color?: string; }
4
+ /** Spider chart — a few entities across the same 3–10 measures. Hovering a spoke reports every series. */
5
+ export interface RadarChartProps {
6
+ axes?: RadarAxis[];
7
+ series?: RadarSeries[];
8
+ size?: number;
9
+ /** Concentric grid rings */ levels?: number;
10
+ legend?: boolean; interactive?: boolean;
11
+ valueFormat?: (v: number) => string;
12
+ style?: React.CSSProperties;
13
+ }
14
+ export declare function RadarChart(props: RadarChartProps): JSX.Element;
@@ -0,0 +1,79 @@
1
+ import React from "react";
2
+ import { fmtNum, niceTicks, polar, seriesColor } from "./chart-geometry.js";
3
+ import { ChartLegend, ChartTooltip, wrapStyle } from "./ChartKit.jsx";
4
+
5
+ /**
6
+ * Radar (spider) chart — compare a few entities across the same 3–10 measures.
7
+ * axes: [{label, max?}]; series: [{name, values, color?}] aligned to axes.
8
+ * Hovering an axis spoke highlights it and reports every series at that measure.
9
+ */
10
+ export function RadarChart({ axes = [], series = [], size = 260, levels = 4, legend = true, interactive = true, valueFormat = fmtNum, style }) {
11
+ const [hover, setHover] = React.useState(null);
12
+ const [hidden, setHidden] = React.useState([]);
13
+ const shown = series.filter((s) => !hidden.includes(s.name));
14
+ /* Axis labels sit outside the outer ring, so the plot radius has to leave room for the
15
+ longest one — otherwise "Accuracy" gets clipped at the SVG edge. */
16
+ const cx = size / 2, cy = size / 2, r = size / 2 - 42;
17
+ const n = axes.length || 1;
18
+ const step = 360 / n;
19
+ const max = axes.map((a, i) => a.max != null ? a.max : niceTicks(0, Math.max(...series.map((s) => s.values[i] || 0)), 3).hi);
20
+ const at = (si, i) => {
21
+ const v = (shown[si].values[i] || 0) / (max[i] || 1);
22
+ return polar(cx, cy, Math.max(0, Math.min(1, v)) * r, i * step);
23
+ };
24
+ const colorOf = (s) => s.color || seriesColor(series.indexOf(s));
25
+ const move = (e) => {
26
+ if (!interactive) return;
27
+ const rc = e.currentTarget.getBoundingClientRect();
28
+ const dx = e.clientX - rc.left - cx, dy = e.clientY - rc.top - cy;
29
+ if (Math.hypot(dx, dy) > r + 22) return setHover(null);
30
+ let deg = (Math.atan2(dy, dx) * 180) / Math.PI + 90;
31
+ if (deg < 0) deg += 360;
32
+ setHover(Math.round(deg / step) % n);
33
+ };
34
+ const tip = hover != null ? polar(cx, cy, r * 0.72, hover * step) : null;
35
+ return (
36
+ <div style={{ ...wrapStyle, ...style }}>
37
+ <svg width={size} height={size} onMouseMove={move} onMouseLeave={() => setHover(null)} role="img" style={{ overflow: "visible" }}>
38
+ {[...Array(levels)].map((_, li) => {
39
+ const rr = (r * (li + 1)) / levels;
40
+ const pts = [...Array(n)].map((__, i) => polar(cx, cy, rr, i * step).map((v) => v.toFixed(1)).join(" ")).join(" L");
41
+ return <path key={li} d={"M" + pts + " Z"} fill="none" stroke="var(--chart-grid)" strokeWidth="1" />;
42
+ })}
43
+ {axes.map((a, i) => {
44
+ const [x, y] = polar(cx, cy, r, i * step);
45
+ const [lx, ly] = polar(cx, cy, r + 14, i * step);
46
+ /* An anchor="start" label grows rightward, so near-vertical spokes must center
47
+ and the right column must not sit so close to the edge that it clips. */
48
+ const anchor = Math.abs(lx - cx) < 12 ? "middle" : lx > cx ? "start" : "end";
49
+ const on = hover === i;
50
+ return (
51
+ <g key={a.label}>
52
+ <line x1={cx} y1={cy} x2={x} y2={y} stroke={on ? "var(--chart-guide)" : "var(--chart-axis)"} strokeWidth="1" />
53
+ <text x={lx} y={ly + 3} textAnchor={anchor}
54
+ style={{ fontFamily: "var(--font-sans)", fontSize: 10, fill: on ? "var(--text-heading)" : "var(--chart-label)" }}>{a.label}</text>
55
+ </g>
56
+ );
57
+ })}
58
+ {shown.map((s, si) => {
59
+ const pts = axes.map((_, i) => at(si, i));
60
+ const d = "M" + pts.map((p) => p[0].toFixed(1) + " " + p[1].toFixed(1)).join(" L") + " Z";
61
+ return (
62
+ <g key={s.name}>
63
+ <path d={d} fill={colorOf(s)} fillOpacity="0.14" stroke={colorOf(s)} strokeWidth="1.5" strokeLinejoin="round" />
64
+ {pts.map((p, i) => <circle key={i} cx={p[0]} cy={p[1]} r={hover === i ? 3.4 : 2.2} fill={colorOf(s)} stroke="var(--bg-surface)" strokeWidth={hover === i ? 1.5 : 0} />)}
65
+ </g>
66
+ );
67
+ })}
68
+ </svg>
69
+ {hover != null && interactive ? (
70
+ <ChartTooltip x={tip[0]} y={tip[1]} flip={tip[0] > size * 0.58} title={axes[hover].label}
71
+ rows={shown.map((s) => ({ label: s.name, value: valueFormat(s.values[hover]), color: colorOf(s) }))} />
72
+ ) : null}
73
+ {legend && series.length > 1 ? (
74
+ <ChartLegend style={{ paddingTop: 6 }} items={series.map((s) => ({ label: s.name, color: colorOf(s) }))} hidden={hidden}
75
+ onToggle={(l) => setHidden(hidden.includes(l) ? hidden.filter((x) => x !== l) : hidden.length + 1 < series.length ? [...hidden, l] : hidden)} />
76
+ ) : null}
77
+ </div>
78
+ );
79
+ }
@@ -0,0 +1,73 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ import { fmtNum, niceTicks, polar, SERIES } from './chart-geometry.js'
4
+ // Spider chart — a few entities across the same 3–10 measures. Hovering a spoke reports all series.
5
+ const props = defineProps({ axes:{type:Array,default:()=>[]}, series:{type:Array,default:()=>[]}, size:{type:Number,default:260}, levels:{type:Number,default:4}, legend:{type:Boolean,default:true}, interactive:{type:Boolean,default:true}, valueFormat:{type:Function,default:fmtNum}, style:Object })
6
+ const hover = ref(null); const hidden = ref([])
7
+ const g = computed(() => {
8
+ const cx = props.size/2, cy = props.size/2, r = props.size/2-42
9
+ const n = props.axes.length||1, step = 360/n
10
+ const shown = props.series.filter(s=>!hidden.value.includes(s.name))
11
+ const max = props.axes.map((a,i)=> a.max!=null ? a.max : niceTicks(0, Math.max(...props.series.map(s=>s.values[i]||0)), 3).hi)
12
+ const rings = [...Array(props.levels)].map((_,li)=>{
13
+ const rr = (r*(li+1))/props.levels
14
+ return 'M' + [...Array(n)].map((__,i)=>polar(cx,cy,rr,i*step).map(v=>v.toFixed(1)).join(' ')).join(' L') + ' Z'
15
+ })
16
+ const spokes = props.axes.map((a,i)=>{
17
+ const p = polar(cx,cy,r,i*step), l = polar(cx,cy,r+14,i*step)
18
+ // An anchor="start" label grows rightward, so near-vertical spokes must center.
19
+ return { label:a.label, x:p[0], y:p[1], lx:l[0], ly:l[1],
20
+ anchor: Math.abs(l[0]-cx)<12 ? 'middle' : (l[0]>cx ? 'start' : 'end') }
21
+ })
22
+ const shapes = shown.map(s=>{
23
+ const pts = props.axes.map((_,i)=>polar(cx,cy,Math.max(0,Math.min(1,(s.values[i]||0)/(max[i]||1)))*r, i*step))
24
+ return { s, color: s.color||SERIES[props.series.indexOf(s)%SERIES.length], pts,
25
+ d:'M'+pts.map(p=>p[0].toFixed(1)+' '+p[1].toFixed(1)).join(' L')+' Z' }
26
+ })
27
+ return { cx, cy, r, n, step, rings, spokes, shapes, shown }
28
+ })
29
+ function move(e){
30
+ if(!props.interactive) return
31
+ const rc = e.currentTarget.getBoundingClientRect()
32
+ const dx = e.clientX-rc.left-g.value.cx, dy = e.clientY-rc.top-g.value.cy
33
+ if (Math.hypot(dx,dy) > g.value.r+22) { hover.value=null; return }
34
+ let deg = (Math.atan2(dy,dx)*180)/Math.PI + 90
35
+ if (deg<0) deg += 360
36
+ hover.value = Math.round(deg/g.value.step) % g.value.n
37
+ }
38
+ const tip = computed(()=> hover.value==null ? null : polar(g.value.cx, g.value.cy, g.value.r*0.72, hover.value*g.value.step))
39
+ function toggle(name){
40
+ if (hidden.value.includes(name)) hidden.value = hidden.value.filter(x=>x!==name)
41
+ else if (hidden.value.length+1 < props.series.length) hidden.value = [...hidden.value, name]
42
+ }
43
+ </script>
44
+ <template>
45
+ <div :style="{position:'relative',display:'inline-block',lineHeight:0,...style}">
46
+ <svg :width="size" :height="size" @mousemove="move" @mouseleave="hover=null" role="img" style="overflow:visible">
47
+ <path v-for="(d,i) in g.rings" :key="'r'+i" :d="d" fill="none" stroke="var(--chart-grid)" stroke-width="1"/>
48
+ <g v-for="(sp,i) in g.spokes" :key="sp.label">
49
+ <line :x1="g.cx" :y1="g.cy" :x2="sp.x" :y2="sp.y" :stroke="hover===i?'var(--chart-guide)':'var(--chart-axis)'" stroke-width="1"/>
50
+ <text :x="sp.lx" :y="sp.ly+3" :text-anchor="sp.anchor" :style="{fontFamily:'var(--font-sans)',fontSize:'10px',fill:hover===i?'var(--text-heading)':'var(--chart-label)'}">{{ sp.label }}</text>
51
+ </g>
52
+ <g v-for="sh in g.shapes" :key="sh.s.name">
53
+ <path :d="sh.d" :fill="sh.color" fill-opacity="0.14" :stroke="sh.color" stroke-width="1.5" stroke-linejoin="round"/>
54
+ <circle v-for="(p,i) in sh.pts" :key="i" :cx="p[0]" :cy="p[1]" :r="hover===i?3.4:2.2" :fill="sh.color" stroke="var(--bg-surface)" :stroke-width="hover===i?1.5:0"/>
55
+ </g>
56
+ </svg>
57
+ <div v-if="hover!=null && interactive" :style="{position:'absolute',left:tip[0]+'px',top:tip[1]+'px',transform:tip[0]>size*0.58?'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'}">
58
+ <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ axes[hover].label }}</div>
59
+ <div v-for="sh in g.shapes" :key="sh.s.name" style="display:flex;align-items:center;gap:6px">
60
+ <span :style="{width:'7px',height:'7px',borderRadius:'2px',background:sh.color}"/>
61
+ <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ sh.s.name }}</span>
62
+ <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ valueFormat(sh.s.values[hover]) }}</span>
63
+ </div>
64
+ </div>
65
+ <div v-if="legend && series.length>1" style="display:flex;flex-wrap:wrap;gap:4px 14px;padding-top:6px;line-height:normal">
66
+ <span v-for="(s,i) in series" :key="s.name" role="button" tabindex="0" @click="toggle(s.name)"
67
+ :style="{display:'inline-flex',alignItems:'center',gap:'6px',cursor:'pointer',opacity:hidden.includes(s.name)?0.4:1}">
68
+ <span :style="{width:'8px',height:'8px',borderRadius:'2px',background:s.color||SERIES[i%SERIES.length]}"/>
69
+ <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ s.name }}</span>
70
+ </span>
71
+ </div>
72
+ </div>
73
+ </template>
@@ -0,0 +1,13 @@
1
+ import * as React from "react";
2
+ export interface Ribbon3DSeries { name: string; values: number[]; color?: string; }
3
+ /** Each series as an extruded band at its own depth row (the classic 3D area); drag to orbit. */
4
+ export interface Ribbon3DProps {
5
+ series?: Ribbon3DSeries[];
6
+ xLabels?: string[];
7
+ width?: number; height?: number;
8
+ yaw?: number; pitch?: number;
9
+ /** Band thickness as a fraction of its row, 0–1 */ ribbon?: number;
10
+ interactive?: boolean;
11
+ style?: React.CSSProperties;
12
+ }
13
+ export declare function Ribbon3D(props: Ribbon3DProps): JSX.Element;
@@ -0,0 +1,89 @@
1
+ import React from "react";
2
+ import { fmtNum, seriesColor, niceTicks } from "./chart-geometry.js";
3
+ import { makeProj, facePath, faceDepth, shade, dragRotate } from "./chart-3d.js";
4
+ import { ChartTooltip, wrapStyle } from "./ChartKit.jsx";
5
+
6
+ /**
7
+ * 3D ribbon series — each series is an extruded band at its own depth row (the classic
8
+ * "3D area" presentation). Drag to orbit.
9
+ */
10
+ export function Ribbon3D({ series = [], xLabels = [], width = 460, height = 300, yaw: yaw0 = 34, pitch: pitch0 = 30, ribbon = 0.55, interactive = true, valueFormat = fmtNum, style }) {
11
+ const [rot, setRot] = React.useState({ yaw: yaw0, pitch: pitch0 });
12
+ const [hover, setHover] = React.useState(null);
13
+ const n = Math.max(...series.map((s) => s.values.length), 2);
14
+ const hi = Math.max(...series.flatMap((s) => s.values)) || 1;
15
+ const m = series.length;
16
+ const scale = Math.min(width, height) * 0.42;
17
+ const P = makeProj(width / 2, height / 2 + height * 0.05, scale, rot.yaw, rot.pitch);
18
+ const base = [P(-0.5, -0.45, -0.5), P(0.5, -0.45, -0.5), P(0.5, -0.45, 0.5), P(-0.5, -0.45, 0.5)];
19
+ /* Vertical value axis with real ticks at the front-left corner. */
20
+ const vt = niceTicks(0, hi, 3);
21
+ const vTicks = vt.ticks.filter((t) => t <= hi * 1.001).map((t) => ({ p: P(-0.5, (t / hi - 0.5) * 0.9, 0.5), v: fmtNum(t) }));
22
+ const vAxis = [P(-0.5, -0.45, 0.5), P(-0.5, 0.45, 0.5)];
23
+ const rows = series.map((s, si) => {
24
+ const z0 = si / m - 0.5 + (1 / m) * (0.5 - ribbon / 2), z1 = z0 + (ribbon / m);
25
+ const y = (i) => ((s.values[i] || 0) / hi - 0.5) * 0.9;
26
+ const quads = [];
27
+ for (let i = 0; i < n - 1; i++) {
28
+ const x0 = i / (n - 1) - 0.5, x1 = (i + 1) / (n - 1) - 0.5;
29
+ const top = [P(x0, y(i), z0), P(x1, y(i + 1), z0), P(x1, y(i + 1), z1), P(x0, y(i), z1)];
30
+ const front = [P(x0, -0.45, z1), P(x1, -0.45, z1), P(x1, y(i + 1), z1), P(x0, y(i), z1)];
31
+ const back = [P(x0, -0.45, z0), P(x1, -0.45, z0), P(x1, y(i + 1), z0), P(x0, y(i), z0)];
32
+ quads.push({ pts: back, kind: "back" }, { pts: top, kind: "top" }, { pts: front, kind: "front" });
33
+ }
34
+ const end = [P(0.5, -0.45, z0), P(0.5, -0.45, z1), P(0.5, y(n - 1), z1), P(0.5, y(n - 1), z0)];
35
+ const start = [P(-0.5, -0.45, z0), P(-0.5, -0.45, z1), P(-0.5, y(0), z1), P(-0.5, y(0), z0)];
36
+ quads.push({ pts: end, kind: "end" }, { pts: start, kind: "end" });
37
+ quads.forEach((q) => { q.depth = faceDepth(q.pts); });
38
+ const rowDepth = quads.reduce((t, q) => t + q.depth, 0) / quads.length;
39
+ return { s, si, quads, rowDepth, color: s.color || seriesColor(si) };
40
+ }).sort((a, b) => b.rowDepth - a.rowDepth);
41
+ const handlers = interactive ? dragRotate(() => rot, setRot) : {};
42
+ return (
43
+ <div style={{ ...wrapStyle, ...style }}>
44
+ <svg width={width} height={height} {...handlers} role="img" style={{ cursor: interactive ? "grab" : "default", touchAction: "none" }}>
45
+ <path d={facePath(base)} fill="var(--bg-inset)" stroke="var(--chart-axis)" strokeWidth="1" />
46
+ <line x1={vAxis[0].x} y1={vAxis[0].y} x2={vAxis[1].x} y2={vAxis[1].y} stroke="var(--chart-guide)" strokeWidth="1" />
47
+ {vTicks.map((t, i) => (
48
+ <g key={"vt" + i}>
49
+ <line x1={t.p.x - 4} y1={t.p.y} x2={t.p.x} y2={t.p.y} stroke="var(--chart-guide)" strokeWidth="1" />
50
+ <text x={t.p.x - 7} y={t.p.y + 3} textAnchor="end" style={{ fontFamily: "var(--font-mono)", fontSize: 9, fill: "var(--chart-label)" }}>{t.v}</text>
51
+ </g>
52
+ ))}
53
+ {xLabels.map((lb, i) => {
54
+ if (lb == null || (xLabels.length > 8 && i % Math.ceil(xLabels.length / 8) && i !== xLabels.length - 1)) return null;
55
+ /* Follow whichever base edge faces the viewer; outside it, painted before the bands. */
56
+ const zE = P(0, -0.45, 0.5).y >= P(0, -0.45, -0.5).y ? 0.62 : -0.62;
57
+ const p0 = P(i / (n - 1) - 0.5, -0.45, zE);
58
+ const ctr = P(0, -0.45, 0);
59
+ const dx = p0.x - ctr.x, dy = p0.y - ctr.y, len = Math.hypot(dx, dy) || 1;
60
+ return <text key={"x" + i} x={p0.x + (dx / len) * 14} y={p0.y + (dy / len) * 14 + 3} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontSize: 10, fill: "var(--chart-label)" }}>{lb}</text>;
61
+ })}
62
+ {rows.map((r) => (
63
+ <g key={r.s.name} opacity={hover == null || hover === r.si ? 1 : 0.4}
64
+ onMouseEnter={interactive ? () => setHover(r.si) : undefined} onMouseLeave={interactive ? () => setHover(null) : undefined}
65
+ style={{ transition: "opacity var(--dur-fast) var(--ease-out)" }}>
66
+ {r.quads.sort((a, b) => b.depth - a.depth).map((q, qi) => (
67
+ <path key={qi} d={facePath(q.pts)} fill={shade(r.color, q.kind === "top" ? 0.14 : q.kind === "front" ? -0.12 : q.kind === "back" ? -0.2 : -0.28)} stroke="var(--bg-surface)" strokeWidth="0.4" />
68
+ ))}
69
+ </g>
70
+ ))}
71
+ </svg>
72
+ {hover != null && interactive ? (() => {
73
+ const r = rows.find((x) => x.si === hover);
74
+ const peak = Math.max(...r.s.values);
75
+ return <ChartTooltip x={width / 2} y={height * 0.28} flip={false} title={r.s.name}
76
+ rows={[{ label: "peak", value: valueFormat(peak), color: r.color }, { label: "latest", value: valueFormat(r.s.values[r.s.values.length - 1]) }]} />;
77
+ })() : null}
78
+ <div style={{ display: "flex", gap: 14, paddingTop: 6, lineHeight: "normal" }}>
79
+ {series.map((s, i) => (
80
+ <span key={s.name} onMouseEnter={interactive ? () => setHover(i) : undefined} onMouseLeave={interactive ? () => setHover(null) : undefined}
81
+ style={{ display: "inline-flex", alignItems: "center", gap: 6, opacity: hover == null || hover === i ? 1 : 0.45, transition: "var(--t-hover)" }}>
82
+ <span style={{ width: 8, height: 8, borderRadius: 2, background: s.color || seriesColor(i) }} />
83
+ <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-xs)", color: "var(--text-secondary)" }}>{s.name}</span>
84
+ </span>
85
+ ))}
86
+ </div>
87
+ </div>
88
+ );
89
+ }
@@ -0,0 +1,79 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ import { fmtNum, SERIES, niceTicks } from './chart-geometry.js'
4
+ import { makeProj, facePath, faceDepth, shade } from './chart-3d.js'
5
+ // Each series as an extruded band at its own depth row (the classic 3D area); drag to orbit.
6
+ const props = defineProps({ series:{type:Array,default:()=>[]}, xLabels:{type:Array,default:()=>[]}, width:{type:Number,default:460}, height:{type:Number,default:300}, yaw:{type:Number,default:34}, pitch:{type:Number,default:30}, ribbon:{type:Number,default:0.55}, interactive:{type:Boolean,default:true}, valueFormat:{type:Function,default:fmtNum}, style:Object })
7
+ const rot = ref({ yaw: props.yaw, pitch: props.pitch })
8
+ const hover = ref(null)
9
+ function down(e){ e.currentTarget.__kbDrag = { x:e.clientX, y:e.clientY, ...rot.value }; e.currentTarget.setPointerCapture && e.currentTarget.setPointerCapture(e.pointerId) }
10
+ function move(e){ const s = e.currentTarget.__kbDrag; if(!s) return; rot.value = { yaw: s.yaw+(e.clientX-s.x)*0.45, pitch: Math.max(8, Math.min(80, s.pitch+(e.clientY-s.y)*0.35)) } }
11
+ function up(e){ e.currentTarget.__kbDrag = null }
12
+ const SHADE = { top:0.14, front:-0.12, back:-0.2, end:-0.28 }
13
+ const g = computed(() => {
14
+ const n = Math.max(...props.series.map(s=>s.values.length), 2)
15
+ const hi = Math.max(...props.series.flatMap(s=>s.values)) || 1
16
+ const m = props.series.length
17
+ const scale = Math.min(props.width, props.height)*0.42
18
+ const P = makeProj(props.width/2, props.height/2 + props.height*0.05, scale, rot.value.yaw, rot.value.pitch)
19
+ const base = [P(-0.5,-0.45,-0.5), P(0.5,-0.45,-0.5), P(0.5,-0.45,0.5), P(-0.5,-0.45,0.5)]
20
+ const rows = props.series.map((s,si)=>{
21
+ const z0 = si/m-0.5+(1/m)*(0.5-props.ribbon/2), z1 = z0+(props.ribbon/m)
22
+ const y = i => ((s.values[i]||0)/hi-0.5)*0.9
23
+ const quads = []
24
+ for (let i=0;i<n-1;i++) {
25
+ const x0 = i/(n-1)-0.5, x1 = (i+1)/(n-1)-0.5
26
+ quads.push({ pts:[P(x0,-0.45,z0),P(x1,-0.45,z0),P(x1,y(i+1),z0),P(x0,y(i),z0)], kind:'back' })
27
+ quads.push({ pts:[P(x0,y(i),z0),P(x1,y(i+1),z0),P(x1,y(i+1),z1),P(x0,y(i),z1)], kind:'top' })
28
+ quads.push({ pts:[P(x0,-0.45,z1),P(x1,-0.45,z1),P(x1,y(i+1),z1),P(x0,y(i),z1)], kind:'front' })
29
+ }
30
+ quads.push({ pts:[P(0.5,-0.45,z0),P(0.5,-0.45,z1),P(0.5,y(n-1),z1),P(0.5,y(n-1),z0)], kind:'end' })
31
+ quads.push({ pts:[P(-0.5,-0.45,z0),P(-0.5,-0.45,z1),P(-0.5,y(0),z1),P(-0.5,y(0),z0)], kind:'end' })
32
+ quads.forEach(q=>{ q.depth = faceDepth(q.pts); q.d = facePath(q.pts) })
33
+ quads.sort((a,b)=>b.depth-a.depth)
34
+ return { s, si, quads, rowDepth: quads.reduce((t,q)=>t+q.depth,0)/quads.length, color: s.color || SERIES[si%SERIES.length] }
35
+ }).sort((a,b)=>b.rowDepth-a.rowDepth)
36
+ const vt = niceTicks(0, hi, 3)
37
+ const vTicks = vt.ticks.filter(t=>t<=hi*1.001).map(t=>({ p:P(-0.5,(t/hi-0.5)*0.9,0.5), v:fmtNum(t) }))
38
+ const zE = P(0,-0.45,0.5).y >= P(0,-0.45,-0.5).y ? 0.62 : -0.62
39
+ const ctr = P(0,-0.45,0)
40
+ const every = props.xLabels.length > 8 ? Math.ceil(props.xLabels.length/8) : 1
41
+ const labels = props.xLabels.map((lb,i)=>{
42
+ if (lb==null || (every>1 && i%every && i!==props.xLabels.length-1)) return null
43
+ const p0 = P(i/(n-1)-0.5, -0.45, zE)
44
+ const dx = p0.x-ctr.x, dy = p0.y-ctr.y, len = Math.hypot(dx,dy)||1
45
+ return { lb, x: p0.x+(dx/len)*14, y: p0.y+(dy/len)*14+3 }
46
+ }).filter(Boolean)
47
+ return { rows, basePath: facePath(base), vTicks, vAxis:[P(-0.5,-0.45,0.5), P(-0.5,0.45,0.5)], labels, SHADE }
48
+ })
49
+ </script>
50
+ <template>
51
+ <div :style="{position:'relative',display:'inline-block',lineHeight:0,...style}">
52
+ <svg :width="width" :height="height" @pointerdown="down" @pointermove="move" @pointerup="up" @pointerleave="up"
53
+ role="img" :style="{cursor:interactive?'grab':'default',touchAction:'none'}">
54
+ <path :d="g.basePath" fill="var(--bg-inset)" stroke="var(--chart-axis)" stroke-width="1"/>
55
+ <line :x1="g.vAxis[0].x" :y1="g.vAxis[0].y" :x2="g.vAxis[1].x" :y2="g.vAxis[1].y" stroke="var(--chart-guide)" stroke-width="1"/>
56
+ <g v-for="(t,i) in g.vTicks" :key="'vt'+i">
57
+ <line :x1="t.p.x-4" :y1="t.p.y" :x2="t.p.x" :y2="t.p.y" stroke="var(--chart-guide)" stroke-width="1"/>
58
+ <text :x="t.p.x-7" :y="t.p.y+3" text-anchor="end" style="font-family:var(--font-mono);font-size:9px;fill:var(--chart-label)">{{ t.v }}</text>
59
+ </g>
60
+ <text v-for="(l,i) in g.labels" :key="'x'+i" :x="l.x" :y="l.y" text-anchor="middle" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ l.lb }}</text>
61
+ <g v-for="r in g.rows" :key="r.s.name" :opacity="hover==null||hover===r.si?1:0.4"
62
+ @mouseenter="interactive && (hover=r.si)" @mouseleave="interactive && (hover=null)"
63
+ style="transition:opacity var(--dur-fast) var(--ease-out)">
64
+ <path v-for="(q,qi) in r.quads" :key="qi" :d="q.d" :fill="shade(r.color, g.SHADE[q.kind])" stroke="var(--bg-surface)" stroke-width="0.4"/>
65
+ </g>
66
+ </svg>
67
+ <div v-if="hover!=null && interactive" :style="{position:'absolute',left:(width/2)+'px',top:(height*0.28)+'px',transform:'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'}">
68
+ <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ series[hover].name }}</div>
69
+ <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">peak {{ valueFormat(Math.max(...series[hover].values)) }} · latest {{ valueFormat(series[hover].values[series[hover].values.length-1]) }}</div>
70
+ </div>
71
+ <div style="display:flex;gap:14px;padding-top:6px;line-height:normal">
72
+ <span v-for="(s,i) in series" :key="s.name" @mouseenter="interactive && (hover=i)" @mouseleave="interactive && (hover=null)"
73
+ :style="{display:'inline-flex',alignItems:'center',gap:'6px',opacity:hover==null||hover===i?1:0.45,transition:'var(--t-hover)'}">
74
+ <span :style="{width:'8px',height:'8px',borderRadius:'2px',background:s.color||SERIES[i%SERIES.length]}"/>
75
+ <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ s.name }}</span>
76
+ </span>
77
+ </div>
78
+ </div>
79
+ </template>
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+ export interface SankeyNode { id: string; label: string; color?: string; }
3
+ export interface SankeyLink { source: string; target: string; value: number; }
4
+ /** Flow diagram; node columns are derived from the longest path, never positioned by hand. */
5
+ export interface SankeyChartProps {
6
+ nodes?: SankeyNode[];
7
+ links?: SankeyLink[];
8
+ width?: number; height?: number;
9
+ nodeWidth?: number; nodePadding?: number;
10
+ /** Right-hand room reserved for labels */ labelWidth?: number;
11
+ valueFormat?: (v: number) => string;
12
+ interactive?: boolean;
13
+ style?: React.CSSProperties;
14
+ }
15
+ export declare function SankeyChart(props: SankeyChartProps): JSX.Element;
@@ -0,0 +1,59 @@
1
+ import React from "react";
2
+ import { fmtNum, sankey, seriesColor } from "./chart-geometry.js";
3
+ import { ChartTooltip, wrapStyle } from "./ChartKit.jsx";
4
+
5
+ /**
6
+ * Sankey flow diagram — budget allocation, headcount movement, data lineage.
7
+ * nodes: [{id, label, color?}]; links: [{source, target, value}]. Depth (column) is
8
+ * derived from the longest path, so you never position nodes by hand. Hovering a
9
+ * ribbon or a node isolates that flow.
10
+ */
11
+ export function SankeyChart({ nodes = [], links = [], width = 620, height = 320, nodeWidth = 13, nodePadding = 16, valueFormat = fmtNum, interactive = true, labelWidth = 96, style }) {
12
+ const [hover, setHover] = React.useState(null);
13
+ const pad = { l: 6, r: labelWidth, t: 8, b: 8 };
14
+ const inner = { w: width - pad.l - pad.r, h: height - pad.t - pad.b };
15
+ const { nodes: laid, ribbons } = React.useMemo(
16
+ () => sankey(nodes, links, inner.w, inner.h, nodeWidth, nodePadding),
17
+ [nodes, links, inner.w, inner.h, nodeWidth, nodePadding]
18
+ );
19
+ const byId = Object.fromEntries(laid.map((n) => [n.id, n]));
20
+ const colorOf = (n) => n.color || seriesColor(laid.indexOf(n));
21
+ const litRibbon = (r) => hover == null ? 0.42
22
+ : hover.kind === "ribbon" ? (hover.i === ribbons.indexOf(r) ? 0.78 : 0.12)
23
+ : (r.link.source === hover.id || r.link.target === hover.id ? 0.7 : 0.1);
24
+ const h = hover;
25
+ const tip = h ? (h.kind === "ribbon"
26
+ ? { x: (ribbons[h.i].source.x + ribbons[h.i].target.x) / 2 + pad.l, y: 0, title: byId[ribbons[h.i].link.source].label + " → " + byId[ribbons[h.i].link.target].label,
27
+ rows: [{ label: "flow", value: valueFormat(ribbons[h.i].link.value), color: colorOf(byId[ribbons[h.i].link.source]) }] }
28
+ : { x: byId[h.id].x + pad.l + nodeWidth, y: byId[h.id].y + pad.t + byId[h.id].h / 2, title: byId[h.id].label,
29
+ rows: [{ label: "total", value: valueFormat(byId[h.id].value), color: colorOf(byId[h.id]) },
30
+ { label: "links", value: String(byId[h.id].in.length + byId[h.id].out.length) }] }) : null;
31
+ return (
32
+ <div style={{ ...wrapStyle, ...style }}>
33
+ <svg width={width} height={height} role="img">
34
+ <g transform={"translate(" + pad.l + "," + pad.t + ")"}>
35
+ {ribbons.map((r, i) => (
36
+ <path key={i} d={r.d} fill={colorOf(byId[r.link.source])} opacity={litRibbon(r)}
37
+ onMouseEnter={interactive ? () => setHover({ kind: "ribbon", i }) : undefined} onMouseLeave={interactive ? () => setHover(null) : undefined}
38
+ style={{ transition: "opacity var(--dur-fast) var(--ease-out)", cursor: interactive ? "default" : undefined }} />
39
+ ))}
40
+ {laid.map((n) => {
41
+ const c = colorOf(n);
42
+ const rightSide = n.x > inner.w * 0.55;
43
+ return (
44
+ <g key={n.id} onMouseEnter={interactive ? () => setHover({ kind: "node", id: n.id }) : undefined} onMouseLeave={interactive ? () => setHover(null) : undefined}
45
+ style={{ cursor: interactive ? "default" : undefined }}>
46
+ <rect x={n.x} y={n.y} width={n.w} height={n.h} rx="2" fill={c} opacity={hover == null || (hover.kind === "node" && hover.id === n.id) ? 1 : 0.55} />
47
+ <text x={rightSide ? n.x + n.w + 6 : n.x + n.w + 6} y={n.y + n.h / 2 + 3.5}
48
+ style={{ fontFamily: "var(--font-sans)", fontSize: 11, fill: "var(--text-heading)" }}>{n.label}</text>
49
+ <text x={n.x + n.w + 6} y={n.y + n.h / 2 + 15}
50
+ style={{ fontFamily: "var(--font-mono)", fontSize: 9.5, fill: "var(--chart-label)" }}>{valueFormat(n.value)}</text>
51
+ </g>
52
+ );
53
+ })}
54
+ </g>
55
+ </svg>
56
+ {tip && interactive ? <ChartTooltip x={tip.x} y={tip.y || height / 2} flip={tip.x > width * 0.6} title={tip.title} rows={tip.rows} /> : null}
57
+ </div>
58
+ );
59
+ }
@@ -0,0 +1,53 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ import { fmtNum, sankey, SERIES } from './chart-geometry.js'
4
+ // Flow diagram; node columns are derived from the longest path, never positioned by hand.
5
+ const props = defineProps({ nodes:{type:Array,default:()=>[]}, links:{type:Array,default:()=>[]}, width:{type:Number,default:620}, height:{type:Number,default:320}, nodeWidth:{type:Number,default:13}, nodePadding:{type:Number,default:16}, labelWidth:{type:Number,default:96}, valueFormat:{type:Function,default:fmtNum}, interactive:{type:Boolean,default:true}, style:Object })
6
+ const hover = ref(null)
7
+ const g = computed(() => {
8
+ const pad = { l:6, r:props.labelWidth, t:8, b:8 }
9
+ const inner = { w: props.width-pad.l-pad.r, h: props.height-pad.t-pad.b }
10
+ const laid = sankey(props.nodes, props.links, inner.w, inner.h, props.nodeWidth, props.nodePadding)
11
+ const byId = {}; laid.nodes.forEach(n=>{ byId[n.id]=n })
12
+ const color = n => n.color || SERIES[laid.nodes.indexOf(n)%SERIES.length]
13
+ return { pad, inner, laid, byId, color,
14
+ ribbons: laid.ribbons.map((r,i)=>Object.assign({}, r, { i, fill: color(byId[r.link.source]) })) }
15
+ })
16
+ function ribbonOpacity(r){
17
+ const h = hover.value
18
+ if (!h) return 0.42
19
+ if (h.kind==='ribbon') return h.i===r.i ? 0.78 : 0.12
20
+ return (r.link.source===h.id || r.link.target===h.id) ? 0.7 : 0.1
21
+ }
22
+ const tip = computed(()=>{
23
+ const h = hover.value
24
+ if (!h) return null
25
+ if (h.kind==='ribbon') {
26
+ const r = g.value.ribbons[h.i]
27
+ return { x:(r.source.x+r.target.x)/2+g.value.pad.l, y:props.height/2,
28
+ title: g.value.byId[r.link.source].label+' → '+g.value.byId[r.link.target].label, value: props.valueFormat(r.link.value) }
29
+ }
30
+ const n = g.value.byId[h.id]
31
+ return { x:n.x+g.value.pad.l+props.nodeWidth, y:n.y+g.value.pad.t+n.h/2, title:n.label, value: props.valueFormat(n.value) }
32
+ })
33
+ </script>
34
+ <template>
35
+ <div :style="{position:'relative',display:'inline-block',lineHeight:0,...style}">
36
+ <svg :width="width" :height="height" role="img">
37
+ <g :transform="'translate('+g.pad.l+','+g.pad.t+')'">
38
+ <path v-for="r in g.ribbons" :key="r.i" :d="r.d" :fill="r.fill" :opacity="ribbonOpacity(r)"
39
+ @mouseenter="interactive && (hover={kind:'ribbon',i:r.i})" @mouseleave="interactive && (hover=null)"
40
+ style="transition:opacity var(--dur-fast) var(--ease-out)"/>
41
+ <g v-for="n in g.laid.nodes" :key="n.id" @mouseenter="interactive && (hover={kind:'node',id:n.id})" @mouseleave="interactive && (hover=null)">
42
+ <rect :x="n.x" :y="n.y" :width="n.w" :height="n.h" rx="2" :fill="g.color(n)" :opacity="hover==null||(hover.kind==='node'&&hover.id===n.id)?1:0.55"/>
43
+ <text :x="n.x+n.w+6" :y="n.y+n.h/2+3.5" style="font-family:var(--font-sans);font-size:11px;fill:var(--text-heading)">{{ n.label }}</text>
44
+ <text :x="n.x+n.w+6" :y="n.y+n.h/2+15" style="font-family:var(--font-mono);font-size:9.5px;fill:var(--chart-label)">{{ valueFormat(n.value) }}</text>
45
+ </g>
46
+ </g>
47
+ </svg>
48
+ <div v-if="tip && interactive" :style="{position:'absolute',left:tip.x+'px',top:tip.y+'px',transform:tip.x>width*0.6?'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">{{ tip.title }}</div>
50
+ <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ tip.value }}</div>
51
+ </div>
52
+ </div>
53
+ </template>
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ export interface Point3D { x: number; y: number; z: number; series?: string; label?: string; color?: string; }
3
+ /** 3D scatter in an orientation cube; drag to orbit. Depth attenuates radius and opacity. */
4
+ export interface Scatter3DProps {
5
+ points?: Point3D[];
6
+ width?: number; height?: number;
7
+ yaw?: number; pitch?: number;
8
+ /** Base dot radius */ r?: number;
9
+ legend?: boolean; interactive?: boolean;
10
+ style?: React.CSSProperties;
11
+ }
12
+ export declare function Scatter3D(props: Scatter3DProps): JSX.Element;