@voila.dev/chart 4.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 (377) hide show
  1. package/README.md +85 -0
  2. package/dist/brush/brush-layer.d.ts +16 -0
  3. package/dist/brush/brush-layer.d.ts.map +1 -0
  4. package/dist/brush/brush-layer.js +137 -0
  5. package/dist/brush/brush-layer.js.map +7 -0
  6. package/dist/brush/index.d.ts +10 -0
  7. package/dist/brush/index.d.ts.map +1 -0
  8. package/dist/brush/index.js +8 -0
  9. package/dist/brush/index.js.map +7 -0
  10. package/dist/canvas/canvas-renderer.d.ts +11 -0
  11. package/dist/canvas/canvas-renderer.d.ts.map +1 -0
  12. package/dist/canvas/canvas-renderer.js +49 -0
  13. package/dist/canvas/canvas-renderer.js.map +7 -0
  14. package/dist/canvas/index.d.ts +2 -0
  15. package/dist/canvas/index.d.ts.map +1 -0
  16. package/dist/canvas/index.js +5 -0
  17. package/dist/canvas/index.js.map +7 -0
  18. package/dist/canvas/paint-scene.d.ts +4 -0
  19. package/dist/canvas/paint-scene.d.ts.map +1 -0
  20. package/dist/canvas/paint-scene.js +130 -0
  21. package/dist/canvas/paint-scene.js.map +7 -0
  22. package/dist/canvas/resolve-color.d.ts +18 -0
  23. package/dist/canvas/resolve-color.d.ts.map +1 -0
  24. package/dist/canvas/resolve-color.js +52 -0
  25. package/dist/canvas/resolve-color.js.map +7 -0
  26. package/dist/contour/density.d.ts +20 -0
  27. package/dist/contour/density.d.ts.map +1 -0
  28. package/dist/contour/density.js +59 -0
  29. package/dist/contour/density.js.map +7 -0
  30. package/dist/contour/index.d.ts +2 -0
  31. package/dist/contour/index.d.ts.map +1 -0
  32. package/dist/contour/index.js +5 -0
  33. package/dist/contour/index.js.map +7 -0
  34. package/dist/core/channel.d.ts +11 -0
  35. package/dist/core/channel.d.ts.map +1 -0
  36. package/dist/core/channel.js +44 -0
  37. package/dist/core/channel.js.map +7 -0
  38. package/dist/core/compile.d.ts +22 -0
  39. package/dist/core/compile.d.ts.map +1 -0
  40. package/dist/core/compile.js +295 -0
  41. package/dist/core/compile.js.map +7 -0
  42. package/dist/core/define-chart.d.ts +96 -0
  43. package/dist/core/define-chart.d.ts.map +1 -0
  44. package/dist/core/define-chart.js +7 -0
  45. package/dist/core/define-chart.js.map +7 -0
  46. package/dist/core/facet.d.ts +7 -0
  47. package/dist/core/facet.d.ts.map +1 -0
  48. package/dist/core/facet.js +134 -0
  49. package/dist/core/facet.js.map +7 -0
  50. package/dist/core/format.d.ts +37 -0
  51. package/dist/core/format.d.ts.map +1 -0
  52. package/dist/core/format.js +108 -0
  53. package/dist/core/format.js.map +7 -0
  54. package/dist/core/guides/axes.d.ts +25 -0
  55. package/dist/core/guides/axes.d.ts.map +1 -0
  56. package/dist/core/guides/axes.js +204 -0
  57. package/dist/core/guides/axes.js.map +7 -0
  58. package/dist/core/marks/area.d.ts +23 -0
  59. package/dist/core/marks/area.d.ts.map +1 -0
  60. package/dist/core/marks/area.js +187 -0
  61. package/dist/core/marks/area.js.map +7 -0
  62. package/dist/core/marks/bar.d.ts +26 -0
  63. package/dist/core/marks/bar.d.ts.map +1 -0
  64. package/dist/core/marks/bar.js +200 -0
  65. package/dist/core/marks/bar.js.map +7 -0
  66. package/dist/core/marks/cell.d.ts +16 -0
  67. package/dist/core/marks/cell.d.ts.map +1 -0
  68. package/dist/core/marks/cell.js +98 -0
  69. package/dist/core/marks/cell.js.map +7 -0
  70. package/dist/core/marks/dot.d.ts +17 -0
  71. package/dist/core/marks/dot.d.ts.map +1 -0
  72. package/dist/core/marks/dot.js +125 -0
  73. package/dist/core/marks/dot.js.map +7 -0
  74. package/dist/core/marks/funnel-layout.d.ts +37 -0
  75. package/dist/core/marks/funnel-layout.d.ts.map +1 -0
  76. package/dist/core/marks/funnel-layout.js +61 -0
  77. package/dist/core/marks/funnel-layout.js.map +7 -0
  78. package/dist/core/marks/funnel.d.ts +19 -0
  79. package/dist/core/marks/funnel.d.ts.map +1 -0
  80. package/dist/core/marks/funnel.js +143 -0
  81. package/dist/core/marks/funnel.js.map +7 -0
  82. package/dist/core/marks/line.d.ts +17 -0
  83. package/dist/core/marks/line.d.ts.map +1 -0
  84. package/dist/core/marks/line.js +150 -0
  85. package/dist/core/marks/line.js.map +7 -0
  86. package/dist/core/marks/polar.d.ts +61 -0
  87. package/dist/core/marks/polar.d.ts.map +1 -0
  88. package/dist/core/marks/polar.js +436 -0
  89. package/dist/core/marks/polar.js.map +7 -0
  90. package/dist/core/marks/rect.d.ts +22 -0
  91. package/dist/core/marks/rect.d.ts.map +1 -0
  92. package/dist/core/marks/rect.js +119 -0
  93. package/dist/core/marks/rect.js.map +7 -0
  94. package/dist/core/marks/rule.d.ts +17 -0
  95. package/dist/core/marks/rule.d.ts.map +1 -0
  96. package/dist/core/marks/rule.js +73 -0
  97. package/dist/core/marks/rule.js.map +7 -0
  98. package/dist/core/marks/shared.d.ts +41 -0
  99. package/dist/core/marks/shared.d.ts.map +1 -0
  100. package/dist/core/marks/shared.js +74 -0
  101. package/dist/core/marks/shared.js.map +7 -0
  102. package/dist/core/marks/stack.d.ts +13 -0
  103. package/dist/core/marks/stack.d.ts.map +1 -0
  104. package/dist/core/marks/stack.js +28 -0
  105. package/dist/core/marks/stack.js.map +7 -0
  106. package/dist/core/marks/stats/difference.d.ts +20 -0
  107. package/dist/core/marks/stats/difference.d.ts.map +1 -0
  108. package/dist/core/marks/stats/difference.js +168 -0
  109. package/dist/core/marks/stats/difference.js.map +7 -0
  110. package/dist/core/marks/stats/distribution.d.ts +41 -0
  111. package/dist/core/marks/stats/distribution.d.ts.map +1 -0
  112. package/dist/core/marks/stats/distribution.js +354 -0
  113. package/dist/core/marks/stats/distribution.js.map +7 -0
  114. package/dist/core/marks/stats/dodge.d.ts +16 -0
  115. package/dist/core/marks/stats/dodge.d.ts.map +1 -0
  116. package/dist/core/marks/stats/dodge.js +104 -0
  117. package/dist/core/marks/stats/dodge.js.map +7 -0
  118. package/dist/core/marks/stats/hexbin.d.ts +26 -0
  119. package/dist/core/marks/stats/hexbin.d.ts.map +1 -0
  120. package/dist/core/marks/stats/hexbin.js +113 -0
  121. package/dist/core/marks/stats/hexbin.js.map +7 -0
  122. package/dist/core/marks/stats/histogram.d.ts +27 -0
  123. package/dist/core/marks/stats/histogram.d.ts.map +1 -0
  124. package/dist/core/marks/stats/histogram.js +70 -0
  125. package/dist/core/marks/stats/histogram.js.map +7 -0
  126. package/dist/core/marks/stats/regression.d.ts +13 -0
  127. package/dist/core/marks/stats/regression.d.ts.map +1 -0
  128. package/dist/core/marks/stats/regression.js +80 -0
  129. package/dist/core/marks/stats/regression.js.map +7 -0
  130. package/dist/core/marks/stats/statistics.d.ts +34 -0
  131. package/dist/core/marks/stats/statistics.d.ts.map +1 -0
  132. package/dist/core/marks/stats/statistics.js +111 -0
  133. package/dist/core/marks/stats/statistics.js.map +7 -0
  134. package/dist/core/marks/stats/waffle.d.ts +18 -0
  135. package/dist/core/marks/stats/waffle.d.ts.map +1 -0
  136. package/dist/core/marks/stats/waffle.js +117 -0
  137. package/dist/core/marks/stats/waffle.js.map +7 -0
  138. package/dist/core/marks/text.d.ts +19 -0
  139. package/dist/core/marks/text.d.ts.map +1 -0
  140. package/dist/core/marks/text.js +60 -0
  141. package/dist/core/marks/text.js.map +7 -0
  142. package/dist/core/motion/tween.d.ts +6 -0
  143. package/dist/core/motion/tween.d.ts.map +1 -0
  144. package/dist/core/motion/tween.js +98 -0
  145. package/dist/core/motion/tween.js.map +7 -0
  146. package/dist/core/nearest.d.ts +25 -0
  147. package/dist/core/nearest.d.ts.map +1 -0
  148. package/dist/core/nearest.js +151 -0
  149. package/dist/core/nearest.js.map +7 -0
  150. package/dist/core/paths.d.ts +65 -0
  151. package/dist/core/paths.d.ts.map +1 -0
  152. package/dist/core/paths.js +192 -0
  153. package/dist/core/paths.js.map +7 -0
  154. package/dist/core/scales/color.d.ts +11 -0
  155. package/dist/core/scales/color.d.ts.map +1 -0
  156. package/dist/core/scales/color.js +35 -0
  157. package/dist/core/scales/color.js.map +7 -0
  158. package/dist/core/scales/continuous.d.ts +18 -0
  159. package/dist/core/scales/continuous.d.ts.map +1 -0
  160. package/dist/core/scales/continuous.js +82 -0
  161. package/dist/core/scales/continuous.js.map +7 -0
  162. package/dist/core/scales/discrete.d.ts +22 -0
  163. package/dist/core/scales/discrete.d.ts.map +1 -0
  164. package/dist/core/scales/discrete.js +103 -0
  165. package/dist/core/scales/discrete.js.map +7 -0
  166. package/dist/core/scales/resolve.d.ts +18 -0
  167. package/dist/core/scales/resolve.d.ts.map +1 -0
  168. package/dist/core/scales/resolve.js +112 -0
  169. package/dist/core/scales/resolve.js.map +7 -0
  170. package/dist/core/scales/time.d.ts +4 -0
  171. package/dist/core/scales/time.d.ts.map +1 -0
  172. package/dist/core/scales/time.js +140 -0
  173. package/dist/core/scales/time.js.map +7 -0
  174. package/dist/core/text.d.ts +3 -0
  175. package/dist/core/text.d.ts.map +1 -0
  176. package/dist/core/text.js +29 -0
  177. package/dist/core/text.js.map +7 -0
  178. package/dist/core/theme.d.ts +8 -0
  179. package/dist/core/theme.d.ts.map +1 -0
  180. package/dist/core/theme.js +21 -0
  181. package/dist/core/theme.js.map +7 -0
  182. package/dist/core/ticks.d.ts +27 -0
  183. package/dist/core/ticks.d.ts.map +1 -0
  184. package/dist/core/ticks.js +76 -0
  185. package/dist/core/ticks.js.map +7 -0
  186. package/dist/core/types.d.ts +305 -0
  187. package/dist/core/types.d.ts.map +1 -0
  188. package/dist/core/types.js +1 -0
  189. package/dist/core/types.js.map +7 -0
  190. package/dist/force/force.d.ts +30 -0
  191. package/dist/force/force.d.ts.map +1 -0
  192. package/dist/force/force.js +143 -0
  193. package/dist/force/force.js.map +7 -0
  194. package/dist/force/index.d.ts +2 -0
  195. package/dist/force/index.d.ts.map +1 -0
  196. package/dist/force/index.js +7 -0
  197. package/dist/force/index.js.map +7 -0
  198. package/dist/geo/geo.d.ts +59 -0
  199. package/dist/geo/geo.d.ts.map +1 -0
  200. package/dist/geo/geo.js +231 -0
  201. package/dist/geo/geo.js.map +7 -0
  202. package/dist/geo/index.d.ts +2 -0
  203. package/dist/geo/index.d.ts.map +1 -0
  204. package/dist/geo/index.js +13 -0
  205. package/dist/geo/index.js.map +7 -0
  206. package/dist/hierarchy/hierarchy.d.ts +24 -0
  207. package/dist/hierarchy/hierarchy.d.ts.map +1 -0
  208. package/dist/hierarchy/hierarchy.js +279 -0
  209. package/dist/hierarchy/hierarchy.js.map +7 -0
  210. package/dist/hierarchy/index.d.ts +2 -0
  211. package/dist/hierarchy/index.d.ts.map +1 -0
  212. package/dist/hierarchy/index.js +11 -0
  213. package/dist/hierarchy/index.js.map +7 -0
  214. package/dist/index.d.ts +29 -0
  215. package/dist/index.d.ts.map +1 -0
  216. package/dist/index.js +96 -0
  217. package/dist/index.js.map +7 -0
  218. package/dist/react/brush-contract.d.ts +23 -0
  219. package/dist/react/brush-contract.d.ts.map +1 -0
  220. package/dist/react/brush-contract.js +7 -0
  221. package/dist/react/brush-contract.js.map +7 -0
  222. package/dist/react/chart-data-table.d.ts +18 -0
  223. package/dist/react/chart-data-table.d.ts.map +1 -0
  224. package/dist/react/chart-data-table.js +55 -0
  225. package/dist/react/chart-data-table.js.map +7 -0
  226. package/dist/react/chart-legend.d.ts +17 -0
  227. package/dist/react/chart-legend.d.ts.map +1 -0
  228. package/dist/react/chart-legend.js +126 -0
  229. package/dist/react/chart-legend.js.map +7 -0
  230. package/dist/react/chart-tooltip.d.ts +18 -0
  231. package/dist/react/chart-tooltip.d.ts.map +1 -0
  232. package/dist/react/chart-tooltip.js +95 -0
  233. package/dist/react/chart-tooltip.js.map +7 -0
  234. package/dist/react/chart.d.ts +45 -0
  235. package/dist/react/chart.d.ts.map +1 -0
  236. package/dist/react/chart.js +383 -0
  237. package/dist/react/chart.js.map +7 -0
  238. package/dist/react/focus-overlay.d.ts +14 -0
  239. package/dist/react/focus-overlay.d.ts.map +1 -0
  240. package/dist/react/focus-overlay.js +147 -0
  241. package/dist/react/focus-overlay.js.map +7 -0
  242. package/dist/react/index.d.ts +7 -0
  243. package/dist/react/index.d.ts.map +1 -0
  244. package/dist/react/index.js +11 -0
  245. package/dist/react/index.js.map +7 -0
  246. package/dist/react/messages.d.ts +16 -0
  247. package/dist/react/messages.d.ts.map +1 -0
  248. package/dist/react/messages.js +33 -0
  249. package/dist/react/messages.js.map +7 -0
  250. package/dist/react/renderer.d.ts +14 -0
  251. package/dist/react/renderer.d.ts.map +1 -0
  252. package/dist/react/renderer.js +1 -0
  253. package/dist/react/renderer.js.map +7 -0
  254. package/dist/react/sr-only.d.ts +4 -0
  255. package/dist/react/sr-only.d.ts.map +1 -0
  256. package/dist/react/sr-only.js +15 -0
  257. package/dist/react/sr-only.js.map +7 -0
  258. package/dist/react/svg-renderer.d.ts +4 -0
  259. package/dist/react/svg-renderer.d.ts.map +1 -0
  260. package/dist/react/svg-renderer.js +20 -0
  261. package/dist/react/svg-renderer.js.map +7 -0
  262. package/dist/react/svg-scene.d.ts +8 -0
  263. package/dist/react/svg-scene.d.ts.map +1 -0
  264. package/dist/react/svg-scene.js +121 -0
  265. package/dist/react/svg-scene.js.map +7 -0
  266. package/dist/react/use-animated-scene.d.ts +10 -0
  267. package/dist/react/use-animated-scene.d.ts.map +1 -0
  268. package/dist/react/use-animated-scene.js +41 -0
  269. package/dist/react/use-animated-scene.js.map +7 -0
  270. package/dist/react/use-chart-focus.d.ts +29 -0
  271. package/dist/react/use-chart-focus.d.ts.map +1 -0
  272. package/dist/react/use-chart-focus.js +180 -0
  273. package/dist/react/use-chart-focus.js.map +7 -0
  274. package/dist/react/use-chart-width.d.ts +12 -0
  275. package/dist/react/use-chart-width.d.ts.map +1 -0
  276. package/dist/react/use-chart-width.js +23 -0
  277. package/dist/react/use-chart-width.js.map +7 -0
  278. package/dist/react/use-chart-zoom.d.ts +13 -0
  279. package/dist/react/use-chart-zoom.d.ts.map +1 -0
  280. package/dist/react/use-chart-zoom.js +52 -0
  281. package/dist/react/use-chart-zoom.js.map +7 -0
  282. package/dist/react/use-text-measurer.d.ts +8 -0
  283. package/dist/react/use-text-measurer.d.ts.map +1 -0
  284. package/dist/react/use-text-measurer.js +44 -0
  285. package/dist/react/use-text-measurer.js.map +7 -0
  286. package/dist/sankey/index.d.ts +2 -0
  287. package/dist/sankey/index.d.ts.map +1 -0
  288. package/dist/sankey/index.js +7 -0
  289. package/dist/sankey/index.js.map +7 -0
  290. package/dist/sankey/sankey.d.ts +29 -0
  291. package/dist/sankey/sankey.d.ts.map +1 -0
  292. package/dist/sankey/sankey.js +127 -0
  293. package/dist/sankey/sankey.js.map +7 -0
  294. package/dist/voronoi/index.d.ts +2 -0
  295. package/dist/voronoi/index.d.ts.map +1 -0
  296. package/dist/voronoi/index.js +5 -0
  297. package/dist/voronoi/index.js.map +7 -0
  298. package/dist/voronoi/voronoi.d.ts +16 -0
  299. package/dist/voronoi/voronoi.d.ts.map +1 -0
  300. package/dist/voronoi/voronoi.js +62 -0
  301. package/dist/voronoi/voronoi.js.map +7 -0
  302. package/package.json +74 -0
  303. package/src/brush/brush-layer.tsx +174 -0
  304. package/src/brush/index.ts +14 -0
  305. package/src/canvas/canvas-renderer.tsx +56 -0
  306. package/src/canvas/index.ts +1 -0
  307. package/src/canvas/paint-scene.ts +152 -0
  308. package/src/canvas/resolve-color.ts +65 -0
  309. package/src/contour/density.ts +90 -0
  310. package/src/contour/index.ts +1 -0
  311. package/src/core/channel.ts +53 -0
  312. package/src/core/compile.ts +436 -0
  313. package/src/core/define-chart.ts +128 -0
  314. package/src/core/facet.ts +189 -0
  315. package/src/core/format.ts +163 -0
  316. package/src/core/guides/axes.ts +283 -0
  317. package/src/core/marks/area.ts +238 -0
  318. package/src/core/marks/bar.ts +278 -0
  319. package/src/core/marks/cell.ts +123 -0
  320. package/src/core/marks/dot.ts +157 -0
  321. package/src/core/marks/funnel-layout.ts +113 -0
  322. package/src/core/marks/funnel.ts +172 -0
  323. package/src/core/marks/line.ts +206 -0
  324. package/src/core/marks/polar.ts +568 -0
  325. package/src/core/marks/rect.ts +156 -0
  326. package/src/core/marks/rule.ts +104 -0
  327. package/src/core/marks/shared.ts +134 -0
  328. package/src/core/marks/stack.ts +42 -0
  329. package/src/core/marks/stats/difference.ts +205 -0
  330. package/src/core/marks/stats/distribution.ts +425 -0
  331. package/src/core/marks/stats/dodge.ts +136 -0
  332. package/src/core/marks/stats/hexbin.ts +152 -0
  333. package/src/core/marks/stats/histogram.ts +108 -0
  334. package/src/core/marks/stats/regression.ts +108 -0
  335. package/src/core/marks/stats/statistics.ts +153 -0
  336. package/src/core/marks/stats/waffle.ts +141 -0
  337. package/src/core/marks/text.ts +97 -0
  338. package/src/core/motion/tween.ts +131 -0
  339. package/src/core/nearest.ts +223 -0
  340. package/src/core/paths.ts +323 -0
  341. package/src/core/scales/color.ts +51 -0
  342. package/src/core/scales/continuous.ts +111 -0
  343. package/src/core/scales/discrete.ts +156 -0
  344. package/src/core/scales/resolve.ts +158 -0
  345. package/src/core/scales/time.ts +180 -0
  346. package/src/core/text.ts +42 -0
  347. package/src/core/theme.ts +26 -0
  348. package/src/core/ticks.ts +118 -0
  349. package/src/core/types.ts +358 -0
  350. package/src/force/force.ts +200 -0
  351. package/src/force/index.ts +6 -0
  352. package/src/geo/geo.ts +346 -0
  353. package/src/geo/index.ts +10 -0
  354. package/src/hierarchy/hierarchy.ts +377 -0
  355. package/src/hierarchy/index.ts +6 -0
  356. package/src/index.ts +70 -0
  357. package/src/react/brush-contract.ts +35 -0
  358. package/src/react/chart-data-table.tsx +98 -0
  359. package/src/react/chart-legend.tsx +130 -0
  360. package/src/react/chart-tooltip.tsx +111 -0
  361. package/src/react/chart.tsx +489 -0
  362. package/src/react/focus-overlay.tsx +164 -0
  363. package/src/react/index.ts +9 -0
  364. package/src/react/messages.ts +49 -0
  365. package/src/react/renderer.ts +15 -0
  366. package/src/react/sr-only.ts +14 -0
  367. package/src/react/svg-renderer.tsx +18 -0
  368. package/src/react/svg-scene.tsx +146 -0
  369. package/src/react/use-animated-scene.ts +57 -0
  370. package/src/react/use-chart-focus.ts +245 -0
  371. package/src/react/use-chart-width.ts +27 -0
  372. package/src/react/use-chart-zoom.ts +67 -0
  373. package/src/react/use-text-measurer.ts +49 -0
  374. package/src/sankey/index.ts +6 -0
  375. package/src/sankey/sankey.ts +179 -0
  376. package/src/voronoi/index.ts +1 -0
  377. package/src/voronoi/voronoi.ts +84 -0
