@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,62 @@
1
+ import React from "react";
2
+ import { fmtNum, niceTicks, scaleLin, seriesColor } from "./chart-geometry.js";
3
+ import { ChartTooltip, PlotGrid, wrapStyle } from "./ChartKit.jsx";
4
+
5
+ /** Five-number summary from raw values (Tukey fences for outliers). */
6
+ export function summarize(values) {
7
+ const v = values.slice().sort((a, b) => a - b);
8
+ const q = (p) => { const i = (v.length - 1) * p, lo = Math.floor(i), hi = Math.ceil(i); return v[lo] + (v[hi] - v[lo]) * (i - lo); };
9
+ const q1 = q(0.25), med = q(0.5), q3 = q(0.75), iqr = q3 - q1;
10
+ const lo = Math.max(v[0], q1 - 1.5 * iqr), hi = Math.min(v[v.length - 1], q3 + 1.5 * iqr);
11
+ return { q1, med, q3, lo, hi, outliers: v.filter((x) => x < lo || x > hi) };
12
+ }
13
+
14
+ /**
15
+ * Box & whisker — spread and outliers per group. groups: [{label, values}] (summarized
16
+ * for you) or [{label, q1, med, q3, lo, hi, outliers}] when you have the stats already.
17
+ */
18
+ export function BoxPlot({ groups = [], width = 560, height = 260, axes = true, valueFormat = fmtNum, interactive = true, style }) {
19
+ const [hover, setHover] = React.useState(null);
20
+ const boxes = groups.map((g) => (g.values ? { label: g.label, color: g.color, ...summarize(g.values) } : g));
21
+ const pad = { l: axes ? 44 : 4, r: 8, t: 12, b: axes ? 26 : 4 };
22
+ const all = boxes.flatMap((b) => [b.lo, b.hi, ...(b.outliers || [])]);
23
+ const { ticks, lo, hi } = niceTicks(Math.min(...all), Math.max(...all), 4);
24
+ const sy = scaleLin(lo, hi, height - pad.b, pad.t);
25
+ const slot = (width - pad.l - pad.r) / boxes.length;
26
+ const bw = Math.min(slot * 0.5, 38);
27
+ const move = (e) => {
28
+ if (!interactive) return;
29
+ const r = e.currentTarget.getBoundingClientRect();
30
+ const i = Math.floor((e.clientX - r.left - pad.l) / slot);
31
+ setHover(i >= 0 && i < boxes.length ? i : null);
32
+ };
33
+ const h = hover != null ? boxes[hover] : null;
34
+ return (
35
+ <div style={{ ...wrapStyle, ...style }}>
36
+ <svg width={width} height={height} onMouseMove={move} onMouseLeave={() => setHover(null)} role="img">
37
+ {axes ? <PlotGrid w={width} h={height} pad={pad} ticks={ticks} sy={sy} xLabels={[]} sxLabel={() => 0} yFormat={valueFormat} /> : null}
38
+ {boxes.map((b, i) => {
39
+ const x = pad.l + slot * i + slot / 2;
40
+ const c = b.color || seriesColor(i);
41
+ return (
42
+ <g key={b.label} opacity={hover == null || hover === i ? 1 : 0.45} style={{ transition: "opacity var(--dur-fast) var(--ease-out)" }}>
43
+ <line x1={x} x2={x} y1={sy(b.hi)} y2={sy(b.q3)} stroke="var(--chart-axis)" strokeWidth="1" />
44
+ <line x1={x} x2={x} y1={sy(b.q1)} y2={sy(b.lo)} stroke="var(--chart-axis)" strokeWidth="1" />
45
+ <line x1={x - bw / 3} x2={x + bw / 3} y1={sy(b.hi)} y2={sy(b.hi)} stroke="var(--chart-axis)" strokeWidth="1" />
46
+ <line x1={x - bw / 3} x2={x + bw / 3} y1={sy(b.lo)} y2={sy(b.lo)} stroke="var(--chart-axis)" strokeWidth="1" />
47
+ <rect x={x - bw / 2} y={sy(b.q3)} width={bw} height={Math.max(1, sy(b.q1) - sy(b.q3))} rx="2" fill={c} fillOpacity="0.28" stroke={c} strokeWidth="1.2" />
48
+ <line x1={x - bw / 2} x2={x + bw / 2} y1={sy(b.med)} y2={sy(b.med)} stroke={c} strokeWidth="2" />
49
+ {(b.outliers || []).map((o, oi) => <circle key={oi} cx={x} cy={sy(o)} r="2.2" fill="none" stroke={c} strokeWidth="1" />)}
50
+ <text x={x} y={height - pad.b + 16} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontSize: 10, fill: "var(--chart-label)" }}>{b.label}</text>
51
+ </g>
52
+ );
53
+ })}
54
+ </svg>
55
+ {h && interactive ? (
56
+ <ChartTooltip x={pad.l + slot * hover + slot / 2} y={height / 2 - 10} flip={pad.l + slot * hover > width * 0.62} title={h.label}
57
+ rows={[{ label: "max", value: valueFormat(h.hi) }, { label: "Q3", value: valueFormat(h.q3) }, { label: "median", value: valueFormat(h.med), color: h.color || seriesColor(hover) }, { label: "Q1", value: valueFormat(h.q1) }, { label: "min", value: valueFormat(h.lo) }]
58
+ .concat((h.outliers || []).length ? [{ label: "outliers", value: String(h.outliers.length) }] : [])} />
59
+ ) : null}
60
+ </div>
61
+ );
62
+ }
@@ -0,0 +1,58 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ import { fmtNum, niceTicks, scaleLin, SERIES } from './chart-geometry.js'
4
+ // Box & whisker with Tukey-fence outliers. Pass raw values or the five-number stats.
5
+ const props = defineProps({ groups:{type:Array,default:()=>[]}, width:{type:Number,default:560}, height:{type:Number,default:260}, axes:{type:Boolean,default:true}, valueFormat:{type:Function,default:fmtNum}, interactive:{type:Boolean,default:true}, style:Object })
6
+ const hover = ref(null)
7
+ function summarize(values){
8
+ const v = values.slice().sort((a,b)=>a-b)
9
+ const q = p => { const i=(v.length-1)*p, lo=Math.floor(i), hi=Math.ceil(i); return v[lo]+(v[hi]-v[lo])*(i-lo) }
10
+ const q1=q(0.25), med=q(0.5), q3=q(0.75), iqr=q3-q1
11
+ const lo=Math.max(v[0], q1-1.5*iqr), hi=Math.min(v[v.length-1], q3+1.5*iqr)
12
+ return { q1, med, q3, lo, hi, outliers: v.filter(x=>x<lo||x>hi) }
13
+ }
14
+ const g = computed(() => {
15
+ const boxes = props.groups.map((x,i)=> Object.assign({ label:x.label, color:x.color||SERIES[i%SERIES.length] }, x.values ? summarize(x.values) : x))
16
+ const pad = { l: props.axes?44:4, r:8, t:12, b: props.axes?26:4 }
17
+ const all = boxes.flatMap(b=>[b.lo, b.hi, ...(b.outliers||[])])
18
+ const { ticks, lo, hi } = niceTicks(Math.min(...all), Math.max(...all), 4)
19
+ const sy = scaleLin(lo, hi, props.height-pad.b, pad.t)
20
+ const slot = (props.width-pad.l-pad.r)/boxes.length
21
+ const bw = Math.min(slot*0.5, 38)
22
+ return { boxes: boxes.map((b,i)=>Object.assign({}, b, { x: pad.l+slot*i+slot/2, bw })), pad, ticks, sy, slot }
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.boxes.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)">{{ valueFormat(t) }}</text>
38
+ </g>
39
+ </template>
40
+ <g v-for="(b,i) in g.boxes" :key="b.label" :opacity="hover==null||hover===i?1:0.45" style="transition:opacity var(--dur-fast) var(--ease-out)">
41
+ <line :x1="b.x" :x2="b.x" :y1="g.sy(b.hi)" :y2="g.sy(b.q3)" stroke="var(--chart-axis)" stroke-width="1"/>
42
+ <line :x1="b.x" :x2="b.x" :y1="g.sy(b.q1)" :y2="g.sy(b.lo)" stroke="var(--chart-axis)" stroke-width="1"/>
43
+ <line :x1="b.x-b.bw/3" :x2="b.x+b.bw/3" :y1="g.sy(b.hi)" :y2="g.sy(b.hi)" stroke="var(--chart-axis)" stroke-width="1"/>
44
+ <line :x1="b.x-b.bw/3" :x2="b.x+b.bw/3" :y1="g.sy(b.lo)" :y2="g.sy(b.lo)" stroke="var(--chart-axis)" stroke-width="1"/>
45
+ <rect :x="b.x-b.bw/2" :y="g.sy(b.q3)" :width="b.bw" :height="Math.max(1,g.sy(b.q1)-g.sy(b.q3))" rx="2" :fill="b.color" fill-opacity="0.28" :stroke="b.color" stroke-width="1.2"/>
46
+ <line :x1="b.x-b.bw/2" :x2="b.x+b.bw/2" :y1="g.sy(b.med)" :y2="g.sy(b.med)" :stroke="b.color" stroke-width="2"/>
47
+ <circle v-for="(o,oi) in (b.outliers||[])" :key="oi" :cx="b.x" :cy="g.sy(o)" r="2.2" fill="none" :stroke="b.color" stroke-width="1"/>
48
+ <text :x="b.x" :y="height-g.pad.b+16" text-anchor="middle" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ b.label }}</text>
49
+ </g>
50
+ </svg>
51
+ <div v-if="hover!=null && interactive" :style="{position:'absolute',left:g.boxes[hover].x+'px',top:(height/2-10)+'px',transform:g.boxes[hover].x>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'}">
52
+ <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ g.boxes[hover].label }}</div>
53
+ <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">
54
+ {{ valueFormat(g.boxes[hover].lo) }} · Q1 {{ valueFormat(g.boxes[hover].q1) }} · med {{ valueFormat(g.boxes[hover].med) }} · Q3 {{ valueFormat(g.boxes[hover].q3) }} · {{ valueFormat(g.boxes[hover].hi) }}
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </template>
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ export interface BulletRow { label: string; value: number; target: number; max?: number; /** [poor, ok] track tints */ ranges?: [number, number]; }
3
+ /** Actual vs. target rows with a target tick. tone "auto" colors misses red. */
4
+ export interface BulletChartProps {
5
+ rows?: BulletRow[];
6
+ width?: number;
7
+ tone?: "default" | "auto";
8
+ valueFormat?: (v: number) => string;
9
+ interactive?: boolean;
10
+ style?: React.CSSProperties;
11
+ }
12
+ export declare function BulletChart(props: BulletChartProps): JSX.Element;
@@ -0,0 +1,41 @@
1
+ import React from "react";
2
+ import { fmtNum } from "./chart-geometry.js";
3
+
4
+ /**
5
+ * Bullet rows — actual vs. target on a qualitative track. rows:
6
+ * [{label, value, target, max?, ranges?: [poor, ok] }] — ranges tint the track;
7
+ * the bar is --series-1 (or --chart-neg when value misses target with tone="auto").
8
+ */
9
+ export function BulletChart({ rows = [], width = 360, valueFormat = fmtNum, tone = "default", interactive = true, style }) {
10
+ const [hover, setHover] = React.useState(null);
11
+ return (
12
+ <div style={{ display: "flex", flexDirection: "column", gap: 14, width, ...style }}>
13
+ {rows.map((r) => {
14
+ const max = r.max != null ? r.max : Math.max(r.value, r.target) * 1.15;
15
+ const pc = (v) => Math.min(100, (v / max) * 100) + "%";
16
+ const missed = r.value < r.target;
17
+ const bar = tone === "auto" ? (missed ? "var(--chart-neg)" : "var(--chart-pos)") : "var(--series-1)";
18
+ const on = hover === r.label;
19
+ return (
20
+ <div key={r.label} onMouseEnter={interactive ? () => setHover(r.label) : undefined} onMouseLeave={interactive ? () => setHover(null) : undefined}
21
+ style={{ opacity: hover == null || on ? 1 : 0.55, transition: "var(--t-hover)" }}>
22
+ <div style={{ display: "flex", alignItems: "baseline", paddingBottom: 4 }}>
23
+ <span style={{ fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", color: "var(--text-body)" }}>{r.label}</span>
24
+ <span style={{ marginLeft: "auto", fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)", color: "var(--text-heading)" }}>{valueFormat(r.value)}</span>
25
+ <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", color: "var(--text-muted)", paddingLeft: 6 }}>/ {valueFormat(r.target)}</span>
26
+ {on ? <span style={{ fontFamily: "var(--font-mono)", fontSize: "var(--text-2xs)", color: missed ? "var(--chart-neg)" : "var(--chart-pos)", paddingLeft: 8 }}>{(missed ? "" : "+") + (((r.value - r.target) / (r.target || 1)) * 100).toFixed(1)}%</span> : null}
27
+ </div>
28
+ <div style={{ position: "relative", height: 12, borderRadius: 3, background: "var(--chart-track)", overflow: "hidden" }}>
29
+ {r.ranges ? <>
30
+ <div style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: pc(r.ranges[0]), background: "var(--alpha-heavy, rgba(0,0,0,.12))", opacity: 0.5 }} />
31
+ <div style={{ position: "absolute", left: 0, top: 0, bottom: 0, width: pc(r.ranges[1]), background: "var(--alpha-heavy, rgba(0,0,0,.12))", opacity: 0.25 }} />
32
+ </> : null}
33
+ <div style={{ position: "absolute", left: 0, top: 3, bottom: 3, width: pc(r.value), borderRadius: 2, background: bar, transition: "width var(--dur) var(--ease-out)" }} />
34
+ <div style={{ position: "absolute", left: pc(r.target), top: 0, bottom: 0, width: 2, background: "var(--chart-total)" }} />
35
+ </div>
36
+ </div>
37
+ );
38
+ })}
39
+ </div>
40
+ );
41
+ }
@@ -0,0 +1,33 @@
1
+ <script setup>
2
+ import { ref } from 'vue'
3
+ import { fmtNum } from './chart-geometry.js'
4
+ // Actual vs. target rows with a target tick. tone "auto" colors misses red.
5
+ const props = defineProps({ rows:{type:Array,default:()=>[]}, width:{type:Number,default:360}, tone:{type:String,default:'default'}, valueFormat:{type:Function,default:fmtNum}, interactive:{type:Boolean,default:true}, style:Object })
6
+ const hover = ref(null)
7
+ function geom(r){
8
+ const max = r.max!=null ? r.max : Math.max(r.value,r.target)*1.15
9
+ const pc = v => Math.min(100,(v/max)*100)+'%'
10
+ return { pc, bar: props.tone==='auto' ? (r.value<r.target?'var(--chart-neg)':'var(--chart-pos)') : 'var(--series-1)' }
11
+ }
12
+ </script>
13
+ <template>
14
+ <div :style="{display:'flex',flexDirection:'column',gap:'14px',width:width+'px',...style}">
15
+ <div v-for="r in rows" :key="r.label" @mouseenter="interactive && (hover=r.label)" @mouseleave="interactive && (hover=null)"
16
+ :style="{opacity:hover==null||hover===r.label?1:0.55,transition:'var(--t-hover)'}">
17
+ <div style="display:flex;align-items:baseline;padding-bottom:4px">
18
+ <span style="font-family:var(--font-sans);font-size:var(--text-sm);color:var(--text-body)">{{ r.label }}</span>
19
+ <span style="margin-left:auto;font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ valueFormat(r.value) }}</span>
20
+ <span style="font-family:var(--font-mono);font-size:var(--text-2xs);color:var(--text-muted);padding-left:6px">/ {{ valueFormat(r.target) }}</span>
21
+ <span v-if="hover===r.label" :style="{fontFamily:'var(--font-mono)',fontSize:'var(--text-2xs)',paddingLeft:'8px',color:r.value<r.target?'var(--chart-neg)':'var(--chart-pos)'}">{{ (r.value<r.target?'':'+')+(((r.value-r.target)/(r.target||1))*100).toFixed(1) }}%</span>
22
+ </div>
23
+ <div style="position:relative;height:12px;border-radius:3px;background:var(--chart-track);overflow:hidden">
24
+ <template v-if="r.ranges">
25
+ <div :style="{position:'absolute',left:0,top:0,bottom:0,width:geom(r).pc(r.ranges[0]),background:'rgba(0,0,0,.12)',opacity:.5}"/>
26
+ <div :style="{position:'absolute',left:0,top:0,bottom:0,width:geom(r).pc(r.ranges[1]),background:'rgba(0,0,0,.12)',opacity:.25}"/>
27
+ </template>
28
+ <div :style="{position:'absolute',left:0,top:'3px',bottom:'3px',width:geom(r).pc(r.value),borderRadius:'2px',background:geom(r).bar,transition:'width var(--dur) var(--ease-out)'}"/>
29
+ <div :style="{position:'absolute',left:geom(r).pc(r.target),top:0,bottom:0,width:'2px',background:'var(--chart-total)'}"/>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </template>
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+ export interface Candle { /** Axis label (date) */ t: string; o: number; h: number; l: number; c: number; /** Volume */ v?: number; }
3
+ /** OHLC K-line with optional volume band and moving average. */
4
+ export interface CandlestickChartProps {
5
+ candles?: Candle[];
6
+ width?: number; height?: number;
7
+ axes?: boolean;
8
+ /** Volume histogram under the price plot */ volume?: boolean;
9
+ /** Moving-average window in periods; 0 = off */ ma?: number;
10
+ /** Hollow bodies for up candles (East-Asian convention) */ hollowUp?: boolean;
11
+ valueFormat?: (v: number) => string;
12
+ interactive?: boolean;
13
+ style?: React.CSSProperties;
14
+ }
15
+ export declare function CandlestickChart(props: CandlestickChartProps): JSX.Element;
@@ -0,0 +1,63 @@
1
+ import React from "react";
2
+ import { fmtNum, niceTicks, scaleLin } from "./chart-geometry.js";
3
+ import { ChartTooltip, PlotGrid, wrapStyle } from "./ChartKit.jsx";
4
+
5
+ /**
6
+ * Candlestick (K-line) — OHLC price action with an optional volume band and moving average.
7
+ * candles: [{t, o, h, l, c, v?}]. Up candles are --chart-pos, down --chart-neg; a hovered
8
+ * candle pins the full OHLC readout. Set hollowUp for the East-Asian hollow-body convention.
9
+ */
10
+ export function CandlestickChart({ candles = [], width = 620, height = 300, axes = true, volume = true, ma = 0, hollowUp = false, valueFormat = fmtNum, interactive = true, style }) {
11
+ const [hover, setHover] = React.useState(null);
12
+ const volH = volume ? Math.round(height * 0.22) : 0;
13
+ const pad = { l: axes ? 46 : 4, r: 8, t: 10, b: (axes ? 22 : 4) + volH };
14
+ const { ticks, lo, hi } = niceTicks(Math.min(...candles.map((c) => c.l)), Math.max(...candles.map((c) => c.h)), 4);
15
+ const sy = scaleLin(lo, hi, height - pad.b, pad.t);
16
+ const slot = (width - pad.l - pad.r) / (candles.length || 1);
17
+ const bw = Math.max(2, Math.min(slot * 0.62, 14));
18
+ const cx = (i) => pad.l + slot * i + slot / 2;
19
+ const maxV = Math.max(1, ...candles.map((c) => c.v || 0));
20
+ const vy = (v) => height - (axes ? 22 : 4) - (v / maxV) * (volH - 6);
21
+ const avg = ma > 1 ? candles.map((_, i) => (i < ma - 1 ? null : candles.slice(i - ma + 1, i + 1).reduce((s, c) => s + c.c, 0) / ma)) : null;
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 < candles.length ? i : null);
27
+ };
28
+ const h = hover != null ? candles[hover] : null;
29
+ const up = (c) => c.c >= c.o;
30
+ return (
31
+ <div style={{ ...wrapStyle, ...style }}>
32
+ <svg width={width} height={height} onMouseMove={move} onMouseLeave={() => setHover(null)} role="img">
33
+ {axes ? <PlotGrid w={width} h={height} pad={pad} ticks={ticks} sy={sy} xLabels={[]} sxLabel={() => 0} yFormat={valueFormat} /> : null}
34
+ {hover != null ? <rect x={pad.l + slot * hover} y={pad.t} width={slot} height={height - pad.t - (axes ? 22 : 4)} fill="var(--bg-hover)" /> : null}
35
+ {avg ? <path d={"M" + avg.map((v, i) => (v == null ? null : cx(i).toFixed(1) + " " + sy(v).toFixed(1))).filter(Boolean).join(" L")} fill="none" stroke="var(--series-8)" strokeWidth="1.3" /> : null}
36
+ {candles.map((c, i) => {
37
+ const col = up(c) ? "var(--chart-pos)" : "var(--chart-neg)";
38
+ const yO = sy(c.o), yC = sy(c.c);
39
+ const top = Math.min(yO, yC), body = Math.max(1.5, Math.abs(yC - yO));
40
+ const solid = !hollowUp || !up(c);
41
+ return (
42
+ <g key={i} opacity={hover == null || hover === i ? 1 : 0.5} style={{ transition: "opacity var(--dur-fast) var(--ease-out)" }}>
43
+ <line x1={cx(i)} x2={cx(i)} y1={sy(c.h)} y2={sy(c.l)} stroke={col} strokeWidth="1" />
44
+ <rect x={cx(i) - bw / 2} y={top} width={bw} height={body} fill={solid ? col : "var(--bg-surface)"} stroke={col} strokeWidth="1" />
45
+ {volume && c.v != null ? <rect x={cx(i) - bw / 2} y={vy(c.v)} width={bw} height={Math.max(1, height - (axes ? 22 : 4) - vy(c.v))} fill={col} opacity="0.35" /> : null}
46
+ </g>
47
+ );
48
+ })}
49
+ {volume ? <line x1={pad.l} x2={width - pad.r} y1={height - (axes ? 22 : 4)} y2={height - (axes ? 22 : 4)} stroke="var(--chart-axis)" strokeWidth="1" /> : null}
50
+ {axes ? candles.map((c, i) => (i % Math.ceil(candles.length / 6) ? null : (
51
+ <text key={"t" + i} x={cx(i)} y={height - 6} textAnchor="middle" style={{ fontFamily: "var(--font-sans)", fontSize: 10, fill: "var(--chart-label)" }}>{c.t}</text>
52
+ ))) : null}
53
+ </svg>
54
+ {h && interactive ? (
55
+ <ChartTooltip x={cx(hover)} y={height / 2 - 20} flip={cx(hover) > width * 0.62} title={h.t}
56
+ rows={[{ label: "open", value: valueFormat(h.o) }, { label: "high", value: valueFormat(h.h) }, { label: "low", value: valueFormat(h.l) },
57
+ { label: "close", value: valueFormat(h.c), color: up(h) ? "var(--chart-pos)" : "var(--chart-neg)" },
58
+ { label: "change", value: ((h.c - h.o) >= 0 ? "+" : "") + (((h.c - h.o) / (h.o || 1)) * 100).toFixed(2) + "%" }]
59
+ .concat(h.v != null ? [{ label: "volume", value: fmtNum(h.v) }] : [])} />
60
+ ) : null}
61
+ </div>
62
+ );
63
+ }
@@ -0,0 +1,62 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ import { fmtNum, niceTicks, scaleLin } from './chart-geometry.js'
4
+ // OHLC K-line with optional volume band and moving average.
5
+ const props = defineProps({ candles:{type:Array,default:()=>[]}, width:{type:Number,default:620}, height:{type:Number,default:300}, axes:{type:Boolean,default:true}, volume:{type:Boolean,default:true}, ma:{type:Number,default:0}, hollowUp:Boolean, valueFormat:{type:Function,default:fmtNum}, interactive:{type:Boolean,default:true}, style:Object })
6
+ const hover = ref(null)
7
+ const g = computed(() => {
8
+ const volH = props.volume ? Math.round(props.height*0.22) : 0
9
+ const axisB = props.axes ? 22 : 4
10
+ const pad = { l: props.axes?46:4, r:8, t:10, b: axisB+volH }
11
+ const { ticks, lo, hi } = niceTicks(Math.min(...props.candles.map(c=>c.l)), Math.max(...props.candles.map(c=>c.h)), 4)
12
+ const sy = scaleLin(lo, hi, props.height-pad.b, pad.t)
13
+ const slot = (props.width-pad.l-pad.r)/(props.candles.length||1)
14
+ const bw = Math.max(2, Math.min(slot*0.62, 14))
15
+ const cx = i => pad.l+slot*i+slot/2
16
+ const maxV = Math.max(1, ...props.candles.map(c=>c.v||0))
17
+ const vy = v => props.height-axisB-(v/maxV)*(volH-6)
18
+ const avg = props.ma>1 ? props.candles.map((_,i)=> i<props.ma-1 ? null : props.candles.slice(i-props.ma+1,i+1).reduce((s,c)=>s+c.c,0)/props.ma) : null
19
+ const bars = props.candles.map((c,i)=>{
20
+ const up = c.c>=c.o, col = up?'var(--chart-pos)':'var(--chart-neg)'
21
+ const yO=sy(c.o), yC=sy(c.c)
22
+ return { c, i, up, col, x:cx(i), bw, top:Math.min(yO,yC), body:Math.max(1.5,Math.abs(yC-yO)), yH:sy(c.h), yL:sy(c.l),
23
+ solid: !props.hollowUp || !up, vY: c.v!=null?vy(c.v):null, vH: c.v!=null?Math.max(1, props.height-axisB-vy(c.v)):0 }
24
+ })
25
+ const maPath = avg ? 'M'+avg.map((v,i)=> v==null?null:cx(i).toFixed(1)+' '+sy(v).toFixed(1)).filter(Boolean).join(' L') : null
26
+ return { pad, ticks, sy, slot, cx, bars, maPath, axisB, every: Math.ceil(props.candles.length/6) }
27
+ })
28
+ function move(e){
29
+ if(!props.interactive) return
30
+ const r = e.currentTarget.getBoundingClientRect()
31
+ const i = Math.floor((e.clientX-r.left-g.value.pad.l)/g.value.slot)
32
+ hover.value = i>=0 && i<props.candles.length ? i : null
33
+ }
34
+ </script>
35
+ <template>
36
+ <div :style="{position:'relative',display:'inline-block',lineHeight:0,...style}">
37
+ <svg :width="width" :height="height" @mousemove="move" @mouseleave="hover=null" role="img">
38
+ <template v-if="axes">
39
+ <g v-for="t in g.ticks" :key="t">
40
+ <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"/>
41
+ <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>
42
+ </g>
43
+ </template>
44
+ <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.axisB" fill="var(--bg-hover)"/>
45
+ <path v-if="g.maPath" :d="g.maPath" fill="none" stroke="var(--series-8)" stroke-width="1.3"/>
46
+ <g v-for="b in g.bars" :key="b.i" :opacity="hover==null||hover===b.i?1:0.5" style="transition:opacity var(--dur-fast) var(--ease-out)">
47
+ <line :x1="b.x" :x2="b.x" :y1="b.yH" :y2="b.yL" :stroke="b.col" stroke-width="1"/>
48
+ <rect :x="b.x-b.bw/2" :y="b.top" :width="b.bw" :height="b.body" :fill="b.solid?b.col:'var(--bg-surface)'" :stroke="b.col" stroke-width="1"/>
49
+ <rect v-if="volume && b.vY!=null" :x="b.x-b.bw/2" :y="b.vY" :width="b.bw" :height="b.vH" :fill="b.col" opacity="0.35"/>
50
+ </g>
51
+ <line v-if="volume" :x1="g.pad.l" :x2="width-g.pad.r" :y1="height-g.axisB" :y2="height-g.axisB" stroke="var(--chart-axis)" stroke-width="1"/>
52
+ <text v-if="axes" v-for="b in g.bars.filter(b=>b.i%g.every===0)" :key="'t'+b.i" :x="b.x" :y="height-6" text-anchor="middle" style="font-family:var(--font-sans);font-size:10px;fill:var(--chart-label)">{{ b.c.t }}</text>
53
+ </svg>
54
+ <div v-if="hover!=null && interactive" :style="{position:'absolute',left:g.cx(hover)+'px',top:(height/2-20)+'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'}">
55
+ <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ candles[hover].t }}</div>
56
+ <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">O {{ valueFormat(candles[hover].o) }} · H {{ valueFormat(candles[hover].h) }} · L {{ valueFormat(candles[hover].l) }} · C {{ valueFormat(candles[hover].c) }}</div>
57
+ <div :style="{fontFamily:'var(--font-mono)',fontSize:'var(--text-2xs)',color:candles[hover].c>=candles[hover].o?'var(--chart-pos)':'var(--chart-neg)'}">
58
+ {{ (candles[hover].c-candles[hover].o>=0?'+':'')+(((candles[hover].c-candles[hover].o)/(candles[hover].o||1))*100).toFixed(2) }}%<span v-if="candles[hover].v!=null" style="color:var(--text-muted)"> · vol {{ fmtNum(candles[hover].v) }}</span>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </template>
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ /** Shared chart furniture: legend, tooltip, grid. Used by every chart; rarely imported directly. */
3
+ export interface LegendItem { label: string; color?: string; }
4
+ export interface ChartLegendProps { items?: LegendItem[]; hidden?: string[]; onToggle?: (label: string) => void; align?: "left" | "right"; style?: React.CSSProperties; }
5
+ export declare function ChartLegend(props: ChartLegendProps): JSX.Element;
6
+ export interface TooltipRow { label: string; value: string; color?: string; }
7
+ export interface ChartTooltipProps { x: number; y: number; title?: React.ReactNode; rows?: TooltipRow[]; /** Render to the left of the anchor */ flip?: boolean; }
8
+ export declare function ChartTooltip(props: ChartTooltipProps): JSX.Element;
@@ -0,0 +1,76 @@
1
+ import React from "react";
2
+ import { fmtNum, seriesColor } from "./chart-geometry.js";
3
+
4
+ const sans = "var(--font-sans)", mono = "var(--font-mono)";
5
+
6
+ /** Legend row. items: [{label,color}] — with onToggle, clicking mutes a series. */
7
+ export function ChartLegend({ items = [], hidden = [], onToggle, align = "left", style }) {
8
+ return (
9
+ <div style={{ display: "flex", flexWrap: "wrap", gap: "4px 14px", justifyContent: align === "right" ? "flex-end" : "flex-start", lineHeight: "normal", ...style }}>
10
+ {items.map((it, i) => {
11
+ const off = hidden.includes(it.label);
12
+ return (
13
+ <span key={it.label} role={onToggle ? "button" : undefined} tabIndex={onToggle ? 0 : undefined}
14
+ onClick={onToggle ? () => onToggle(it.label) : undefined}
15
+ style={{ display: "inline-flex", alignItems: "center", gap: 6, cursor: onToggle ? "pointer" : "default", opacity: off ? 0.4 : 1, transition: "var(--t-hover)" }}>
16
+ <span style={{ width: 8, height: 8, borderRadius: 2, background: it.color || seriesColor(i), flexShrink: 0 }} />
17
+ <span style={{ fontFamily: sans, fontSize: "var(--text-xs)", color: "var(--text-secondary)", textDecoration: off ? "line-through" : "none" }}>{it.label}</span>
18
+ </span>
19
+ );
20
+ })}
21
+ </div>
22
+ );
23
+ }
24
+
25
+ /** Floating tooltip. rows: [{label,value,color}]; pins near (x,y) inside the chart wrapper. */
26
+ export function ChartTooltip({ x, y, title, rows = [], flip }) {
27
+ return (
28
+ <div style={{ position: "absolute", left: x, top: y, transform: `translate(${flip ? "calc(-100% - 10px)" : "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: 90, lineHeight: "normal", whiteSpace: "nowrap" }}>
29
+ {title != null ? <div style={{ fontFamily: sans, fontSize: "var(--text-2xs)", color: "var(--text-muted)", paddingBottom: rows.length ? 4 : 0, whiteSpace: "nowrap" }}>{title}</div> : null}
30
+ {rows.map((r, i) => (
31
+ <div key={i} style={{ display: "flex", alignItems: "center", gap: 6, padding: "1px 0" }}>
32
+ {r.color ? <span style={{ width: 7, height: 7, borderRadius: 2, background: r.color, flexShrink: 0 }} /> : null}
33
+ <span style={{ fontFamily: sans, fontSize: "var(--text-xs)", color: "var(--text-secondary)", whiteSpace: "nowrap" }}>{r.label}</span>
34
+ <span style={{ fontFamily: mono, fontSize: "var(--text-xs)", color: "var(--text-heading)", marginLeft: "auto", paddingLeft: 10 }}>{r.value}</span>
35
+ </div>
36
+ ))}
37
+ </div>
38
+ );
39
+ }
40
+
41
+ /** y-axis tick labels + horizontal gridlines, x category labels. Shared plot furniture. */
42
+ export function PlotGrid({ w, h, pad, ticks, sy, xLabels = [], sxLabel, showAxisLine = false, yFormat = fmtNum }) {
43
+ return (
44
+ <g>
45
+ {ticks.map((t) => (
46
+ <g key={t}>
47
+ <line x1={pad.l} x2={w - pad.r} y1={sy(t)} y2={sy(t)} stroke={t === 0 ? "var(--chart-axis)" : "var(--chart-grid)"} strokeWidth="1" />
48
+ <text x={pad.l - 8} y={sy(t) + 3} textAnchor="end" style={{ fontFamily: mono, fontSize: 10, fill: "var(--chart-label)" }}>{yFormat(t)}</text>
49
+ </g>
50
+ ))}
51
+ {showAxisLine ? <line x1={pad.l} x2={pad.l} y1={pad.t} y2={h - pad.b} stroke="var(--chart-axis)" strokeWidth="1" /> : null}
52
+ {xLabels.map((lb, i) => (lb == null ? null : (
53
+ <text key={i} x={sxLabel(i)} y={h - pad.b + 16} textAnchor="middle" style={{ fontFamily: sans, fontSize: 10, fill: "var(--chart-label)" }}>{lb}</text>
54
+ )))}
55
+ </g>
56
+ );
57
+ }
58
+
59
+ /** Thin x labels only (sm tier / no y axis). */
60
+ export function XLabels({ h, pad, xLabels = [], sxLabel }) {
61
+ return <g>{xLabels.map((lb, i) => (lb == null ? null : <text key={i} x={sxLabel(i)} y={h - pad.b + 16} textAnchor="middle" style={{ fontFamily: sans, fontSize: 10, fill: "var(--chart-label)" }}>{lb}</text>))}</g>;
62
+ }
63
+
64
+ /** Skip x labels so they never collide: keeps ~every nth. */
65
+ export function thinLabels(labels, maxShown = 8) {
66
+ const n = labels.length;
67
+ if (n <= maxShown) return labels;
68
+ const step = Math.ceil(n / maxShown);
69
+ return labels.map((l, i) => (i % step === 0 || i === n - 1 ? l : null));
70
+ }
71
+
72
+ /** Wrapper div a chart mounts into: relative for tooltips, block-level svg. */
73
+ export const wrapStyle = { position: "relative", display: "inline-block", verticalAlign: "top", lineHeight: 0 };
74
+
75
+ /** Namespace handle so the kit can be imported as one symbol. */
76
+ export const ChartKit = { ChartLegend, ChartTooltip, PlotGrid, XLabels, thinLabels, wrapStyle };
@@ -0,0 +1,47 @@
1
+ <script setup>
2
+ import { computed } from 'vue'
3
+ import { SERIES } from './chart-geometry.js'
4
+ /**
5
+ * Shared chart furniture for the Vue charts — one component, two shapes via `kind`
6
+ * ("legend" | "tooltip"), so a single import covers both. The React side exposes these
7
+ * as ChartLegend / ChartTooltip.
8
+ */
9
+ const props = defineProps({
10
+ kind: { type: String, default: 'legend' },
11
+ items: { type: Array, default: () => [] },
12
+ hidden: { type: Array, default: () => [] },
13
+ align: { type: String, default: 'left' },
14
+ x: Number,
15
+ y: Number,
16
+ title: String,
17
+ rows: { type: Array, default: () => [] },
18
+ flip: Boolean,
19
+ style: Object,
20
+ })
21
+ const emit = defineEmits(['toggle'])
22
+ const tipStyle = computed(() => ({
23
+ position: 'absolute', left: props.x + 'px', top: props.y + 'px',
24
+ transform: props.flip ? 'translate(calc(-100% - 10px),-50%)' : 'translate(10px,-50%)',
25
+ pointerEvents: 'none', zIndex: 5, background: 'var(--bg-surface)',
26
+ border: '1px solid var(--border-card)', borderRadius: 'var(--radius-md)',
27
+ boxShadow: 'var(--shadow-menu)', padding: '7px 10px', minWidth: '90px',
28
+ lineHeight: 'normal', whiteSpace: 'nowrap',
29
+ }))
30
+ </script>
31
+ <template>
32
+ <div v-if="kind === 'tooltip'" :style="tipStyle">
33
+ <div v-if="title != null" style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:4px">{{ title }}</div>
34
+ <div v-for="(r,i) in rows" :key="i" style="display:flex;align-items:center;gap:6px;padding:1px 0">
35
+ <span v-if="r.color" :style="{width:'7px',height:'7px',borderRadius:'2px',background:r.color,flexShrink:0}"/>
36
+ <span style="font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)">{{ r.label }}</span>
37
+ <span style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading);margin-left:auto;padding-left:10px">{{ r.value }}</span>
38
+ </div>
39
+ </div>
40
+ <div v-else :style="{display:'flex',flexWrap:'wrap',gap:'4px 14px',justifyContent:align==='right'?'flex-end':'flex-start',lineHeight:'normal',...style}">
41
+ <span v-for="(it,i) in items" :key="it.label" role="button" tabindex="0" @click="emit('toggle', it.label)"
42
+ :style="{display:'inline-flex',alignItems:'center',gap:'6px',cursor:'pointer',opacity:hidden.includes(it.label)?0.4:1,transition:'var(--t-hover)'}">
43
+ <span :style="{width:'8px',height:'8px',borderRadius:'2px',background:it.color||SERIES[i%SERIES.length],flexShrink:0}"/>
44
+ <span :style="{fontFamily:'var(--font-sans)',fontSize:'var(--text-xs)',color:'var(--text-secondary)',textDecoration:hidden.includes(it.label)?'line-through':'none'}">{{ it.label }}</span>
45
+ </span>
46
+ </div>
47
+ </template>
@@ -0,0 +1,13 @@
1
+ import * as React from "react";
2
+ /** Circular Sankey — many-to-many, cyclical flows. matrix[i][j] = flow from i to j. */
3
+ export interface ChordChartProps {
4
+ labels?: string[];
5
+ matrix?: number[][];
6
+ size?: number;
7
+ /** Arc ring thickness */ thickness?: number;
8
+ /** Degrees between arcs */ gap?: number;
9
+ valueFormat?: (v: number) => string;
10
+ interactive?: boolean;
11
+ style?: React.CSSProperties;
12
+ }
13
+ export declare function ChordChart(props: ChordChartProps): JSX.Element;
@@ -0,0 +1,55 @@
1
+ import React from "react";
2
+ import { fmtNum, chord, chordRibbon, arcPath, polar, seriesColor } from "./chart-geometry.js";
3
+ import { ChartTooltip, wrapStyle } from "./ChartKit.jsx";
4
+
5
+ /**
6
+ * Chord diagram — the circular Sankey. A square `matrix` of flows between `labels`
7
+ * (matrix[i][j] = i → j). Use it when flows are many-to-many and cyclical, where a
8
+ * left-to-right Sankey would cross itself. Hovering an arc isolates that node's flows.
9
+ */
10
+ export function ChordChart({ labels = [], matrix = [], size = 380, thickness = 14, gap = 2, valueFormat = fmtNum, interactive = true, style }) {
11
+ const [hover, setHover] = React.useState(null);
12
+ const cx = size / 2, cy = size / 2;
13
+ const rO = size / 2 - 58, rI = rO - thickness;
14
+ const { arcs, ribbons, totals, grand } = React.useMemo(() => chord(matrix, gap), [matrix, gap]);
15
+ const colorOf = (i) => seriesColor(i);
16
+ const lit = (r) => hover == null ? 0.42 : hover.kind === "ribbon" ? (hover.k === r.k ? 0.8 : 0.08) : (r.i === hover.i || r.j === hover.i ? 0.72 : 0.07);
17
+ const keyed = ribbons.map((r, k) => ({ ...r, k }));
18
+ const h = hover;
19
+ const tipRows = () => {
20
+ if (h.kind === "ribbon") {
21
+ const r = keyed[h.k];
22
+ return { title: labels[r.i] + " → " + labels[r.j], rows: [{ label: "flow", value: valueFormat(r.value), color: colorOf(r.i) }, { label: "share", value: ((r.value / grand) * 100).toFixed(1) + "%" }] };
23
+ }
24
+ const out = matrix[h.i].reduce((s, v, j) => s + (j === h.i ? 0 : v), 0);
25
+ const inc = matrix.reduce((s, row, j) => s + (j === h.i ? 0 : row[h.i]), 0);
26
+ return { title: labels[h.i], rows: [{ label: "outgoing", value: valueFormat(out), color: colorOf(h.i) }, { label: "incoming", value: valueFormat(inc) }, { label: "total", value: valueFormat(totals[h.i]) }] };
27
+ };
28
+ const tipAt = h ? polar(cx, cy, rI * 0.55, h.kind === "ribbon" ? (keyed[h.k].a0 + keyed[h.k].b1) / 2 : (arcs[h.i].a0 + arcs[h.i].a1) / 2) : null;
29
+ return (
30
+ <div style={{ ...wrapStyle, ...style }}>
31
+ <svg width={size} height={size} role="img">
32
+ {keyed.map((r) => (
33
+ <path key={r.k} d={chordRibbon(cx, cy, rI - 1, r.a0, r.a1, r.b0, r.b1)} fill={colorOf(r.i)} opacity={lit(r)}
34
+ onMouseEnter={interactive ? () => setHover({ kind: "ribbon", k: r.k }) : undefined} onMouseLeave={interactive ? () => setHover(null) : undefined}
35
+ style={{ transition: "opacity var(--dur-fast) var(--ease-out)" }} />
36
+ ))}
37
+ {arcs.map((a) => {
38
+ const mid = (a.a0 + a.a1) / 2;
39
+ const [lx, ly] = polar(cx, cy, rO + 12, mid);
40
+ const on = hover && hover.kind === "node" && hover.i === a.i;
41
+ const anchor = Math.abs(lx - cx) < 10 ? "middle" : lx > cx ? "start" : "end";
42
+ return (
43
+ <g key={a.i} onMouseEnter={interactive ? () => setHover({ kind: "node", i: a.i }) : undefined} onMouseLeave={interactive ? () => setHover(null) : undefined}
44
+ style={{ cursor: interactive ? "default" : undefined }}>
45
+ <path d={arcPath(cx, cy, rO, rI, a.a0, a.a1)} fill={colorOf(a.i)} opacity={hover == null || on ? 1 : 0.4} style={{ transition: "opacity var(--dur-fast) var(--ease-out)" }} />
46
+ <text x={lx} y={ly + 3} textAnchor={anchor} style={{ fontFamily: "var(--font-sans)", fontSize: 10.5, fill: on ? "var(--text-heading)" : "var(--text-secondary)" }}>{labels[a.i]}</text>
47
+ <text x={lx} y={ly + 14} textAnchor={anchor} style={{ fontFamily: "var(--font-mono)", fontSize: 9, fill: "var(--chart-label)" }}>{valueFormat(a.value)}</text>
48
+ </g>
49
+ );
50
+ })}
51
+ </svg>
52
+ {h && interactive ? <ChartTooltip x={tipAt[0]} y={tipAt[1]} flip={tipAt[0] > size * 0.55} {...tipRows()} /> : null}
53
+ </div>
54
+ );
55
+ }
@@ -0,0 +1,54 @@
1
+ <script setup>
2
+ import { ref, computed } from 'vue'
3
+ import { fmtNum, chord, chordRibbon, arcPath, polar, SERIES } from './chart-geometry.js'
4
+ // Circular Sankey — many-to-many, cyclical flows. matrix[i][j] = flow from i to j.
5
+ const props = defineProps({ labels:{type:Array,default:()=>[]}, matrix:{type:Array,default:()=>[]}, size:{type:Number,default:380}, thickness:{type:Number,default:14}, gap:{type:Number,default:2}, valueFormat:{type:Function,default:fmtNum}, interactive:{type:Boolean,default:true}, style:Object })
6
+ const hover = ref(null)
7
+ const g = computed(() => {
8
+ const cx = props.size/2, cy = props.size/2
9
+ const rO = props.size/2-58, rI = rO-props.thickness
10
+ const { arcs, ribbons, totals, grand } = chord(props.matrix, props.gap)
11
+ return { cx, cy, rO, rI, grand, totals,
12
+ arcs: arcs.map(a=>{
13
+ const mid=(a.a0+a.a1)/2, l=polar(cx,cy,rO+12,mid)
14
+ return Object.assign({}, a, { mid, d: arcPath(cx,cy,rO,rI,a.a0,a.a1), color: SERIES[a.i%SERIES.length],
15
+ lx:l[0], ly:l[1], anchor: Math.abs(l[0]-cx)<10 ? 'middle' : (l[0]>cx?'start':'end') })
16
+ }),
17
+ ribbons: ribbons.map((r,k)=>Object.assign({}, r, { k, d: chordRibbon(cx,cy,rI-1,r.a0,r.a1,r.b0,r.b1), color: SERIES[r.i%SERIES.length] })) }
18
+ })
19
+ function op(r){
20
+ const h = hover.value
21
+ if (!h) return 0.42
22
+ return h.kind==='ribbon' ? (h.k===r.k?0.8:0.08) : ((r.i===h.i||r.j===h.i)?0.72:0.07)
23
+ }
24
+ const tip = computed(()=>{
25
+ const h = hover.value
26
+ if (!h) return null
27
+ if (h.kind==='ribbon') {
28
+ const r = g.value.ribbons[h.k], p = polar(g.value.cx,g.value.cy,g.value.rI*0.55,(r.a0+r.b1)/2)
29
+ return { x:p[0], y:p[1], title: props.labels[r.i]+' → '+props.labels[r.j], value: props.valueFormat(r.value)+' · '+((r.value/g.value.grand)*100).toFixed(1)+'%' }
30
+ }
31
+ const a = g.value.arcs[h.i], p = polar(g.value.cx,g.value.cy,g.value.rI*0.55,a.mid)
32
+ const out = props.matrix[h.i].reduce((s,v,j)=>s+(j===h.i?0:v),0)
33
+ const inc = props.matrix.reduce((s,row,j)=>s+(j===h.i?0:row[h.i]),0)
34
+ return { x:p[0], y:p[1], title: props.labels[h.i], value: 'out '+props.valueFormat(out)+' · in '+props.valueFormat(inc) }
35
+ })
36
+ </script>
37
+ <template>
38
+ <div :style="{position:'relative',display:'inline-block',lineHeight:0,...style}">
39
+ <svg :width="size" :height="size" role="img">
40
+ <path v-for="r in g.ribbons" :key="r.k" :d="r.d" :fill="r.color" :opacity="op(r)"
41
+ @mouseenter="interactive && (hover={kind:'ribbon',k:r.k})" @mouseleave="interactive && (hover=null)"
42
+ style="transition:opacity var(--dur-fast) var(--ease-out)"/>
43
+ <g v-for="a in g.arcs" :key="a.i" @mouseenter="interactive && (hover={kind:'node',i:a.i})" @mouseleave="interactive && (hover=null)">
44
+ <path :d="a.d" :fill="a.color" :opacity="hover==null||(hover.kind==='node'&&hover.i===a.i)?1:0.4" style="transition:opacity var(--dur-fast) var(--ease-out)"/>
45
+ <text :x="a.lx" :y="a.ly+3" :text-anchor="a.anchor" style="font-family:var(--font-sans);font-size:10.5px;fill:var(--text-secondary)">{{ labels[a.i] }}</text>
46
+ <text :x="a.lx" :y="a.ly+14" :text-anchor="a.anchor" style="font-family:var(--font-mono);font-size:9px;fill:var(--chart-label)">{{ valueFormat(a.value) }}</text>
47
+ </g>
48
+ </svg>
49
+ <div v-if="tip && interactive" :style="{position:'absolute',left:tip.x+'px',top:tip.y+'px',transform:tip.x>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'}">
50
+ <div style="font-family:var(--font-sans);font-size:var(--text-2xs);color:var(--text-muted);padding-bottom:3px">{{ tip.title }}</div>
51
+ <div style="font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-heading)">{{ tip.value }}</div>
52
+ </div>
53
+ </div>
54
+ </template>