@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,112 @@
1
+ import { linearScale, logScale, toNumber } from "./continuous.js";
2
+ import { bandScale, categoryKey, pointScale } from "./discrete.js";
3
+ import { timeScale } from "./time.js";
4
+ import { niceDomain } from "../ticks.js";
5
+ function inferKind(channels, values) {
6
+ if (channels.some((channel) => channel.discrete === "band")) {
7
+ return "band";
8
+ }
9
+ const askedPoint = channels.some((channel) => channel.discrete === "point");
10
+ if (values.length > 0 && values.every((value) => value instanceof Date)) {
11
+ return askedPoint ? "point" : "time";
12
+ }
13
+ if (values.length > 0 && values.every((value) => typeof value === "number")) {
14
+ return askedPoint ? "point" : "linear";
15
+ }
16
+ return "point";
17
+ }
18
+ function distinct(values) {
19
+ const seen = /* @__PURE__ */ new Set();
20
+ const kept = [];
21
+ for (const value of values) {
22
+ const key = categoryKey(value);
23
+ if (!seen.has(key)) {
24
+ seen.add(key);
25
+ kept.push(value);
26
+ }
27
+ }
28
+ return kept;
29
+ }
30
+ function continuousDomain(values, options, kind) {
31
+ let low = Number.POSITIVE_INFINITY;
32
+ let high = Number.NEGATIVE_INFINITY;
33
+ for (const value of values) {
34
+ const number = toNumber(value);
35
+ if (Number.isFinite(number)) {
36
+ low = Math.min(low, number);
37
+ high = Math.max(high, number);
38
+ }
39
+ }
40
+ if (!Number.isFinite(low)) {
41
+ return [0, 1];
42
+ }
43
+ if (options.zero && kind === "linear") {
44
+ low = Math.min(low, 0);
45
+ high = Math.max(high, 0);
46
+ }
47
+ if (options.nice && kind === "linear") {
48
+ return niceDomain(low, high);
49
+ }
50
+ return [low, high];
51
+ }
52
+ function planScale(channels, options, axis) {
53
+ if (channels.length === 0) {
54
+ return void 0;
55
+ }
56
+ const values = channels.flatMap((channel) => channel.values);
57
+ const kind = options?.type ?? inferKind(channels, values);
58
+ const label = options?.label ?? channels.find((channel) => channel.label)?.label;
59
+ if (kind === "band" || kind === "point") {
60
+ const padding = channels.find(
61
+ (channel) => channel.padding !== void 0
62
+ )?.padding;
63
+ const paddingOuter = channels.find(
64
+ (channel) => channel.paddingOuter !== void 0
65
+ )?.paddingOuter;
66
+ return {
67
+ kind,
68
+ domain: options?.domain ?? distinct(values),
69
+ label,
70
+ padding,
71
+ paddingOuter: paddingOuter ?? padding
72
+ };
73
+ }
74
+ if (options?.domain !== void 0) {
75
+ return { kind, domain: options.domain.map(toNumber), label };
76
+ }
77
+ const zero = options?.zero ?? channels.some((channel) => channel.includeZero === true);
78
+ const nice = options?.nice ?? (axis === "y" || zero);
79
+ return {
80
+ kind,
81
+ domain: continuousDomain(values, { zero, nice }, kind),
82
+ label
83
+ };
84
+ }
85
+ function buildScale(plan, options, range, locale, tickCount) {
86
+ const ordered = options?.reverse ? [range[1], range[0]] : range;
87
+ if (plan.kind === "band" || plan.kind === "point") {
88
+ const discrete = {
89
+ domain: plan.domain,
90
+ range: ordered,
91
+ locale,
92
+ paddingInner: options?.paddingInner ?? plan.padding,
93
+ paddingOuter: options?.paddingOuter ?? plan.paddingOuter
94
+ };
95
+ return plan.kind === "band" ? bandScale(discrete) : pointScale(discrete);
96
+ }
97
+ const continuous = {
98
+ domain: [toNumber(plan.domain[0]), toNumber(plan.domain[1])],
99
+ range: ordered,
100
+ locale,
101
+ tickCount
102
+ };
103
+ if (plan.kind === "time") {
104
+ return timeScale(continuous);
105
+ }
106
+ return plan.kind === "log" ? logScale(continuous) : linearScale(continuous);
107
+ }
108
+ export {
109
+ buildScale,
110
+ planScale
111
+ };
112
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/scales/resolve.ts"],
4
+ "sourcesContent": ["import type { ChartPositionScaleOptions } from \"#/core/define-chart.ts\";\nimport { linearScale, logScale, toNumber } from \"#/core/scales/continuous.ts\";\nimport { bandScale, categoryKey, pointScale } from \"#/core/scales/discrete.ts\";\nimport { timeScale } from \"#/core/scales/time.ts\";\nimport { niceDomain } from \"#/core/ticks.ts\";\nimport type {\n\tChartChannel,\n\tChartPositionScale,\n\tChartPositionScaleKind,\n\tChartValue,\n} from \"#/core/types.ts\";\n\n/**\n * What a scale will be once the plot size is known: its kind and its domain,\n * read off every channel the marks put on it. Split from the range so the\n * axes can measure their tick labels before the plot rectangle exists.\n */\nexport interface ScalePlan {\n\treadonly kind: ChartPositionScaleKind;\n\treadonly domain: ReadonlyArray<ChartValue>;\n\treadonly label: string | undefined;\n\t/** The band padding a mark asked for, when the options name none. */\n\treadonly padding?: number;\n\treadonly paddingOuter?: number;\n}\n\nfunction inferKind(\n\tchannels: ReadonlyArray<ChartChannel>,\n\tvalues: ReadonlyArray<ChartValue>,\n): ChartPositionScaleKind {\n\tif (channels.some((channel) => channel.discrete === \"band\")) {\n\t\treturn \"band\";\n\t}\n\tconst askedPoint = channels.some((channel) => channel.discrete === \"point\");\n\tif (values.length > 0 && values.every((value) => value instanceof Date)) {\n\t\treturn askedPoint ? \"point\" : \"time\";\n\t}\n\tif (values.length > 0 && values.every((value) => typeof value === \"number\")) {\n\t\treturn askedPoint ? \"point\" : \"linear\";\n\t}\n\treturn \"point\";\n}\n\nfunction distinct(\n\tvalues: ReadonlyArray<ChartValue>,\n): ReadonlyArray<ChartValue> {\n\tconst seen = new Set<string>();\n\tconst kept: ChartValue[] = [];\n\tfor (const value of values) {\n\t\tconst key = categoryKey(value);\n\t\tif (!seen.has(key)) {\n\t\t\tseen.add(key);\n\t\t\tkept.push(value);\n\t\t}\n\t}\n\treturn kept;\n}\n\nfunction continuousDomain(\n\tvalues: ReadonlyArray<ChartValue>,\n\toptions: { readonly zero: boolean; readonly nice: boolean },\n\tkind: ChartPositionScaleKind,\n): readonly [number, number] {\n\tlet low = Number.POSITIVE_INFINITY;\n\tlet high = Number.NEGATIVE_INFINITY;\n\tfor (const value of values) {\n\t\tconst number = toNumber(value);\n\t\tif (Number.isFinite(number)) {\n\t\t\tlow = Math.min(low, number);\n\t\t\thigh = Math.max(high, number);\n\t\t}\n\t}\n\tif (!Number.isFinite(low)) {\n\t\treturn [0, 1];\n\t}\n\tif (options.zero && kind === \"linear\") {\n\t\tlow = Math.min(low, 0);\n\t\thigh = Math.max(high, 0);\n\t}\n\tif (options.nice && kind === \"linear\") {\n\t\treturn niceDomain(low, high);\n\t}\n\treturn [low, high];\n}\n\nexport function planScale(\n\tchannels: ReadonlyArray<ChartChannel>,\n\toptions: ChartPositionScaleOptions | undefined,\n\taxis: \"x\" | \"y\",\n): ScalePlan | undefined {\n\tif (channels.length === 0) {\n\t\treturn undefined;\n\t}\n\tconst values = channels.flatMap((channel) => channel.values);\n\tconst kind = options?.type ?? inferKind(channels, values);\n\tconst label =\n\t\toptions?.label ?? channels.find((channel) => channel.label)?.label;\n\tif (kind === \"band\" || kind === \"point\") {\n\t\tconst padding = channels.find(\n\t\t\t(channel) => channel.padding !== undefined,\n\t\t)?.padding;\n\t\tconst paddingOuter = channels.find(\n\t\t\t(channel) => channel.paddingOuter !== undefined,\n\t\t)?.paddingOuter;\n\t\treturn {\n\t\t\tkind,\n\t\t\tdomain: options?.domain ?? distinct(values),\n\t\t\tlabel,\n\t\t\tpadding,\n\t\t\tpaddingOuter: paddingOuter ?? padding,\n\t\t};\n\t}\n\tif (options?.domain !== undefined) {\n\t\treturn { kind, domain: options.domain.map(toNumber), label };\n\t}\n\tconst zero =\n\t\toptions?.zero ?? channels.some((channel) => channel.includeZero === true);\n\t// A value axis (y, or the x of horizontal bars) reads best on round\n\t// numbers; a position axis (the x of a scatter) on the data's own extent.\n\tconst nice = options?.nice ?? (axis === \"y\" || zero);\n\treturn {\n\t\tkind,\n\t\tdomain: continuousDomain(values, { zero, nice }, kind),\n\t\tlabel,\n\t};\n}\n\nexport function buildScale(\n\tplan: ScalePlan,\n\toptions: ChartPositionScaleOptions | undefined,\n\trange: readonly [number, number],\n\tlocale: string,\n\ttickCount: number,\n): ChartPositionScale {\n\tconst ordered: readonly [number, number] = options?.reverse\n\t\t? [range[1], range[0]]\n\t\t: range;\n\tif (plan.kind === \"band\" || plan.kind === \"point\") {\n\t\tconst discrete = {\n\t\t\tdomain: plan.domain,\n\t\t\trange: ordered,\n\t\t\tlocale,\n\t\t\tpaddingInner: options?.paddingInner ?? plan.padding,\n\t\t\tpaddingOuter: options?.paddingOuter ?? plan.paddingOuter,\n\t\t};\n\t\treturn plan.kind === \"band\" ? bandScale(discrete) : pointScale(discrete);\n\t}\n\tconst continuous = {\n\t\tdomain: [toNumber(plan.domain[0]), toNumber(plan.domain[1])] as const,\n\t\trange: ordered,\n\t\tlocale,\n\t\ttickCount,\n\t};\n\tif (plan.kind === \"time\") {\n\t\treturn timeScale(continuous);\n\t}\n\treturn plan.kind === \"log\" ? logScale(continuous) : linearScale(continuous);\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,4 @@
1
+ import { type ContinuousScaleOptions } from "./continuous.js";
2
+ import type { ChartPositionScale } from "../types.js";
3
+ export declare function timeScale(options: ContinuousScaleOptions): ChartPositionScale;
4
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../../src/core/scales/time.ts"],"names":[],"mappings":"AACA,OAAO,EACN,KAAK,sBAAsB,EAG3B,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EAAE,kBAAkB,EAAc,MAAM,iBAAiB,CAAC;AA0JtE,wBAAgB,SAAS,CAAC,OAAO,EAAE,sBAAsB,GAAG,kBAAkB,CAkB7E"}
@@ -0,0 +1,140 @@
1
+ import { dateFormat } from "../format.js";
2
+ import {
3
+ linearScale,
4
+ toNumber
5
+ } from "./continuous.js";
6
+ import { niceTicks } from "../ticks.js";
7
+ const SECOND = 1e3;
8
+ const MINUTE = 60 * SECOND;
9
+ const HOUR = 60 * MINUTE;
10
+ const DAY = 24 * HOUR;
11
+ const WEEK = 7 * DAY;
12
+ const MONTH = 30 * DAY;
13
+ const YEAR = 365 * DAY;
14
+ const INTERVALS = [
15
+ { unit: "second", step: 1, duration: SECOND },
16
+ { unit: "second", step: 5, duration: 5 * SECOND },
17
+ { unit: "second", step: 15, duration: 15 * SECOND },
18
+ { unit: "second", step: 30, duration: 30 * SECOND },
19
+ { unit: "minute", step: 1, duration: MINUTE },
20
+ { unit: "minute", step: 5, duration: 5 * MINUTE },
21
+ { unit: "minute", step: 15, duration: 15 * MINUTE },
22
+ { unit: "minute", step: 30, duration: 30 * MINUTE },
23
+ { unit: "hour", step: 1, duration: HOUR },
24
+ { unit: "hour", step: 3, duration: 3 * HOUR },
25
+ { unit: "hour", step: 6, duration: 6 * HOUR },
26
+ { unit: "hour", step: 12, duration: 12 * HOUR },
27
+ { unit: "day", step: 1, duration: DAY },
28
+ { unit: "day", step: 2, duration: 2 * DAY },
29
+ { unit: "week", step: 1, duration: WEEK },
30
+ { unit: "month", step: 1, duration: MONTH },
31
+ { unit: "month", step: 3, duration: 3 * MONTH },
32
+ { unit: "month", step: 6, duration: 6 * MONTH },
33
+ { unit: "year", step: 1, duration: YEAR }
34
+ ];
35
+ const UNIT_MS = {
36
+ second: SECOND,
37
+ minute: MINUTE,
38
+ hour: HOUR,
39
+ day: DAY
40
+ };
41
+ function chooseInterval(span, count) {
42
+ const target = span / Math.max(1, count);
43
+ let best = INTERVALS[0];
44
+ for (const interval of INTERVALS) {
45
+ if (Math.abs(Math.log(interval.duration / target)) < Math.abs(Math.log(best.duration / target))) {
46
+ best = interval;
47
+ }
48
+ }
49
+ return best;
50
+ }
51
+ function monthTicks(start, end, step) {
52
+ const from = new Date(start);
53
+ const year = from.getUTCFullYear();
54
+ let month = from.getUTCMonth();
55
+ month = Math.ceil(month / step) * step;
56
+ const ticks = [];
57
+ for (; ; ) {
58
+ const time = Date.UTC(year + Math.floor(month / 12), month % 12, 1);
59
+ if (time > end) {
60
+ return ticks;
61
+ }
62
+ if (time >= start) {
63
+ ticks.push(time);
64
+ }
65
+ month += step;
66
+ }
67
+ }
68
+ function intervalTicks(start, end, interval) {
69
+ const { unit, step } = interval;
70
+ if (unit === "year") {
71
+ const years = niceTicks(
72
+ new Date(start).getUTCFullYear(),
73
+ new Date(end).getUTCFullYear(),
74
+ Math.max(1, Math.round((end - start) / YEAR))
75
+ ).filter(Number.isInteger);
76
+ return years.map((year) => Date.UTC(year, 0, 1)).filter((time) => time >= start && time <= end);
77
+ }
78
+ if (unit === "month") {
79
+ return monthTicks(start, end, step);
80
+ }
81
+ if (unit === "week") {
82
+ const monday = Date.UTC(1970, 0, 5);
83
+ const first = monday + Math.ceil((start - monday) / WEEK) * WEEK;
84
+ const ticks2 = [];
85
+ for (let time = first; time <= end; time += WEEK) {
86
+ ticks2.push(time);
87
+ }
88
+ return ticks2;
89
+ }
90
+ const size = UNIT_MS[unit] * step;
91
+ const ticks = [];
92
+ for (let time = Math.ceil(start / size) * size; time <= end; time += size) {
93
+ ticks.push(time);
94
+ }
95
+ return ticks;
96
+ }
97
+ function tickLabel(time, unit, locale) {
98
+ const date = new Date(time);
99
+ if (unit === "year") {
100
+ return dateFormat(locale, { year: "numeric" }).format(date);
101
+ }
102
+ if (unit === "month") {
103
+ return date.getUTCMonth() === 0 ? dateFormat(locale, { month: "short", year: "numeric" }).format(date) : dateFormat(locale, { month: "short" }).format(date);
104
+ }
105
+ if (unit === "day" || unit === "week") {
106
+ return dateFormat(locale, { day: "numeric", month: "short" }).format(date);
107
+ }
108
+ if (unit === "second") {
109
+ return dateFormat(locale, {
110
+ hour: "2-digit",
111
+ minute: "2-digit",
112
+ second: "2-digit"
113
+ }).format(date);
114
+ }
115
+ return dateFormat(locale, { hour: "2-digit", minute: "2-digit" }).format(
116
+ date
117
+ );
118
+ }
119
+ function timeScale(options) {
120
+ const { domain, locale, tickCount } = options;
121
+ const linear = linearScale(options);
122
+ const [start, end] = [Math.min(...domain), Math.max(...domain)];
123
+ let unit = chooseInterval(end - start, tickCount).unit;
124
+ return {
125
+ ...linear,
126
+ kind: "time",
127
+ domain: [new Date(domain[0]), new Date(domain[1])],
128
+ invert: (pixel) => new Date(toNumber(linear.invert(pixel))),
129
+ ticks: (count = tickCount) => {
130
+ const interval = chooseInterval(end - start, count);
131
+ unit = interval.unit;
132
+ return intervalTicks(start, end, interval).map((time) => new Date(time));
133
+ },
134
+ format: (value) => tickLabel(toNumber(value), unit, locale)
135
+ };
136
+ }
137
+ export {
138
+ timeScale
139
+ };
140
+ //# sourceMappingURL=time.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/core/scales/time.ts"],
4
+ "sourcesContent": ["import { dateFormat } from \"#/core/format.ts\";\nimport {\n\ttype ContinuousScaleOptions,\n\tlinearScale,\n\ttoNumber,\n} from \"#/core/scales/continuous.ts\";\nimport { niceTicks } from \"#/core/ticks.ts\";\nimport type { ChartPositionScale, ChartValue } from \"#/core/types.ts\";\n\n/**\n * Dates in, pixels out. Ticks land on calendar boundaries (midnight, the first\n * of the month, New Year) rather than on round millisecond counts, and their\n * labels only say what changes between two ticks.\n *\n * Everything is UTC. Chart data is mostly day or month buckets written as\n * `2026-03-01`, which JavaScript parses as UTC midnight: reading them in the\n * viewer's zone would push every bucket onto the previous day west of\n * Greenwich.\n */\n\nconst SECOND = 1000;\nconst MINUTE = 60 * SECOND;\nconst HOUR = 60 * MINUTE;\nconst DAY = 24 * HOUR;\nconst WEEK = 7 * DAY;\nconst MONTH = 30 * DAY;\nconst YEAR = 365 * DAY;\n\ntype Unit = \"second\" | \"minute\" | \"hour\" | \"day\" | \"week\" | \"month\" | \"year\";\n\ninterface TimeInterval {\n\treadonly unit: Unit;\n\treadonly step: number;\n\t/** Approximate length, only to pick the interval closest to the tick count. */\n\treadonly duration: number;\n}\n\nconst INTERVALS: ReadonlyArray<TimeInterval> = [\n\t{ unit: \"second\", step: 1, duration: SECOND },\n\t{ unit: \"second\", step: 5, duration: 5 * SECOND },\n\t{ unit: \"second\", step: 15, duration: 15 * SECOND },\n\t{ unit: \"second\", step: 30, duration: 30 * SECOND },\n\t{ unit: \"minute\", step: 1, duration: MINUTE },\n\t{ unit: \"minute\", step: 5, duration: 5 * MINUTE },\n\t{ unit: \"minute\", step: 15, duration: 15 * MINUTE },\n\t{ unit: \"minute\", step: 30, duration: 30 * MINUTE },\n\t{ unit: \"hour\", step: 1, duration: HOUR },\n\t{ unit: \"hour\", step: 3, duration: 3 * HOUR },\n\t{ unit: \"hour\", step: 6, duration: 6 * HOUR },\n\t{ unit: \"hour\", step: 12, duration: 12 * HOUR },\n\t{ unit: \"day\", step: 1, duration: DAY },\n\t{ unit: \"day\", step: 2, duration: 2 * DAY },\n\t{ unit: \"week\", step: 1, duration: WEEK },\n\t{ unit: \"month\", step: 1, duration: MONTH },\n\t{ unit: \"month\", step: 3, duration: 3 * MONTH },\n\t{ unit: \"month\", step: 6, duration: 6 * MONTH },\n\t{ unit: \"year\", step: 1, duration: YEAR },\n];\n\nconst UNIT_MS: Record<\"second\" | \"minute\" | \"hour\" | \"day\", number> = {\n\tsecond: SECOND,\n\tminute: MINUTE,\n\thour: HOUR,\n\tday: DAY,\n};\n\nfunction chooseInterval(span: number, count: number): TimeInterval {\n\tconst target = span / Math.max(1, count);\n\tlet best = INTERVALS[0];\n\tfor (const interval of INTERVALS) {\n\t\tif (\n\t\t\tMath.abs(Math.log(interval.duration / target)) <\n\t\t\tMath.abs(Math.log(best.duration / target))\n\t\t) {\n\t\t\tbest = interval;\n\t\t}\n\t}\n\treturn best;\n}\n\nfunction monthTicks(start: number, end: number, step: number): number[] {\n\tconst from = new Date(start);\n\tconst year = from.getUTCFullYear();\n\tlet month = from.getUTCMonth();\n\tmonth = Math.ceil(month / step) * step;\n\tconst ticks: number[] = [];\n\tfor (;;) {\n\t\tconst time = Date.UTC(year + Math.floor(month / 12), month % 12, 1);\n\t\tif (time > end) {\n\t\t\treturn ticks;\n\t\t}\n\t\tif (time >= start) {\n\t\t\tticks.push(time);\n\t\t}\n\t\tmonth += step;\n\t}\n}\n\nfunction intervalTicks(\n\tstart: number,\n\tend: number,\n\tinterval: TimeInterval,\n): number[] {\n\tconst { unit, step } = interval;\n\tif (unit === \"year\") {\n\t\tconst years = niceTicks(\n\t\t\tnew Date(start).getUTCFullYear(),\n\t\t\tnew Date(end).getUTCFullYear(),\n\t\t\tMath.max(1, Math.round((end - start) / YEAR)),\n\t\t).filter(Number.isInteger);\n\t\treturn years\n\t\t\t.map((year) => Date.UTC(year, 0, 1))\n\t\t\t.filter((time) => time >= start && time <= end);\n\t}\n\tif (unit === \"month\") {\n\t\treturn monthTicks(start, end, step);\n\t}\n\tif (unit === \"week\") {\n\t\t// Weeks start on Monday: 1970-01-05 was one.\n\t\tconst monday = Date.UTC(1970, 0, 5);\n\t\tconst first = monday + Math.ceil((start - monday) / WEEK) * WEEK;\n\t\tconst ticks: number[] = [];\n\t\tfor (let time = first; time <= end; time += WEEK) {\n\t\t\tticks.push(time);\n\t\t}\n\t\treturn ticks;\n\t}\n\tconst size = UNIT_MS[unit] * step;\n\tconst ticks: number[] = [];\n\tfor (let time = Math.ceil(start / size) * size; time <= end; time += size) {\n\t\tticks.push(time);\n\t}\n\treturn ticks;\n}\n\nfunction tickLabel(time: number, unit: Unit, locale: string): string {\n\tconst date = new Date(time);\n\tif (unit === \"year\") {\n\t\treturn dateFormat(locale, { year: \"numeric\" }).format(date);\n\t}\n\tif (unit === \"month\") {\n\t\t// The year only where it turns, so a row of months stays short.\n\t\treturn date.getUTCMonth() === 0\n\t\t\t? dateFormat(locale, { month: \"short\", year: \"numeric\" }).format(date)\n\t\t\t: dateFormat(locale, { month: \"short\" }).format(date);\n\t}\n\tif (unit === \"day\" || unit === \"week\") {\n\t\treturn dateFormat(locale, { day: \"numeric\", month: \"short\" }).format(date);\n\t}\n\tif (unit === \"second\") {\n\t\treturn dateFormat(locale, {\n\t\t\thour: \"2-digit\",\n\t\t\tminute: \"2-digit\",\n\t\t\tsecond: \"2-digit\",\n\t\t}).format(date);\n\t}\n\treturn dateFormat(locale, { hour: \"2-digit\", minute: \"2-digit\" }).format(\n\t\tdate,\n\t);\n}\n\nexport function timeScale(options: ContinuousScaleOptions): ChartPositionScale {\n\tconst { domain, locale, tickCount } = options;\n\tconst linear = linearScale(options);\n\tconst [start, end] = [Math.min(...domain), Math.max(...domain)];\n\tlet unit = chooseInterval(end - start, tickCount).unit;\n\n\treturn {\n\t\t...linear,\n\t\tkind: \"time\",\n\t\tdomain: [new Date(domain[0]), new Date(domain[1])],\n\t\tinvert: (pixel) => new Date(toNumber(linear.invert(pixel))),\n\t\tticks: (count = tickCount) => {\n\t\t\tconst interval = chooseInterval(end - start, count);\n\t\t\tunit = interval.unit;\n\t\t\treturn intervalTicks(start, end, interval).map((time) => new Date(time));\n\t\t},\n\t\tformat: (value: ChartValue) => tickLabel(toNumber(value), unit, locale),\n\t};\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["ticks"]
7
+ }
@@ -0,0 +1,3 @@
1
+ import type { ChartTextMeasurer } from "./types.js";
2
+ export declare const estimateTextWidth: ChartTextMeasurer;
3
+ //# sourceMappingURL=text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../src/core/text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AA+BzD,eAAO,MAAM,iBAAiB,EAAE,iBAU/B,CAAC"}
@@ -0,0 +1,29 @@
1
+ const NARROW = /[ijlt.,:;'!|()[\]\s]/;
2
+ const WIDE = /[mwMW@%]/;
3
+ const DIGIT = /[0-9]/;
4
+ const UPPER = /[A-Z]/;
5
+ const EM_NARROW = 0.3;
6
+ const EM_WIDE = 0.85;
7
+ const EM_DIGIT = 0.58;
8
+ const EM_UPPER = 0.66;
9
+ const EM_OTHER = 0.54;
10
+ const BOLD_FACTOR = 1.06;
11
+ const BOLD_WEIGHT = 600;
12
+ function emWidth(character) {
13
+ if (NARROW.test(character)) return EM_NARROW;
14
+ if (WIDE.test(character)) return EM_WIDE;
15
+ if (DIGIT.test(character)) return EM_DIGIT;
16
+ if (UPPER.test(character)) return EM_UPPER;
17
+ return EM_OTHER;
18
+ }
19
+ const estimateTextWidth = (text, fontSize, fontWeight = 400) => {
20
+ let ems = 0;
21
+ for (const character of text) {
22
+ ems += emWidth(character);
23
+ }
24
+ return ems * fontSize * (fontWeight >= BOLD_WEIGHT ? BOLD_FACTOR : 1);
25
+ };
26
+ export {
27
+ estimateTextWidth
28
+ };
29
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/text.ts"],
4
+ "sourcesContent": ["import type { ChartTextMeasurer } from \"#/core/types.ts\";\n\n/**\n * Text width without a browser. The server and the first client render both\n * lay out with this estimate, so the hydrated markup matches; the browser\n * swaps in real metrics right after.\n *\n * Widths are in ems for a typical UI sans-serif: narrow letters, wide\n * letters, digits (tabular in most UI fonts) and everything else.\n */\nconst NARROW = /[ijlt.,:;'!|()[\\]\\s]/;\nconst WIDE = /[mwMW@%]/;\nconst DIGIT = /[0-9]/;\nconst UPPER = /[A-Z]/;\n\nconst EM_NARROW = 0.3;\nconst EM_WIDE = 0.85;\nconst EM_DIGIT = 0.58;\nconst EM_UPPER = 0.66;\nconst EM_OTHER = 0.54;\nconst BOLD_FACTOR = 1.06;\nconst BOLD_WEIGHT = 600;\n\nfunction emWidth(character: string): number {\n\tif (NARROW.test(character)) return EM_NARROW;\n\tif (WIDE.test(character)) return EM_WIDE;\n\tif (DIGIT.test(character)) return EM_DIGIT;\n\tif (UPPER.test(character)) return EM_UPPER;\n\treturn EM_OTHER;\n}\n\nexport const estimateTextWidth: ChartTextMeasurer = (\n\ttext,\n\tfontSize,\n\tfontWeight = 400,\n) => {\n\tlet ems = 0;\n\tfor (const character of text) {\n\t\tems += emWidth(character);\n\t}\n\treturn ems * fontSize * (fontWeight >= BOLD_WEIGHT ? BOLD_FACTOR : 1);\n};\n"],
5
+ "mappings": "AAUA,MAAM,SAAS;AACf,MAAM,OAAO;AACb,MAAM,QAAQ;AACd,MAAM,QAAQ;AAEd,MAAM,YAAY;AAClB,MAAM,UAAU;AAChB,MAAM,WAAW;AACjB,MAAM,WAAW;AACjB,MAAM,WAAW;AACjB,MAAM,cAAc;AACpB,MAAM,cAAc;AAEpB,SAAS,QAAQ,WAA2B;AAC3C,MAAI,OAAO,KAAK,SAAS,EAAG,QAAO;AACnC,MAAI,KAAK,KAAK,SAAS,EAAG,QAAO;AACjC,MAAI,MAAM,KAAK,SAAS,EAAG,QAAO;AAClC,MAAI,MAAM,KAAK,SAAS,EAAG,QAAO;AAClC,SAAO;AACR;AAEO,MAAM,oBAAuC,CACnD,MACA,UACA,aAAa,QACT;AACJ,MAAI,MAAM;AACV,aAAW,aAAa,MAAM;AAC7B,WAAO,QAAQ,SAAS;AAAA,EACzB;AACA,SAAO,MAAM,YAAY,cAAc,cAAc,cAAc;AACpE;",
6
+ "names": []
7
+ }
@@ -0,0 +1,8 @@
1
+ import type { ChartTheme } from "./types.js";
2
+ /**
3
+ * Every colour is a CSS variable with a fallback, so a chart picks up the
4
+ * kit's tokens (`--chart-1`…, `--muted-foreground`, `--border`) when the page
5
+ * has them and still draws legibly when it does not.
6
+ */
7
+ export declare const DEFAULT_THEME: ChartTheme;
8
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/core/theme.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAWlD;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,UAS3B,CAAC"}
@@ -0,0 +1,21 @@
1
+ const FALLBACK_PALETTE = [
2
+ "oklch(0.646 0.222 41.116)",
3
+ "oklch(0.6 0.118 184.704)",
4
+ "oklch(0.398 0.07 227.392)",
5
+ "oklch(0.828 0.189 84.429)",
6
+ "oklch(0.769 0.188 70.08)"
7
+ ];
8
+ const DEFAULT_THEME = {
9
+ palette: FALLBACK_PALETTE.map(
10
+ (fallback, index) => `var(--chart-${index + 1}, ${fallback})`
11
+ ),
12
+ foreground: "var(--foreground, currentColor)",
13
+ muted: "var(--muted-foreground, currentColor)",
14
+ grid: "var(--border, color-mix(in oklab, currentColor 15%, transparent))",
15
+ background: "var(--background, Canvas)",
16
+ fontSize: 12
17
+ };
18
+ export {
19
+ DEFAULT_THEME
20
+ };
21
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/theme.ts"],
4
+ "sourcesContent": ["import type { ChartTheme } from \"#/core/types.ts\";\n\n/** What the palette falls back to on a page without the kit's `--chart-N` tokens. */\nconst FALLBACK_PALETTE = [\n\t\"oklch(0.646 0.222 41.116)\",\n\t\"oklch(0.6 0.118 184.704)\",\n\t\"oklch(0.398 0.07 227.392)\",\n\t\"oklch(0.828 0.189 84.429)\",\n\t\"oklch(0.769 0.188 70.08)\",\n];\n\n/**\n * Every colour is a CSS variable with a fallback, so a chart picks up the\n * kit's tokens (`--chart-1`\u2026, `--muted-foreground`, `--border`) when the page\n * has them and still draws legibly when it does not.\n */\nexport const DEFAULT_THEME: ChartTheme = {\n\tpalette: FALLBACK_PALETTE.map(\n\t\t(fallback, index) => `var(--chart-${index + 1}, ${fallback})`,\n\t),\n\tforeground: \"var(--foreground, currentColor)\",\n\tmuted: \"var(--muted-foreground, currentColor)\",\n\tgrid: \"var(--border, color-mix(in oklab, currentColor 15%, transparent))\",\n\tbackground: \"var(--background, Canvas)\",\n\tfontSize: 12,\n};\n"],
5
+ "mappings": "AAGA,MAAM,mBAAmB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAOO,MAAM,gBAA4B;AAAA,EACxC,SAAS,iBAAiB;AAAA,IACzB,CAAC,UAAU,UAAU,eAAe,QAAQ,CAAC,KAAK,QAAQ;AAAA,EAC3D;AAAA,EACA,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,UAAU;AACX;",
6
+ "names": []
7
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Round tick values for a continuous axis. The step is always 1, 2, 5 or 10
3
+ * times a power of ten, which is what makes an axis readable: 0/25/50/75/100
4
+ * rather than 0/23.4/46.8.
5
+ */
6
+ /**
7
+ * The round step closest to covering `[min, max]` in about `count` intervals.
8
+ * Returns `0` for a degenerate span, which callers read as "no ticks".
9
+ */
10
+ export declare function tickStep(min: number, max: number, count?: number): number;
11
+ /**
12
+ * Round values strictly inside `[min, max]`. An axis that has been through
13
+ * `niceDomain` first gets ticks on both bounds; a raw domain does not, on
14
+ * purpose — a tick outside the data would be a lie.
15
+ */
16
+ export declare function niceTicks(min: number, max: number, count?: number): ReadonlyArray<number>;
17
+ /**
18
+ * Widens `[min, max]` outwards to the nearest round step, so the axis starts
19
+ * and ends on a tick instead of mid-air.
20
+ */
21
+ export declare function niceDomain(min: number, max: number, count?: number): readonly [number, number];
22
+ /**
23
+ * The `[min, max]` of a set of values, always including zero so bars are
24
+ * measured from a real baseline rather than from an arbitrary floor.
25
+ */
26
+ export declare function extentFromZero(values: ReadonlyArray<number>): readonly [number, number];
27
+ //# sourceMappingURL=ticks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticks.d.ts","sourceRoot":"","sources":["../../src/core/ticks.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAyBH;;;GAGG;AACH,wBAAgB,QAAQ,CACvB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,KAAK,GAAE,MAA2B,GAChC,MAAM,CAUR;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACxB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,KAAK,GAAE,MAA2B,GAChC,aAAa,CAAC,MAAM,CAAC,CAevB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACzB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,KAAK,GAAE,MAA2B,GAChC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAe3B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC7B,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAC3B,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAW3B"}
@@ -0,0 +1,76 @@
1
+ const NICE_FACTORS = [
2
+ { threshold: Math.SQRT2, factor: 1 },
3
+ { threshold: Math.sqrt(10), factor: 2 },
4
+ { threshold: Math.sqrt(50), factor: 5 }
5
+ ];
6
+ const DEFAULT_TICK_COUNT = 5;
7
+ const MAX_TICK_PRECISION = 12;
8
+ function roundToStep(value, step) {
9
+ if (step === 0 || !Number.isFinite(step)) {
10
+ return value;
11
+ }
12
+ const decimals = Math.max(0, -Math.floor(Math.log10(Math.abs(step))));
13
+ return Number(value.toFixed(Math.min(decimals, MAX_TICK_PRECISION)));
14
+ }
15
+ function tickStep(min, max, count = DEFAULT_TICK_COUNT) {
16
+ const span = max - min;
17
+ if (!Number.isFinite(span) || span <= 0 || count <= 0) {
18
+ return 0;
19
+ }
20
+ const rough = span / count;
21
+ const power = Math.floor(Math.log10(rough));
22
+ const error = rough / 10 ** power;
23
+ const match = NICE_FACTORS.find((candidate) => error < candidate.threshold);
24
+ return (match?.factor ?? 10) * 10 ** power;
25
+ }
26
+ function niceTicks(min, max, count = DEFAULT_TICK_COUNT) {
27
+ if (min === max) {
28
+ return [min];
29
+ }
30
+ const step = tickStep(min, max, count);
31
+ if (step === 0) {
32
+ return [];
33
+ }
34
+ const first = Math.ceil(min / step);
35
+ const last = Math.floor(max / step);
36
+ const ticks = [];
37
+ for (let index = first; index <= last; index += 1) {
38
+ ticks.push(roundToStep(index * step, step));
39
+ }
40
+ return ticks;
41
+ }
42
+ function niceDomain(min, max, count = DEFAULT_TICK_COUNT) {
43
+ if (!Number.isFinite(min) || !Number.isFinite(max)) {
44
+ return [0, 1];
45
+ }
46
+ if (min === max) {
47
+ return min === 0 ? [0, 1] : [Math.min(0, min), Math.max(0, max)];
48
+ }
49
+ const step = tickStep(min, max, count);
50
+ if (step === 0) {
51
+ return [min, max];
52
+ }
53
+ return [
54
+ roundToStep(Math.floor(min / step) * step, step),
55
+ roundToStep(Math.ceil(max / step) * step, step)
56
+ ];
57
+ }
58
+ function extentFromZero(values) {
59
+ let min = 0;
60
+ let max = 0;
61
+ for (const value of values) {
62
+ if (!Number.isFinite(value)) {
63
+ continue;
64
+ }
65
+ min = Math.min(min, value);
66
+ max = Math.max(max, value);
67
+ }
68
+ return [min, max];
69
+ }
70
+ export {
71
+ extentFromZero,
72
+ niceDomain,
73
+ niceTicks,
74
+ tickStep
75
+ };
76
+ //# sourceMappingURL=ticks.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/ticks.ts"],
4
+ "sourcesContent": ["/**\n * Round tick values for a continuous axis. The step is always 1, 2, 5 or 10\n * times a power of ten, which is what makes an axis readable: 0/25/50/75/100\n * rather than 0/23.4/46.8.\n */\n\n/** Steps the eye reads as round, in units of the current power of ten. */\nconst NICE_FACTORS = [\n\t{ threshold: Math.SQRT2, factor: 1 },\n\t{ threshold: Math.sqrt(10), factor: 2 },\n\t{ threshold: Math.sqrt(50), factor: 5 },\n] as const;\n\nconst DEFAULT_TICK_COUNT = 5;\n/** Beyond this many decimals the rounding helper stops fighting IEEE noise. */\nconst MAX_TICK_PRECISION = 12;\n\n/**\n * Drops the floating-point dust `i * step` leaves behind (0.30000000000000004)\n * by rounding to the number of decimals the step itself carries.\n */\nfunction roundToStep(value: number, step: number): number {\n\tif (step === 0 || !Number.isFinite(step)) {\n\t\treturn value;\n\t}\n\tconst decimals = Math.max(0, -Math.floor(Math.log10(Math.abs(step))));\n\treturn Number(value.toFixed(Math.min(decimals, MAX_TICK_PRECISION)));\n}\n\n/**\n * The round step closest to covering `[min, max]` in about `count` intervals.\n * Returns `0` for a degenerate span, which callers read as \"no ticks\".\n */\nexport function tickStep(\n\tmin: number,\n\tmax: number,\n\tcount: number = DEFAULT_TICK_COUNT,\n): number {\n\tconst span = max - min;\n\tif (!Number.isFinite(span) || span <= 0 || count <= 0) {\n\t\treturn 0;\n\t}\n\tconst rough = span / count;\n\tconst power = Math.floor(Math.log10(rough));\n\tconst error = rough / 10 ** power;\n\tconst match = NICE_FACTORS.find((candidate) => error < candidate.threshold);\n\treturn (match?.factor ?? 10) * 10 ** power;\n}\n\n/**\n * Round values strictly inside `[min, max]`. An axis that has been through\n * `niceDomain` first gets ticks on both bounds; a raw domain does not, on\n * purpose \u2014 a tick outside the data would be a lie.\n */\nexport function niceTicks(\n\tmin: number,\n\tmax: number,\n\tcount: number = DEFAULT_TICK_COUNT,\n): ReadonlyArray<number> {\n\tif (min === max) {\n\t\treturn [min];\n\t}\n\tconst step = tickStep(min, max, count);\n\tif (step === 0) {\n\t\treturn [];\n\t}\n\tconst first = Math.ceil(min / step);\n\tconst last = Math.floor(max / step);\n\tconst ticks: number[] = [];\n\tfor (let index = first; index <= last; index += 1) {\n\t\tticks.push(roundToStep(index * step, step));\n\t}\n\treturn ticks;\n}\n\n/**\n * Widens `[min, max]` outwards to the nearest round step, so the axis starts\n * and ends on a tick instead of mid-air.\n */\nexport function niceDomain(\n\tmin: number,\n\tmax: number,\n\tcount: number = DEFAULT_TICK_COUNT,\n): readonly [number, number] {\n\tif (!Number.isFinite(min) || !Number.isFinite(max)) {\n\t\treturn [0, 1];\n\t}\n\tif (min === max) {\n\t\treturn min === 0 ? [0, 1] : [Math.min(0, min), Math.max(0, max)];\n\t}\n\tconst step = tickStep(min, max, count);\n\tif (step === 0) {\n\t\treturn [min, max];\n\t}\n\treturn [\n\t\troundToStep(Math.floor(min / step) * step, step),\n\t\troundToStep(Math.ceil(max / step) * step, step),\n\t];\n}\n\n/**\n * The `[min, max]` of a set of values, always including zero so bars are\n * measured from a real baseline rather than from an arbitrary floor.\n */\nexport function extentFromZero(\n\tvalues: ReadonlyArray<number>,\n): readonly [number, number] {\n\tlet min = 0;\n\tlet max = 0;\n\tfor (const value of values) {\n\t\tif (!Number.isFinite(value)) {\n\t\t\tcontinue;\n\t\t}\n\t\tmin = Math.min(min, value);\n\t\tmax = Math.max(max, value);\n\t}\n\treturn [min, max];\n}\n"],
5
+ "mappings": "AAOA,MAAM,eAAe;AAAA,EACpB,EAAE,WAAW,KAAK,OAAO,QAAQ,EAAE;AAAA,EACnC,EAAE,WAAW,KAAK,KAAK,EAAE,GAAG,QAAQ,EAAE;AAAA,EACtC,EAAE,WAAW,KAAK,KAAK,EAAE,GAAG,QAAQ,EAAE;AACvC;AAEA,MAAM,qBAAqB;AAE3B,MAAM,qBAAqB;AAM3B,SAAS,YAAY,OAAe,MAAsB;AACzD,MAAI,SAAS,KAAK,CAAC,OAAO,SAAS,IAAI,GAAG;AACzC,WAAO;AAAA,EACR;AACA,QAAM,WAAW,KAAK,IAAI,GAAG,CAAC,KAAK,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC;AACpE,SAAO,OAAO,MAAM,QAAQ,KAAK,IAAI,UAAU,kBAAkB,CAAC,CAAC;AACpE;AAMO,SAAS,SACf,KACA,KACA,QAAgB,oBACP;AACT,QAAM,OAAO,MAAM;AACnB,MAAI,CAAC,OAAO,SAAS,IAAI,KAAK,QAAQ,KAAK,SAAS,GAAG;AACtD,WAAO;AAAA,EACR;AACA,QAAM,QAAQ,OAAO;AACrB,QAAM,QAAQ,KAAK,MAAM,KAAK,MAAM,KAAK,CAAC;AAC1C,QAAM,QAAQ,QAAQ,MAAM;AAC5B,QAAM,QAAQ,aAAa,KAAK,CAAC,cAAc,QAAQ,UAAU,SAAS;AAC1E,UAAQ,OAAO,UAAU,MAAM,MAAM;AACtC;AAOO,SAAS,UACf,KACA,KACA,QAAgB,oBACQ;AACxB,MAAI,QAAQ,KAAK;AAChB,WAAO,CAAC,GAAG;AAAA,EACZ;AACA,QAAM,OAAO,SAAS,KAAK,KAAK,KAAK;AACrC,MAAI,SAAS,GAAG;AACf,WAAO,CAAC;AAAA,EACT;AACA,QAAM,QAAQ,KAAK,KAAK,MAAM,IAAI;AAClC,QAAM,OAAO,KAAK,MAAM,MAAM,IAAI;AAClC,QAAM,QAAkB,CAAC;AACzB,WAAS,QAAQ,OAAO,SAAS,MAAM,SAAS,GAAG;AAClD,UAAM,KAAK,YAAY,QAAQ,MAAM,IAAI,CAAC;AAAA,EAC3C;AACA,SAAO;AACR;AAMO,SAAS,WACf,KACA,KACA,QAAgB,oBACY;AAC5B,MAAI,CAAC,OAAO,SAAS,GAAG,KAAK,CAAC,OAAO,SAAS,GAAG,GAAG;AACnD,WAAO,CAAC,GAAG,CAAC;AAAA,EACb;AACA,MAAI,QAAQ,KAAK;AAChB,WAAO,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,GAAG,GAAG,KAAK,IAAI,GAAG,GAAG,CAAC;AAAA,EAChE;AACA,QAAM,OAAO,SAAS,KAAK,KAAK,KAAK;AACrC,MAAI,SAAS,GAAG;AACf,WAAO,CAAC,KAAK,GAAG;AAAA,EACjB;AACA,SAAO;AAAA,IACN,YAAY,KAAK,MAAM,MAAM,IAAI,IAAI,MAAM,IAAI;AAAA,IAC/C,YAAY,KAAK,KAAK,MAAM,IAAI,IAAI,MAAM,IAAI;AAAA,EAC/C;AACD;AAMO,SAAS,eACf,QAC4B;AAC5B,MAAI,MAAM;AACV,MAAI,MAAM;AACV,aAAW,SAAS,QAAQ;AAC3B,QAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AAC5B;AAAA,IACD;AACA,UAAM,KAAK,IAAI,KAAK,KAAK;AACzB,UAAM,KAAK,IAAI,KAAK,KAAK;AAAA,EAC1B;AACA,SAAO,CAAC,KAAK,GAAG;AACjB;",
6
+ "names": []
7
+ }