@@ -0,0 +1,238 @@
1
+ import {
2
+ channelLabel,
3
+ isPlaceable,
4
+ numericValue,
5
+ readChannel,
6
+ } from "#/core/channel.ts";
7
+ import {
8
+ type ChartMarkOptions,
9
+ colorChannel,
10
+ groupBySeries,
11
+ markId,
12
+ paint,
13
+ seriesResolver,
14
+ } from "#/core/marks/shared.ts";
15
+ import { stackSegments } from "#/core/marks/stack.ts";
16
+ import { areaPath, type ChartXY, linePath } from "#/core/paths.ts";
17
+ import { categoryKey } from "#/core/scales/discrete.ts";
18
+ import type {
19
+ ChartAccessor,
20
+ ChartCurve,
21
+ ChartMark,
22
+ ChartPoint,
23
+ ChartValue,
24
+ SceneNode,
25
+ } from "#/core/types.ts";
26
+
27
+ export interface AreaOptions<TDatum> extends ChartMarkOptions<TDatum> {
28
+ readonly x?: ChartAccessor<TDatum, ChartValue>;
29
+ readonly y?: ChartAccessor<TDatum, ChartValue>;
30
+ /** Explicit lower and upper edges: a confidence band, a min-max range. */
31
+ readonly from?: ChartAccessor<TDatum, ChartValue>;
32
+ readonly to?: ChartAccessor<TDatum, ChartValue>;
33
+ readonly fill?: string;
34
+ readonly fillOpacity?: number;
35
+ /** Draw the upper edge as a line in the series colour. */
36
+ readonly line?: boolean;
37
+ readonly strokeWidth?: number;
38
+ readonly strokeDasharray?: string;
39
+ readonly curve?: ChartCurve;
40
+ /** Series stack on one another. On by default when there is a color channel. */
41
+ readonly stack?: boolean;
42
+ }
43
+
44
+ const DEFAULT_FILL_OPACITY = 0.3;
45
+ const DEFAULT_STROKE_WIDTH = 2;
46
+
47
+ function areaMark<TDatum>(
48
+ kind: "areaY" | "areaX",
49
+ data: ReadonlyArray<TDatum>,
50
+ options: AreaOptions<TDatum>,
51
+ ): ChartMark {
52
+ const vertical = kind === "areaY";
53
+ const positionKey = vertical ? options.x : options.y;
54
+ const valueKey = vertical ? options.y : options.x;
55
+ const positions = readChannel(
56
+ data,
57
+ positionKey ?? ((_datum: TDatum, index: number) => index),
58
+ );
59
+ const values = readChannel(
60
+ data,
61
+ valueKey ?? ((datum: TDatum) => datum as unknown as ChartValue),
62
+ ).map(numericValue);
63
+ const colors = colorChannel(data, options);
64
+ const explicit = options.from !== undefined && options.to !== undefined;
65
+ const froms = readChannel(data, options.from).map(numericValue);
66
+ const tos = readChannel(data, options.to).map(numericValue);
67
+ const stacked = !explicit && (options.stack ?? options.color !== undefined);
68
+ const segments = stacked
69
+ ? stackSegments(
70
+ positions.map((position) =>
71
+ isPlaceable(position) ? categoryKey(position) : undefined,
72
+ ),
73
+ values,
74
+ )
75
+ : [];
76
+ const edges = positions.map((position, index) => {
77
+ if (!isPlaceable(position)) {
78
+ return undefined;
79
+ }
80
+ if (explicit) {
81
+ const low = froms[index];
82
+ const high = tos[index];
83
+ return low === undefined || high === undefined
84
+ ? undefined
85
+ : { low, high };
86
+ }
87
+ const value = values[index];
88
+ if (value === undefined) {
89
+ return undefined;
90
+ }
91
+ return segments[index] ?? { low: 0, high: value };
92
+ });
93
+
94
+ const positionChannel = {
95
+ values: positions.filter(isPlaceable),
96
+ label: channelLabel(positionKey),
97
+ };
98
+ const valueChannel = {
99
+ values: edges.flatMap((edge) => (edge ? [edge.low, edge.high] : [])),
100
+ includeZero: !explicit,
101
+ label: channelLabel(valueKey) ?? options.label,
102
+ };
103
+
104
+ return {
105
+ id: options.id,
106
+ coordinate: "cartesian",
107
+ focusOrder: vertical ? "x" : "y",
108
+ channels: {
109
+ x: vertical ? positionChannel : valueChannel,
110
+ y: vertical ? valueChannel : positionChannel,
111
+ color: colors.channel,
112
+ },
113
+ render(context) {
114
+ const id = markId(options.id, kind, context);
115
+ const positionScale = vertical ? context.scales.x : context.scales.y;
116
+ const valueScale = vertical ? context.scales.y : context.scales.x;
117
+ if (positionScale === undefined || valueScale === undefined) {
118
+ return { nodes: [] };
119
+ }
120
+ const seriesOf = seriesResolver(
121
+ colors.values,
122
+ { ...options, fixedColor: options.fill },
123
+ id,
124
+ context,
125
+ );
126
+ function place(along: number, value: number): ChartXY {
127
+ const across = valueScale?.map(value) ?? 0;
128
+ return vertical ? { x: along, y: across } : { x: across, y: along };
129
+ }
130
+
131
+ const nodes: SceneNode[] = [];
132
+ const points: ChartPoint[] = [];
133
+ for (const { series, indices } of groupBySeries(
134
+ data.length,
135
+ seriesOf,
136
+ ).values()) {
137
+ const runs: Array<{ top: ChartXY[]; bottom: ChartXY[] }> = [
138
+ { top: [], bottom: [] },
139
+ ];
140
+ for (const index of indices) {
141
+ const position = positions[index];
142
+ const edge = edges[index];
143
+ if (!isPlaceable(position) || edge === undefined) {
144
+ runs.push({ top: [], bottom: [] });
145
+ continue;
146
+ }
147
+ const along = positionScale.center(position);
148
+ const top = place(along, edge.high);
149
+ const run = runs[runs.length - 1];
150
+ run.top.push(top);
151
+ run.bottom.push(place(along, edge.low));
152
+ if (options.tip === false) {
153
+ continue;
154
+ }
155
+ const value = explicit ? edge.high : (values[index] ?? 0);
156
+ points.push({
157
+ key: `${id}:${index}`,
158
+ markId: id,
159
+ index,
160
+ datum: data[index],
161
+ x: top.x,
162
+ y: top.y,
163
+ xValue: vertical ? position : value,
164
+ yValue: vertical ? value : position,
165
+ series: series.key,
166
+ seriesLabel: series.label,
167
+ color: series.color,
168
+ title: vertical
169
+ ? context.formatX(position)
170
+ : context.formatY(position),
171
+ value: vertical ? context.formatY(value) : context.formatX(value),
172
+ });
173
+ }
174
+ const shaped = runs.filter((run) => run.top.length > 0);
175
+ nodes.push({
176
+ kind: "path",
177
+ key: `${id}:area:${series.key}`,
178
+ series: series.key,
179
+ role: "mark",
180
+ d: shaped
181
+ .map((run) => areaPath(run.top, run.bottom, options.curve))
182
+ .join(""),
183
+ paint: paint({
184
+ fill: series.color,
185
+ fillOpacity: options.fillOpacity ?? DEFAULT_FILL_OPACITY,
186
+ opacity: options.opacity,
187
+ }),
188
+ });
189
+ if (options.line) {
190
+ nodes.push({
191
+ kind: "path",
192
+ key: `${id}:line:${series.key}`,
193
+ series: series.key,
194
+ role: "mark",
195
+ d: shaped.map((run) => linePath(run.top, options.curve)).join(""),
196
+ paint: paint({
197
+ fill: "none",
198
+ stroke: series.color,
199
+ strokeWidth: options.strokeWidth ?? DEFAULT_STROKE_WIDTH,
200
+ strokeDasharray: options.strokeDasharray,
201
+ strokeLinecap: "round",
202
+ strokeLinejoin: "round",
203
+ opacity: options.opacity,
204
+ }),
205
+ });
206
+ }
207
+ }
208
+ const legend =
209
+ options.color === undefined && options.label !== undefined
210
+ ? [
211
+ {
212
+ key: id,
213
+ label: options.label,
214
+ color: options.fill ?? context.paletteColor(context.markIndex),
215
+ shape: "square" as const,
216
+ },
217
+ ]
218
+ : [];
219
+ return { nodes, points, legend };
220
+ },
221
+ };
222
+ }
223
+
224
+ /** A filled band under a series, over x positions. */
225
+ export function areaY<TDatum>(
226
+ data: ReadonlyArray<TDatum>,
227
+ options: AreaOptions<TDatum> = {},
228
+ ): ChartMark {
229
+ return areaMark("areaY", data, options);
230
+ }
231
+
232
+ /** A filled band along x, over y positions. */
233
+ export function areaX<TDatum>(
234
+ data: ReadonlyArray<TDatum>,
235
+ options: AreaOptions<TDatum> = {},
236
+ ): ChartMark {
237
+ return areaMark("areaX", data, options);
238
+ }
@@ -0,0 +1,278 @@
1
+ import {
2
+ channelLabel,
3
+ isPlaceable,
4
+ numericValue,
5
+ readChannel,
6
+ } from "#/core/channel.ts";
7
+ import {
8
+ type ChartMarkOptions,
9
+ colorChannel,
10
+ markId,
11
+ paint,
12
+ seriesResolver,
13
+ } from "#/core/marks/shared.ts";
14
+ import { stackSegments } from "#/core/marks/stack.ts";
15
+ import { categoryKey } from "#/core/scales/discrete.ts";
16
+ import type {
17
+ ChartAccessor,
18
+ ChartCorners,
19
+ ChartMark,
20
+ ChartPoint,
21
+ ChartValue,
22
+ SceneNode,
23
+ } from "#/core/types.ts";
24
+
25
+ export interface BarOptions<TDatum> extends ChartMarkOptions<TDatum> {
26
+ readonly x?: ChartAccessor<TDatum, ChartValue>;
27
+ readonly y?: ChartAccessor<TDatum, ChartValue>;
28
+ /** Explicit start and end of the bar on the value axis: floating bars, ranges, Gantt rows. */
29
+ readonly from?: ChartAccessor<TDatum, ChartValue>;
30
+ readonly to?: ChartAccessor<TDatum, ChartValue>;
31
+ readonly fill?: string;
32
+ readonly fillOpacity?: number;
33
+ readonly stroke?: string;
34
+ readonly strokeDasharray?: string;
35
+ /** Series stack on one another. On by default when there is a color channel. */
36
+ readonly stack?: boolean;
37
+ /** Series stand side by side inside the band instead of stacking. */
38
+ readonly group?: boolean;
39
+ /** Corner radius of the bar's far end. */
40
+ readonly radius?: number;
41
+ /** Pixels kept clear on each side of the bar inside its band. */
42
+ readonly inset?: number;
43
+ }
44
+
45
+ const DEFAULT_RADIUS = 4;
46
+
47
+ interface BarDatum {
48
+ readonly index: number;
49
+ readonly position: ChartValue;
50
+ readonly low: number;
51
+ readonly high: number;
52
+ readonly value: number;
53
+ /** Round the far end: the outer segment of a stack, or any unstacked bar. */
54
+ readonly outer: boolean;
55
+ }
56
+
57
+ function barMark<TDatum>(
58
+ kind: "barY" | "barX",
59
+ data: ReadonlyArray<TDatum>,
60
+ options: BarOptions<TDatum>,
61
+ ): ChartMark {
62
+ const vertical = kind === "barY";
63
+ const positionKey = vertical ? options.x : options.y;
64
+ const valueKey = vertical ? options.y : options.x;
65
+ const positions = readChannel(
66
+ data,
67
+ positionKey ?? ((_datum: TDatum, index: number) => index),
68
+ );
69
+ const values = readChannel(
70
+ data,
71
+ valueKey ?? ((datum: TDatum) => datum as unknown as ChartValue),
72
+ ).map(numericValue);
73
+ const colors = colorChannel(data, options);
74
+ const explicit = options.from !== undefined && options.to !== undefined;
75
+ const froms = readChannel(data, options.from).map(numericValue);
76
+ const tos = readChannel(data, options.to).map(numericValue);
77
+ const stacked =
78
+ !explicit &&
79
+ !options.group &&
80
+ (options.stack ?? options.color !== undefined);
81
+ const segments = stacked
82
+ ? stackSegments(
83
+ positions.map((position) =>
84
+ isPlaceable(position) ? categoryKey(position) : undefined,
85
+ ),
86
+ values,
87
+ )
88
+ : [];
89
+
90
+ const bars: BarDatum[] = [];
91
+ for (const [index, position] of positions.entries()) {
92
+ if (!isPlaceable(position)) {
93
+ continue;
94
+ }
95
+ if (explicit) {
96
+ const low = froms[index];
97
+ const high = tos[index];
98
+ if (low !== undefined && high !== undefined) {
99
+ bars.push({
100
+ index,
101
+ position,
102
+ low,
103
+ high,
104
+ value: high - low,
105
+ outer: true,
106
+ });
107
+ }
108
+ continue;
109
+ }
110
+ const value = values[index];
111
+ if (value === undefined) {
112
+ continue;
113
+ }
114
+ const segment = segments[index];
115
+ bars.push(
116
+ segment === undefined
117
+ ? { index, position, low: 0, high: value, value, outer: true }
118
+ : { index, position, ...segment, value },
119
+ );
120
+ }
121
+
122
+ const positionChannel = {
123
+ values: bars.map((bar) => bar.position),
124
+ discrete: "band" as const,
125
+ label: channelLabel(positionKey),
126
+ };
127
+ const valueChannel = {
128
+ values: bars.flatMap((bar) => [bar.low, bar.high]),
129
+ includeZero: !explicit,
130
+ label: channelLabel(valueKey) ?? options.label,
131
+ };
132
+
133
+ return {
134
+ id: options.id,
135
+ coordinate: "cartesian",
136
+ focusOrder: vertical ? "x" : "y",
137
+ channels: {
138
+ x: vertical ? positionChannel : valueChannel,
139
+ y: vertical ? valueChannel : positionChannel,
140
+ color: colors.channel,
141
+ },
142
+ render(context) {
143
+ const id = markId(options.id, kind, context);
144
+ const positionScale = vertical ? context.scales.x : context.scales.y;
145
+ const valueScale = vertical ? context.scales.y : context.scales.x;
146
+ if (positionScale === undefined || valueScale === undefined) {
147
+ return { nodes: [] };
148
+ }
149
+ const seriesOf = seriesResolver(
150
+ colors.values,
151
+ { ...options, fixedColor: options.fill },
152
+ id,
153
+ context,
154
+ );
155
+ const seriesOrder = [
156
+ ...new Set(bars.map((bar) => seriesOf(bar.index).key)),
157
+ ];
158
+ const lanes = options.group ? Math.max(1, seriesOrder.length) : 1;
159
+ const laneWidth = positionScale.bandwidth / lanes;
160
+ const inset = options.inset ?? 0;
161
+ const radius = options.radius ?? DEFAULT_RADIUS;
162
+
163
+ const nodes: SceneNode[] = [];
164
+ const points: ChartPoint[] = [];
165
+ for (const bar of bars) {
166
+ const series = seriesOf(bar.index);
167
+ const lane = options.group ? seriesOrder.indexOf(series.key) : 0;
168
+ const alongStart =
169
+ positionScale.map(bar.position) + lane * laneWidth + inset;
170
+ const alongSize = Math.max(0, laneWidth - inset * 2);
171
+ const lowPixel = valueScale.map(bar.low);
172
+ const highPixel = valueScale.map(bar.high);
173
+ const acrossStart = Math.min(lowPixel, highPixel);
174
+ const acrossSize = Math.abs(highPixel - lowPixel);
175
+ const rect = vertical
176
+ ? {
177
+ x: alongStart,
178
+ y: acrossStart,
179
+ width: alongSize,
180
+ height: acrossSize,
181
+ }
182
+ : {
183
+ x: acrossStart,
184
+ y: alongStart,
185
+ width: acrossSize,
186
+ height: alongSize,
187
+ };
188
+ nodes.push({
189
+ kind: "rect",
190
+ key: `${id}:${bar.index}`,
191
+ series: series.key,
192
+ role: "mark",
193
+ ...rect,
194
+ corners: corners(vertical, bar, radius, explicit),
195
+ paint: paint({
196
+ fill: series.color,
197
+ fillOpacity: options.fillOpacity,
198
+ stroke: options.stroke,
199
+ strokeDasharray: options.strokeDasharray,
200
+ opacity: options.opacity,
201
+ }),
202
+ });
203
+ if (options.tip === false) {
204
+ continue;
205
+ }
206
+ const along = alongStart + alongSize / 2;
207
+ points.push({
208
+ key: `${id}:${bar.index}`,
209
+ markId: id,
210
+ index: bar.index,
211
+ datum: data[bar.index],
212
+ x: vertical ? along : highPixel,
213
+ y: vertical ? highPixel : along,
214
+ xValue: vertical ? bar.position : bar.value,
215
+ yValue: vertical ? bar.value : bar.position,
216
+ series: series.key,
217
+ seriesLabel: series.label,
218
+ color: series.color,
219
+ title: vertical
220
+ ? context.formatX(bar.position)
221
+ : context.formatY(bar.position),
222
+ value: vertical
223
+ ? context.formatY(bar.value)
224
+ : context.formatX(bar.value),
225
+ hit: { kind: "rect", rect },
226
+ });
227
+ }
228
+ const legend =
229
+ options.color === undefined && options.label !== undefined
230
+ ? [
231
+ {
232
+ key: id,
233
+ label: options.label,
234
+ color: options.fill ?? context.paletteColor(context.markIndex),
235
+ shape: "square" as const,
236
+ },
237
+ ]
238
+ : [];
239
+ return { nodes, points, legend };
240
+ },
241
+ };
242
+ }
243
+
244
+ /** Rounds the end of the bar away from zero, and only that end: stacked joins stay square. */
245
+ function corners(
246
+ vertical: boolean,
247
+ bar: BarDatum,
248
+ radius: number,
249
+ explicit: boolean,
250
+ ): ChartCorners | undefined {
251
+ if (radius === 0 || !bar.outer) {
252
+ return undefined;
253
+ }
254
+ if (explicit) {
255
+ return [radius, radius, radius, radius];
256
+ }
257
+ const positive = bar.high >= bar.low;
258
+ if (vertical) {
259
+ return positive ? [radius, radius, 0, 0] : [0, 0, radius, radius];
260
+ }
261
+ return positive ? [0, radius, radius, 0] : [radius, 0, 0, radius];
262
+ }
263
+
264
+ /** Upright bars: categories along x, values up y. */
265
+ export function barY<TDatum>(
266
+ data: ReadonlyArray<TDatum>,
267
+ options: BarOptions<TDatum> = {},
268
+ ): ChartMark {
269
+ return barMark("barY", data, options);
270
+ }
271
+
272
+ /** Bars on their side: categories down y, values along x. */
273
+ export function barX<TDatum>(
274
+ data: ReadonlyArray<TDatum>,
275
+ options: BarOptions<TDatum> = {},
276
+ ): ChartMark {
277
+ return barMark("barX", data, options);
278
+ }
@@ -0,0 +1,123 @@
1
+ import { channelLabel, isPlaceable, readChannel } from "#/core/channel.ts";
2
+ import { formatValue } from "#/core/format.ts";
3
+ import {
4
+ type ChartMarkOptions,
5
+ colorChannel,
6
+ markId,
7
+ paint,
8
+ seriesResolver,
9
+ valueOn,
10
+ } from "#/core/marks/shared.ts";
11
+ import type {
12
+ ChartAccessor,
13
+ ChartMark,
14
+ ChartPoint,
15
+ ChartValue,
16
+ SceneNode,
17
+ } from "#/core/types.ts";
18
+
19
+ export interface CellOptions<TDatum> extends ChartMarkOptions<TDatum> {
20
+ readonly x?: ChartAccessor<TDatum, ChartValue>;
21
+ readonly y?: ChartAccessor<TDatum, ChartValue>;
22
+ readonly fill?: string;
23
+ /** Pixels kept clear around each cell. */
24
+ readonly inset?: number;
25
+ readonly radius?: number;
26
+ }
27
+
28
+ const DEFAULT_INSET = 1;
29
+ const DEFAULT_RADIUS = 2;
30
+
31
+ /**
32
+ * A rectangle per (x, y) pair of categories, filled by its color value: a
33
+ * heatmap, a calendar, a matrix.
34
+ */
35
+ export function cell<TDatum>(
36
+ data: ReadonlyArray<TDatum>,
37
+ options: CellOptions<TDatum>,
38
+ ): ChartMark {
39
+ const xs = readChannel(data, options.x);
40
+ const ys = readChannel(data, options.y);
41
+ const colors = colorChannel(data, options);
42
+ return {
43
+ id: options.id,
44
+ coordinate: "cartesian",
45
+ focusOrder: "point",
46
+ channels: {
47
+ x: {
48
+ values: xs.filter(isPlaceable),
49
+ discrete: "band",
50
+ padding: 0,
51
+ label: channelLabel(options.x),
52
+ },
53
+ y: {
54
+ values: ys.filter(isPlaceable),
55
+ discrete: "band",
56
+ padding: 0,
57
+ label: channelLabel(options.y),
58
+ },
59
+ color: colors.channel,
60
+ },
61
+ render(context) {
62
+ const id = markId(options.id, "cell", context);
63
+ const { x: xScale, y: yScale } = context.scales;
64
+ if (xScale === undefined || yScale === undefined) {
65
+ return { nodes: [] };
66
+ }
67
+ const seriesOf = seriesResolver(
68
+ colors.values,
69
+ { ...options, fixedColor: options.fill },
70
+ id,
71
+ context,
72
+ );
73
+ const inset = options.inset ?? DEFAULT_INSET;
74
+ const radius = options.radius ?? DEFAULT_RADIUS;
75
+ const nodes: SceneNode[] = [];
76
+ const points: ChartPoint[] = [];
77
+ for (const [index, datum] of data.entries()) {
78
+ const xValue = valueOn(xScale, xs[index]);
79
+ const yValue = valueOn(yScale, ys[index]);
80
+ if (xValue === undefined || yValue === undefined) {
81
+ continue;
82
+ }
83
+ const rect = {
84
+ x: xScale.map(xValue) + inset,
85
+ y: yScale.map(yValue) + inset,
86
+ width: Math.max(0, xScale.bandwidth - inset * 2),
87
+ height: Math.max(0, yScale.bandwidth - inset * 2),
88
+ };
89
+ const series = seriesOf(index);
90
+ const colorValue = colors.values[index];
91
+ nodes.push({
92
+ kind: "rect",
93
+ key: `${id}:${index}`,
94
+ role: "mark",
95
+ ...rect,
96
+ corners: [radius, radius, radius, radius],
97
+ paint: paint({ fill: series.color, opacity: options.opacity }),
98
+ });
99
+ if (options.tip !== false) {
100
+ points.push({
101
+ key: `${id}:${index}`,
102
+ markId: id,
103
+ index,
104
+ datum,
105
+ x: rect.x + rect.width / 2,
106
+ y: rect.y + rect.height / 2,
107
+ xValue,
108
+ yValue,
109
+ color: series.color,
110
+ seriesLabel: options.label,
111
+ title: `${context.formatX(xValue)} · ${context.formatY(yValue)}`,
112
+ value:
113
+ colorValue === undefined
114
+ ? ""
115
+ : formatValue(colorValue, context.locale),
116
+ hit: { kind: "rect", rect },
117
+ });
118
+ }
119
+ }
120
+ return { nodes, points };
121
+ },
122
+ };
123
+ }