@fundar/data-chart-telling 1.17.0 → 1.19.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 (353) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +2 -1
  3. package/dist/configuration/config.svelte.js +4 -3
  4. package/dist/index.d.ts +10 -3
  5. package/dist/index.js +4 -1
  6. package/dist/layout/coordinates/CoordinatesLayout.svelte +3 -1
  7. package/dist/layout/geometry/GeometryLayout.svelte +222 -53
  8. package/dist/layout/geometry/ScaleFreeContext.d.ts +31 -0
  9. package/dist/layout/geometry/ScaleFreeContext.js +65 -0
  10. package/dist/layout/geometry/ScaleFreeMarks.svelte +14 -0
  11. package/dist/layout/geometry/ScaleFreeMarks.svelte.d.ts +7 -0
  12. package/dist/layout/geometry/ScaleGate.svelte.d.ts +8 -0
  13. package/dist/layout/geometry/ScaleGate.svelte.js +95 -0
  14. package/dist/layout/geometry/ScaleGateMarks.svelte +15 -0
  15. package/dist/layout/geometry/ScaleGateMarks.svelte.d.ts +7 -0
  16. package/dist/layout/hover/HoverDefaults.d.ts +52 -0
  17. package/dist/layout/hover/HoverDefaults.js +96 -0
  18. package/dist/layout/hover/HoverLayers.d.ts +35 -0
  19. package/dist/layout/hover/HoverLayers.js +122 -0
  20. package/dist/layout/hover/HoverSettings.d.ts +1 -3
  21. package/dist/layout/hover/HoverSettings.js +3 -2
  22. package/dist/layout/hover/HoverShapes.d.ts +56 -0
  23. package/dist/layout/hover/HoverShapes.js +163 -0
  24. package/dist/layout/hover/PlotHover.svelte.d.ts +18 -0
  25. package/dist/layout/hover/PlotHover.svelte.js +54 -1
  26. package/dist/layout/legend/LegendState.svelte.d.ts +2 -0
  27. package/dist/layout/legend/LegendState.svelte.js +5 -0
  28. package/dist/layout/margins/Box.d.ts +4 -0
  29. package/dist/layout/margins/Box.js +14 -0
  30. package/dist/layout/margins/PlotMargins.svelte.js +26 -1
  31. package/dist/layout/plot/{Plot.svelte.d.ts → AbstractPlot.svelte.d.ts} +3 -3
  32. package/dist/layout/plot/Core.svelte +2 -2
  33. package/dist/layout/plot/PublicPlotContext.d.ts +6 -0
  34. package/dist/layout/plot/PublicPlotContext.js +8 -0
  35. package/dist/layout/styles/ValueLabels.d.ts +118 -59
  36. package/dist/layout/styles/ValueLabels.js +313 -146
  37. package/dist/layout/tooltip/TooltipState.svelte.d.ts +8 -9
  38. package/dist/layout/tooltip/TooltipState.svelte.js +28 -29
  39. package/dist/markers/MarkerKinds.d.ts +84 -1
  40. package/dist/markers/MarkerKinds.js +150 -23
  41. package/dist/markers/area/AreaMarker.svelte +1 -1
  42. package/dist/markers/band/BandMarker.svelte +74 -0
  43. package/dist/markers/band/BandMarker.svelte.d.ts +7 -0
  44. package/dist/markers/band/Bands.d.ts +16 -0
  45. package/dist/markers/band/Bands.js +48 -0
  46. package/dist/markers/bar/BarMarker.svelte +29 -70
  47. package/dist/markers/bollinger/BollingerMarker.svelte +1 -1
  48. package/dist/markers/box/BoxMarker.svelte +1 -1
  49. package/dist/markers/dot/DotRadius.d.ts +11 -0
  50. package/dist/markers/dot/DotRadius.js +21 -0
  51. package/dist/markers/hover/HoverMarker.svelte +7 -265
  52. package/dist/markers/hover/HoverMarker.svelte.d.ts +4 -27
  53. package/dist/markers/labels/CurveLabel.svelte +56 -0
  54. package/dist/markers/labels/CurveLabel.svelte.d.ts +9 -0
  55. package/dist/markers/labels/LabelMerge.d.ts +11 -0
  56. package/dist/markers/labels/LabelMerge.js +87 -0
  57. package/dist/markers/labels/MarkerLabel.svelte +36 -0
  58. package/dist/markers/labels/MarkerLabel.svelte.d.ts +10 -0
  59. package/dist/markers/labels/MarkerLabels.d.ts +52 -0
  60. package/dist/markers/labels/MarkerLabels.js +97 -0
  61. package/dist/markers/labels/geometry/BoxGeometry.d.ts +8 -0
  62. package/dist/markers/labels/geometry/BoxGeometry.js +101 -0
  63. package/dist/markers/labels/geometry/CurveGeometry.d.ts +34 -0
  64. package/dist/markers/labels/geometry/CurveGeometry.js +193 -0
  65. package/dist/markers/labels/geometry/PointGeometry.d.ts +9 -0
  66. package/dist/markers/labels/geometry/PointGeometry.js +22 -0
  67. package/dist/markers/labels/layouts/BoxLayout.d.ts +10 -0
  68. package/dist/markers/labels/layouts/BoxLayout.js +49 -0
  69. package/dist/markers/labels/layouts/PathLayout.d.ts +11 -0
  70. package/dist/markers/labels/layouts/PathLayout.js +91 -0
  71. package/dist/markers/labels/layouts/PointLayout.d.ts +19 -0
  72. package/dist/markers/labels/layouts/PointLayout.js +94 -0
  73. package/dist/markers/regression/RegressionMarker.svelte +1 -1
  74. package/dist/markers/text/Covering.d.ts +13 -0
  75. package/dist/markers/text/Covering.js +29 -0
  76. package/dist/markers/text/Declutter.d.ts +24 -3
  77. package/dist/markers/text/Declutter.js +46 -3
  78. package/dist/markers/text/TextMarker.svelte +90 -17
  79. package/dist/markers/text/TextMarker.svelte.d.ts +1 -1
  80. package/dist/markers/tick/TickMarker.svelte +1 -1
  81. package/dist/markers/utils/MarkerChannels.d.ts +1 -1
  82. package/dist/markers/utils/MarkerChannels.js +3 -2
  83. package/dist/markers/utils/PointPixel.d.ts +37 -0
  84. package/dist/markers/utils/PointPixel.js +44 -0
  85. package/dist/markers/violin/ViolinMarker.svelte +1 -1
  86. package/dist/markers/waffle/WaffleMarker.svelte +1 -1
  87. package/dist/phases/Pipeline.d.ts +7 -7
  88. package/dist/phases/Pipeline.js +12 -9
  89. package/dist/phases/context/ContextPhase.d.ts +22 -0
  90. package/dist/phases/context/ContextPhase.js +23 -0
  91. package/dist/phases/context/RowContext.d.ts +37 -0
  92. package/dist/phases/context/RowContext.js +97 -0
  93. package/dist/phases/data/DataPhase.d.ts +4 -4
  94. package/dist/phases/data/DataPhase.js +7 -7
  95. package/dist/phases/hover/HoverPhase.d.ts +6 -7
  96. package/dist/phases/hover/HoverPhase.js +28 -23
  97. package/dist/phases/mapping/MappingPhase.d.ts +4 -4
  98. package/dist/phases/mapping/MappingPhase.js +2 -2
  99. package/dist/phases/markers/MarkerStyles.d.ts +8 -0
  100. package/dist/phases/markers/MarkerStyles.js +24 -6
  101. package/dist/phases/markers/MarkersPhase.d.ts +6 -7
  102. package/dist/phases/markers/MarkersPhase.js +18 -12
  103. package/dist/phases/scales/ScalesPhase.d.ts +4 -4
  104. package/dist/phases/scales/ScalesPhase.js +4 -4
  105. package/dist/phases/sharedScales/SharedScalesPhase.d.ts +2 -2
  106. package/dist/phases/sharedScales/SharedScalesPhase.js +2 -2
  107. package/dist/phases/statistics/MarkerGrouping.d.ts +0 -9
  108. package/dist/phases/statistics/MarkerGrouping.js +5 -48
  109. package/dist/phases/statistics/MarkerInheritance.d.ts +11 -0
  110. package/dist/phases/statistics/MarkerInheritance.js +25 -0
  111. package/dist/phases/statistics/MarkerOrientation.d.ts +26 -0
  112. package/dist/phases/statistics/MarkerOrientation.js +29 -0
  113. package/dist/phases/statistics/StatisticsPhase.js +15 -15
  114. package/dist/phases/styles/StylesPhase.d.ts +3 -3
  115. package/dist/phases/styles/StylesPhase.js +2 -2
  116. package/dist/phases/tooltip/TooltipPhase.d.ts +4 -4
  117. package/dist/phases/tooltip/TooltipPhase.js +10 -7
  118. package/dist/phases/transforms/TransformsPhase.d.ts +3 -3
  119. package/dist/phases/transforms/TransformsPhase.js +7 -5
  120. package/dist/plots/correlation/heatmap/Plot.svelte +2 -2
  121. package/dist/plots/correlation/heatmap/Plot.svelte.d.ts +0 -1
  122. package/dist/plots/correlation/heatmap/defaults.d.ts +2 -2
  123. package/dist/plots/correlation/heatmap/defaults.js +35 -7
  124. package/dist/plots/correlation/heatmap/markers.d.ts +2 -8
  125. package/dist/plots/correlation/heatmap/markers.js +8 -11
  126. package/dist/plots/correlation/scatter/Plot.svelte +2 -2
  127. package/dist/plots/correlation/scatter/Plot.svelte.d.ts +0 -1
  128. package/dist/plots/correlation/scatter/defaults.d.ts +2 -2
  129. package/dist/plots/correlation/scatter/defaults.js +18 -6
  130. package/dist/plots/correlation/scatter/markers.d.ts +2 -11
  131. package/dist/plots/correlation/scatter/markers.js +24 -30
  132. package/dist/plots/distribution/box/BoxSummaries.d.ts +22 -8
  133. package/dist/plots/distribution/box/BoxSummaries.js +99 -40
  134. package/dist/plots/distribution/box/Plot.svelte +2 -2
  135. package/dist/plots/distribution/box/Plot.svelte.d.ts +0 -1
  136. package/dist/plots/distribution/box/defaults.d.ts +2 -2
  137. package/dist/plots/distribution/box/defaults.js +20 -6
  138. package/dist/plots/distribution/box/markers.d.ts +2 -2
  139. package/dist/plots/distribution/box/markers.js +13 -4
  140. package/dist/plots/distribution/density/Plot.svelte +2 -2
  141. package/dist/plots/distribution/density/Plot.svelte.d.ts +0 -1
  142. package/dist/plots/distribution/density/defaults.d.ts +2 -2
  143. package/dist/plots/distribution/density/defaults.js +33 -1
  144. package/dist/plots/distribution/density/markers.d.ts +2 -2
  145. package/dist/plots/distribution/density/markers.js +9 -5
  146. package/dist/plots/distribution/histogram/Plot.svelte +2 -2
  147. package/dist/plots/distribution/histogram/Plot.svelte.d.ts +0 -1
  148. package/dist/plots/distribution/histogram/defaults.d.ts +2 -2
  149. package/dist/plots/distribution/histogram/defaults.js +36 -36
  150. package/dist/plots/distribution/histogram/markers.d.ts +2 -2
  151. package/dist/plots/distribution/histogram/markers.js +7 -6
  152. package/dist/plots/distribution/pyramid/Plot.svelte +2 -2
  153. package/dist/plots/distribution/pyramid/Plot.svelte.d.ts +0 -1
  154. package/dist/plots/distribution/pyramid/defaults.d.ts +2 -2
  155. package/dist/plots/distribution/pyramid/defaults.js +13 -3
  156. package/dist/plots/distribution/pyramid/markers.d.ts +2 -10
  157. package/dist/plots/distribution/pyramid/markers.js +9 -18
  158. package/dist/plots/distribution/violin/Plot.svelte +2 -2
  159. package/dist/plots/distribution/violin/Plot.svelte.d.ts +0 -1
  160. package/dist/plots/distribution/violin/defaults.d.ts +2 -2
  161. package/dist/plots/distribution/violin/defaults.js +20 -7
  162. package/dist/plots/distribution/violin/markers.d.ts +2 -2
  163. package/dist/plots/distribution/violin/markers.js +30 -6
  164. package/dist/plots/evolution/area/Plot.svelte +2 -2
  165. package/dist/plots/evolution/area/Plot.svelte.d.ts +0 -1
  166. package/dist/plots/evolution/area/defaults.d.ts +2 -2
  167. package/dist/plots/evolution/area/defaults.js +5 -8
  168. package/dist/plots/evolution/area/markers.d.ts +2 -2
  169. package/dist/plots/evolution/area/markers.js +7 -5
  170. package/dist/plots/evolution/line/Plot.svelte +2 -2
  171. package/dist/plots/evolution/line/Plot.svelte.d.ts +0 -1
  172. package/dist/plots/evolution/line/defaults.d.ts +2 -2
  173. package/dist/plots/evolution/line/defaults.js +5 -7
  174. package/dist/plots/evolution/line/markers.d.ts +2 -2
  175. package/dist/plots/evolution/line/markers.js +6 -4
  176. package/dist/plots/evolution/slope/Plot.svelte +2 -2
  177. package/dist/plots/evolution/slope/Plot.svelte.d.ts +0 -1
  178. package/dist/plots/evolution/slope/defaults.d.ts +2 -2
  179. package/dist/plots/evolution/slope/defaults.js +11 -6
  180. package/dist/plots/evolution/slope/markers.d.ts +6 -5
  181. package/dist/plots/evolution/slope/markers.js +19 -12
  182. package/dist/plots/evolution/sparkline/Plot.svelte +2 -2
  183. package/dist/plots/evolution/sparkline/Plot.svelte.d.ts +0 -1
  184. package/dist/plots/evolution/sparkline/defaults.d.ts +2 -2
  185. package/dist/plots/evolution/sparkline/defaults.js +5 -2
  186. package/dist/plots/evolution/sparkline/markers.d.ts +2 -2
  187. package/dist/plots/evolution/sparkline/markers.js +6 -4
  188. package/dist/plots/generic/Plot.svelte +42 -0
  189. package/dist/plots/generic/Plot.svelte.d.ts +25 -0
  190. package/dist/plots/generic/defaults.d.ts +4 -0
  191. package/dist/plots/generic/defaults.js +7 -0
  192. package/dist/plots/map/geo/Core.svelte +3 -3
  193. package/dist/plots/map/geo/Plot.svelte +2 -2
  194. package/dist/plots/map/geo/Plot.svelte.d.ts +0 -1
  195. package/dist/plots/map/geo/defaults.d.ts +2 -2
  196. package/dist/plots/map/geo/phases/data.d.ts +1 -1
  197. package/dist/plots/map/geo/phases/data.js +1 -1
  198. package/dist/plots/map/geo/phases/hover.d.ts +2 -2
  199. package/dist/plots/map/geo/phases/hover.js +3 -3
  200. package/dist/plots/map/geo/phases/markers.d.ts +2 -2
  201. package/dist/plots/map/geo/phases/markers.js +3 -3
  202. package/dist/plots/map/geo/phases/scales.d.ts +2 -2
  203. package/dist/plots/map/geo/phases/scales.js +2 -2
  204. package/dist/plots/part-of-a-whole/treemap/Plot.svelte +2 -2
  205. package/dist/plots/part-of-a-whole/treemap/Plot.svelte.d.ts +0 -1
  206. package/dist/plots/part-of-a-whole/treemap/defaults.d.ts +2 -2
  207. package/dist/plots/part-of-a-whole/treemap/defaults.js +38 -22
  208. package/dist/plots/ranking/bar/Plot.svelte +2 -2
  209. package/dist/plots/ranking/bar/Plot.svelte.d.ts +0 -1
  210. package/dist/plots/ranking/bar/defaults.d.ts +2 -2
  211. package/dist/plots/ranking/bar/defaults.js +26 -10
  212. package/dist/plots/ranking/bar/markers.d.ts +3 -3
  213. package/dist/plots/ranking/bar/markers.js +17 -33
  214. package/dist/plots/ranking/bullet/Plot.svelte +2 -2
  215. package/dist/plots/ranking/bullet/Plot.svelte.d.ts +0 -1
  216. package/dist/plots/ranking/bullet/defaults.d.ts +2 -2
  217. package/dist/plots/ranking/bullet/defaults.js +24 -4
  218. package/dist/plots/ranking/bullet/markers.d.ts +3 -3
  219. package/dist/plots/ranking/bullet/markers.js +16 -10
  220. package/dist/plots/ranking/cleveland/ClevelandMarkers.d.ts +4 -11
  221. package/dist/plots/ranking/cleveland/ClevelandMarkers.js +73 -54
  222. package/dist/plots/ranking/cleveland/Plot.svelte +2 -2
  223. package/dist/plots/ranking/cleveland/Plot.svelte.d.ts +0 -1
  224. package/dist/plots/ranking/cleveland/defaults.d.ts +2 -2
  225. package/dist/plots/ranking/cleveland/defaults.js +39 -5
  226. package/dist/plots/ranking/dumbbell/Plot.svelte +2 -2
  227. package/dist/plots/ranking/dumbbell/Plot.svelte.d.ts +0 -1
  228. package/dist/plots/ranking/dumbbell/defaults.d.ts +2 -2
  229. package/dist/plots/ranking/forest/Plot.svelte +2 -2
  230. package/dist/plots/ranking/forest/Plot.svelte.d.ts +0 -1
  231. package/dist/plots/ranking/forest/defaults.d.ts +2 -2
  232. package/dist/plots/ranking/lollipop/Plot.svelte +2 -2
  233. package/dist/plots/ranking/lollipop/Plot.svelte.d.ts +0 -1
  234. package/dist/plots/ranking/lollipop/defaults.d.ts +2 -2
  235. package/dist/types/configuration/styling.d.ts +4 -0
  236. package/dist/types/layout/context.d.ts +23 -0
  237. package/dist/types/layout/context.js +1 -0
  238. package/dist/types/layout/hover.d.ts +3 -0
  239. package/dist/types/layout/styles/bullet.d.ts +4 -1
  240. package/dist/types/layout/styles/values.d.ts +59 -13
  241. package/dist/types/layout/tooltip.d.ts +5 -10
  242. package/dist/types/markers/axis/area.d.ts +5 -1
  243. package/dist/types/markers/axis/axisBasedMarkersConfig.d.ts +2 -1
  244. package/dist/types/markers/axis/band.d.ts +24 -0
  245. package/dist/types/markers/axis/band.js +1 -0
  246. package/dist/types/markers/axis/bar.d.ts +7 -4
  247. package/dist/types/markers/axis/bollinger.d.ts +2 -1
  248. package/dist/types/markers/axis/box.d.ts +2 -1
  249. package/dist/types/markers/axis/cell.d.ts +4 -1
  250. package/dist/types/markers/axis/hover.d.ts +60 -15
  251. package/dist/types/markers/axis/line.d.ts +4 -1
  252. package/dist/types/markers/axis/rect.d.ts +4 -1
  253. package/dist/types/markers/axis/regression.d.ts +2 -1
  254. package/dist/types/markers/axis/tick.d.ts +2 -1
  255. package/dist/types/markers/axis/trail.d.ts +4 -1
  256. package/dist/types/markers/axis/violin.d.ts +2 -1
  257. package/dist/types/markers/axis/waffle.d.ts +5 -1
  258. package/dist/types/markers/basic/dot.d.ts +4 -1
  259. package/dist/types/markers/basic/text.d.ts +25 -21
  260. package/dist/types/markers/channels/commonMarkerConfig.d.ts +6 -2
  261. package/dist/types/markers/channels/commonMarkerConfigWithLabel.d.ts +5 -9
  262. package/dist/types/markers/channels/commonMarkerConfigWithOrientation.d.ts +5 -0
  263. package/dist/types/markers/channels/commonMarkerConfigWithOrientation.js +1 -0
  264. package/dist/types/markers/common.d.ts +1 -0
  265. package/dist/types/markers/common.js +1 -0
  266. package/dist/types/markers/labels/box.d.ts +57 -0
  267. package/dist/types/markers/labels/box.js +1 -0
  268. package/dist/types/markers/labels/common.d.ts +15 -0
  269. package/dist/types/markers/labels/common.js +1 -0
  270. package/dist/types/markers/labels/path.d.ts +52 -0
  271. package/dist/types/markers/labels/path.js +1 -0
  272. package/dist/types/markers/labels/point.d.ts +40 -0
  273. package/dist/types/markers/labels/point.js +1 -0
  274. package/dist/types/markers/traits.d.ts +10 -0
  275. package/dist/types/phases/context/phase.d.ts +7 -0
  276. package/dist/types/phases/context/phase.js +1 -0
  277. package/dist/types/phases/data/phase.d.ts +2 -2
  278. package/dist/types/phases/hover/phase.d.ts +10 -7
  279. package/dist/types/phases/index.d.ts +2 -1
  280. package/dist/types/phases/kind.d.ts +11 -8
  281. package/dist/types/phases/mapping/phase.d.ts +2 -2
  282. package/dist/types/phases/markers/phase.d.ts +3 -10
  283. package/dist/types/phases/scales/phase.d.ts +4 -4
  284. package/dist/types/phases/stage.d.ts +3 -3
  285. package/dist/types/phases/state.d.ts +1 -1
  286. package/dist/types/phases/statistics/phase.d.ts +8 -3
  287. package/dist/types/phases/styles/phase.d.ts +18 -5
  288. package/dist/types/phases/transforms/phase.d.ts +4 -2
  289. package/dist/types/plots/constants.d.ts +3 -17
  290. package/dist/types/plots/correlation/heatmap/props.d.ts +3 -2
  291. package/dist/types/plots/correlation/heatmap/state.d.ts +2 -2
  292. package/dist/types/plots/correlation/scatter/props.d.ts +3 -2
  293. package/dist/types/plots/correlation/scatter/state.d.ts +2 -2
  294. package/dist/types/plots/distribution/box/props.d.ts +28 -2
  295. package/dist/types/plots/distribution/box/state.d.ts +6 -17
  296. package/dist/types/plots/distribution/density/props.d.ts +2 -1
  297. package/dist/types/plots/distribution/density/state.d.ts +2 -2
  298. package/dist/types/plots/distribution/histogram/props.d.ts +16 -2
  299. package/dist/types/plots/distribution/histogram/state.d.ts +2 -2
  300. package/dist/types/plots/distribution/pyramid/props.d.ts +3 -2
  301. package/dist/types/plots/distribution/pyramid/state.d.ts +2 -2
  302. package/dist/types/plots/distribution/violin/props.d.ts +6 -2
  303. package/dist/types/plots/distribution/violin/state.d.ts +2 -2
  304. package/dist/types/plots/evolution/area/props.d.ts +3 -2
  305. package/dist/types/plots/evolution/area/state.d.ts +2 -2
  306. package/dist/types/plots/evolution/line/props.d.ts +3 -2
  307. package/dist/types/plots/evolution/line/state.d.ts +2 -2
  308. package/dist/types/plots/evolution/slope/state.d.ts +2 -2
  309. package/dist/types/plots/evolution/sparkline/state.d.ts +2 -2
  310. package/dist/types/plots/generic/props.d.ts +16 -0
  311. package/dist/types/plots/generic/props.js +1 -0
  312. package/dist/types/plots/generic/state.d.ts +4 -0
  313. package/dist/types/plots/generic/state.js +1 -0
  314. package/dist/types/plots/map/geo/state.d.ts +1 -1
  315. package/dist/types/plots/part-of-a-whole/treemap/props.d.ts +18 -6
  316. package/dist/types/plots/part-of-a-whole/treemap/state.d.ts +2 -2
  317. package/dist/types/plots/props.d.ts +3 -0
  318. package/dist/types/plots/ranking/bar/props.d.ts +3 -2
  319. package/dist/types/plots/ranking/bar/state.d.ts +2 -2
  320. package/dist/types/plots/ranking/bullet/state.d.ts +2 -2
  321. package/dist/types/plots/ranking/cleveland/props.d.ts +3 -2
  322. package/dist/types/plots/ranking/cleveland/state.d.ts +2 -2
  323. package/dist/types/plots/ranking/dumbbell/props.d.ts +3 -2
  324. package/dist/types/plots/ranking/dumbbell/state.d.ts +2 -2
  325. package/dist/types/plots/ranking/forest/props.d.ts +5 -2
  326. package/dist/types/plots/ranking/forest/state.d.ts +2 -2
  327. package/dist/types/plots/ranking/lollipop/props.d.ts +3 -2
  328. package/dist/types/plots/ranking/lollipop/state.d.ts +2 -2
  329. package/docs/architecture.md +9 -9
  330. package/docs/markers.md +36 -6
  331. package/docs/plots/evolution/line.md +1 -1
  332. package/docs/plots/evolution/slope.md +1 -1
  333. package/docs/plots/part-of-a-whole/treemap.md +36 -2
  334. package/docs/plots/ranking/bar.md +1 -0
  335. package/docs/plots/ranking/cleveland.md +1 -1
  336. package/docs/state.md +33 -0
  337. package/docs/styles-and-segments.md +2 -15
  338. package/docs/theming.md +2 -1
  339. package/docs/tooltip-and-hover.md +81 -3
  340. package/docs/value-labels.md +159 -0
  341. package/examples/BarValues.svelte +1 -1
  342. package/examples/LineDates.svelte +1 -1
  343. package/examples/ResponsiveLine.svelte +3 -1
  344. package/examples/TreemapHover.svelte +25 -0
  345. package/examples/ValueLabels.svelte +1 -1
  346. package/llms-full.txt +9 -7
  347. package/llms.txt +3 -1
  348. package/package.json +3 -1
  349. package/dist/markers/utils/BoxLabel.d.ts +0 -58
  350. package/dist/markers/utils/BoxLabel.js +0 -175
  351. package/dist/markers/utils/MarkerLabel.svelte +0 -33
  352. package/dist/markers/utils/MarkerLabel.svelte.d.ts +0 -10
  353. /package/dist/layout/plot/{Plot.svelte → AbstractPlot.svelte} +0 -0
@@ -1,3 +1,4 @@
1
+ import { ValueLabels } from '../../layout/styles/ValueLabels';
1
2
  import { Transforms } from './Transforms';
2
3
  import { ValueSelection } from './ValueSelection';
3
4
  import { ProcessedPositions } from './ProcessedPositions';
@@ -37,7 +38,7 @@ export class TransformsPhase {
37
38
  this.#options = options;
38
39
  this.#connect = connect;
39
40
  }
40
- run = (state, ctx) => {
41
+ run = (state, env) => {
41
42
  const options = this.#options;
42
43
  const visible = (state.resolvedSeries ?? []);
43
44
  // An omitted part of a whole is laid out with the rest, so they keep their places; it is dropped after.
@@ -66,7 +67,7 @@ export class TransformsPhase {
66
67
  shared[valueAxis] = (row) => row[GAP_VALUE] ?? read(row);
67
68
  }
68
69
  const transforms = options.transforms
69
- ? options.transforms(state, ctx)
70
+ ? options.transforms(state, env)
70
71
  : (state.transforms ?? []);
71
72
  const rescaled = TransformsPhase.#rescaled(state, transforms, shared, valueAxis);
72
73
  if (rescaled)
@@ -129,17 +130,18 @@ export class TransformsPhase {
129
130
  #labelled(state) {
130
131
  const options = this.#options.labels;
131
132
  const values = state.values;
132
- if (!options || !(values?.show ?? options.show))
133
+ if (!options || !ValueLabels.shows(values, options.show))
133
134
  return {};
134
135
  const processed = state.processedData;
135
- const valueAxis = PlotAxes.valueOf(state);
136
+ const measure = options.measure?.(state);
137
+ const valueAxis = measure ? 'y' : PlotAxes.valueOf(state);
136
138
  const select = values?.select ?? options.select;
137
139
  const selections = ValueSelection.transforms(select, values?.series, valueAxis);
138
140
  const position = ProcessedPositions.of(processed, valueAxis);
139
141
  const skip = options.skip?.(state);
140
142
  const channels = {
141
143
  x: position.x,
142
- y: position.y,
144
+ y: measure ?? position.y,
143
145
  group: options.group === 'category'
144
146
  ? position[PlotAxes.categoryOf(state)]
145
147
  : SERIES_ID_KEY
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" generics="TData extends Record<string, unknown>">
2
- import Plot from '../../../layout/plot/Plot.svelte';
2
+ import AbstractPlot from '../../../layout/plot/AbstractPlot.svelte';
3
3
  import { buildHeatmapDefaults } from './defaults';
4
4
  import type {
5
5
  HeatmapPlotProps,
@@ -44,7 +44,7 @@
44
44
  });
45
45
  </script>
46
46
 
47
- <Plot
47
+ <AbstractPlot
48
48
  core={core as HeatmapPlotCoreProps<TData>}
49
49
  defaults={buildHeatmapDefaults}
50
50
  bind:margins
@@ -1,4 +1,3 @@
1
- import Plot from '../../../layout/plot/Plot.svelte';
2
1
  import type { HeatmapPlotProps } from '../../../types/plots/correlation/heatmap/props';
3
2
  declare function $$render<TData extends Record<string, unknown>>(): {
4
3
  props: HeatmapPlotProps<TData>;
@@ -1,5 +1,5 @@
1
1
  import type { AxisPlotDefaults } from '../../../types/phases';
2
- import type { HeatmapPlotState, HeatmapPlotCtx } from '../../../types/plots/correlation/heatmap/state';
2
+ import type { HeatmapPlotState, HeatmapPlotEnv } from '../../../types/plots/correlation/heatmap/state';
3
3
  /**
4
4
  * HeatmapPlot on the shared pipeline: both axes are categories. A heatmap's
5
5
  * `color` is each cell's numeric value, which the "data" phase reads as a
@@ -8,4 +8,4 @@ import type { HeatmapPlotState, HeatmapPlotCtx } from '../../../types/plots/corr
8
8
  * cell's tooltip names its coordinates and its value.
9
9
  * Its plot defaults to: hover one cell.
10
10
  */
11
- export declare function buildHeatmapDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<HeatmapPlotState<TData>, HeatmapPlotCtx>;
11
+ export declare function buildHeatmapDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<HeatmapPlotState<TData>, HeatmapPlotEnv>;
@@ -1,15 +1,17 @@
1
1
  import { ColorRamp } from '../../../utils/color';
2
+ import { HoverDefaults } from '../../../layout/hover/HoverDefaults';
2
3
  import { OpacityScale } from '../../../utils/OpacityScale';
3
4
  import { ContinuousDomain } from '../../../utils/ContinuousDomain';
4
5
  import { buildMarks } from './markers';
6
+ import { RowContext } from '../../../phases/context/RowContext';
5
7
  /**
6
8
  * HeatmapPlot's own "scales" output, over the band axes the shared phase laid
7
9
  * out: the color domain over every cell's value (when `color` is set), and each
8
10
  * row's own resolved color and opacity.
9
11
  */
10
- function extendWithColorScales(state, ctx) {
12
+ function extendWithColorScales(state, env) {
11
13
  const { scales, channels } = state;
12
- const { range, cfg } = ctx;
14
+ const { range, cfg } = env;
13
15
  const { color: colorAcc } = channels;
14
16
  const rows = (state.processedData?.data ?? []);
15
17
  const valueDomain = ContinuousDomain.build(scales.color?.domain, rows.map((d) => Number(colorAcc.accessor(d))), true) ?? [0, 1];
@@ -68,6 +70,14 @@ export function buildHeatmapDefaults() {
68
70
  }
69
71
  })
70
72
  },
73
+ // A cell's value is its color's, not a position's.
74
+ context: (state) => {
75
+ const color = state.channels.color.accessor;
76
+ return ((row) => {
77
+ const value = Number(color(row));
78
+ return { value: Number.isFinite(value) ? value : undefined };
79
+ });
80
+ },
71
81
  statistics: { inheritGrouping: false },
72
82
  sharedScales: { fromScalesPhase: ['color'] },
73
83
  scales: {
@@ -75,14 +85,32 @@ export function buildHeatmapDefaults() {
75
85
  defaults: () => ({ x: { labelAnchor: 'right' } }),
76
86
  extend: extendWithColorScales
77
87
  },
78
- styles: { values: { show: true } },
88
+ transforms: {
89
+ // A cell's value is its color: `'min'` and `'max'` compare that.
90
+ labels: {
91
+ show: true,
92
+ measure: (state) => state.channels?.color.accessor
93
+ }
94
+ },
95
+ styles: {
96
+ values: {
97
+ show: true,
98
+ layout: 'box',
99
+ layouts: { path: { edge: 'top', side: 'inside' }, point: { anchor: 'top', side: 'above' } }
100
+ }
101
+ },
79
102
  markers: { marks: buildMarks },
80
103
  hover: {
81
- // A hovered cell reads as its formatted value.
104
+ // A cell writes its own value: the hover only restyles the cells - the hovered one outlined.
105
+ marker: (_state, env) => HoverDefaults.shapes(env.cfg),
106
+ // A hovered cell reads as its value, as its value labels write it.
82
107
  label: (state) => {
83
- const format = state.values?.format ?? ((v) => `${v}`);
84
- const color = state.channels.color.accessor;
85
- return ((row) => format(Number(color(row)), ''));
108
+ const format = state.values?.format;
109
+ const number = (v) => `${v}`;
110
+ return ((row) => {
111
+ const context = RowContext.of(row);
112
+ return format ? format(Number(context.value), number, context) : `${context.value}`;
113
+ });
86
114
  },
87
115
  // A cell's color is its value, not a series'.
88
116
  decorate: () => () => ({ series: undefined, color: undefined })
@@ -1,9 +1,3 @@
1
- import type { HeatmapPlotState, HeatmapPlotCtx } from '../../../types/plots/correlation/heatmap/state';
1
+ import type { HeatmapPlotState, HeatmapPlotEnv } from '../../../types/plots/correlation/heatmap/state';
2
2
  import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
3
- /**
4
- * HeatmapPlot's "markers" phase: one `'cell'` marker per resolved-style
5
- * bucket - svelteplot's opacity/numeric scale machinery requires
6
- * `fillOpacity`/`strokeWidth` to be constant per mark instance, unlike
7
- * `fill`, which stays a per-row accessor.
8
- */
9
- export declare function buildMarks<TData extends Record<string, unknown>>(state: HeatmapPlotState<TData>, ctx: HeatmapPlotCtx): AxisBasedMarkersConfig[];
3
+ export declare function buildMarks<TData extends Record<string, unknown>>(state: HeatmapPlotState<TData>, env: HeatmapPlotEnv): AxisBasedMarkersConfig[];
@@ -8,15 +8,9 @@ function readChannel(channel) {
8
8
  return resolveAccessor(channel);
9
9
  }
10
10
  const OPACITY_STEPS = 32;
11
- /**
12
- * HeatmapPlot's "markers" phase: one `'cell'` marker per resolved-style
13
- * bucket - svelteplot's opacity/numeric scale machinery requires
14
- * `fillOpacity`/`strokeWidth` to be constant per mark instance, unlike
15
- * `fill`, which stays a per-row accessor.
16
- */
17
- export function buildMarks(state, ctx) {
11
+ export function buildMarks(state, env) {
18
12
  const { segments, channels, resolveColor, resolveOpacity } = state;
19
- const { range } = ctx;
13
+ const { range } = env;
20
14
  const labels = state.valueLabels?.show ? state.valueLabels : undefined;
21
15
  const getColor = channels.color.accessor;
22
16
  // The cells are the rows the "transforms" phase left, placed by its channels.
@@ -110,9 +104,12 @@ export function buildMarks(state, ctx) {
110
104
  borderRadius: group.styles.borderRadius,
111
105
  inset: 0.5
112
106
  },
113
- // Each cell writes its own value, in the middle of it.
114
- label: labels?.shapeLabel(ctx, (d) => labels.format(Number(getColor(d)), ''), {
115
- fontWeight: 600
107
+ hover: 'style',
108
+ // Each cell writes its own value, in the middle of it by default.
109
+ label: labels?.label(env, (d) => labels.text(d), {
110
+ fill: 'currentColor',
111
+ fontWeight: 600,
112
+ labelled: state.labelled
116
113
  })
117
114
  }));
118
115
  return markers;
@@ -1,5 +1,5 @@
1
1
  <script lang="ts" generics="TData extends Record<string, unknown>">
2
- import Plot from '../../../layout/plot/Plot.svelte';
2
+ import AbstractPlot from '../../../layout/plot/AbstractPlot.svelte';
3
3
  import { buildScatterDefaults } from './defaults';
4
4
  import type {
5
5
  ScatterPlotProps,
@@ -27,7 +27,7 @@
27
27
  }: ScatterPlotProps<TData> = $props();
28
28
  </script>
29
29
 
30
- <Plot
30
+ <AbstractPlot
31
31
  core={core as ScatterPlotCoreProps<TData>}
32
32
  defaults={buildScatterDefaults}
33
33
  bind:margins
@@ -1,4 +1,3 @@
1
- import Plot from '../../../layout/plot/Plot.svelte';
2
1
  import type { ScatterPlotProps } from '../../../types/plots/correlation/scatter/props';
3
2
  declare function $$render<TData extends Record<string, unknown>>(): {
4
3
  props: ScatterPlotProps<TData>;
@@ -1,4 +1,4 @@
1
1
  import type { AxisPlotDefaults } from '../../../types/phases';
2
- import type { ScatterPlotState, ScatterPlotCtx } from '../../../types/plots/correlation/scatter/state';
2
+ import type { ScatterPlotState, ScatterPlotEnv } from '../../../types/plots/correlation/scatter/state';
3
3
  /** ScatterPlot on the shared pipeline: discrete dots, its own radius-padded scales, and its own markers. Its plot defaults to: hover the exact point. */
4
- export declare function buildScatterDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<ScatterPlotState<TData>, ScatterPlotCtx>;
4
+ export declare function buildScatterDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<ScatterPlotState<TData>, ScatterPlotEnv>;
@@ -1,5 +1,6 @@
1
1
  import { ProcessedPositions } from '../../../phases/transforms/ProcessedPositions';
2
- import { buildMarks, buildValueLabels } from './markers';
2
+ import { HoverDefaults } from '../../../layout/hover/HoverDefaults';
3
+ import { buildMarks } from './markers';
3
4
  import { Margins } from '../../../layout/margins/Margins';
4
5
  import { MarkerAxisValues } from '../../../phases/scales/MarkerAxisValues';
5
6
  /** Stand-in max render radius for a channel-bound `r` (svelteplot's own auto sqrt scale). */
@@ -25,7 +26,7 @@ function paddedDomain(values, radiusPx, sizePx) {
25
26
  * the largest dot's radius, and grown to cover what derived markers draw
26
27
  * (a density curve's peak, a regression band).
27
28
  */
28
- function fitDomains(state, ctx) {
29
+ function fitDomains(state, env) {
29
30
  const { scales, channels, groupedSeries } = state;
30
31
  const processed = state.processedData;
31
32
  const position = ProcessedPositions.of(processed);
@@ -39,10 +40,10 @@ function fitDomains(state, ctx) {
39
40
  ? r
40
41
  : r !== undefined
41
42
  ? AUTO_SCALED_RADIUS_ESTIMATE
42
- : Math.max(0, ...groupedSeries.flatMap((group) => group.visualSegments.map((seg) => seg.styles.dotRadius ?? ctx.cfg.scatter.dotRadius)));
43
+ : Math.max(0, ...groupedSeries.flatMap((group) => group.visualSegments.map((seg) => seg.styles.dotRadius ?? env.cfg.scatter.dotRadius)));
43
44
  if (radius <= 0 && !extra.x.length && !extra.y.length)
44
45
  return {};
45
- const box = Margins.resolve({ ...ctx.cfg.margins, ...state.margins }).contentBox(state.width, state.height);
46
+ const box = Margins.resolve({ ...env.cfg.margins, ...state.margins }).contentBox(state.width, state.height);
46
47
  const fit = {};
47
48
  for (const [axis, size] of [
48
49
  ['x', box.width],
@@ -67,11 +68,22 @@ export function buildScatterDefaults() {
67
68
  base: { flat: 'dot' }
68
69
  }
69
70
  },
70
- transforms: { groups: 'runs' },
71
+ transforms: { groups: 'runs', labels: {} },
71
72
  scales: { fit: fitDomains },
72
- markers: { marks: buildMarks, valueLabels: buildValueLabels },
73
+ styles: {
74
+ values: {
75
+ layout: 'point',
76
+ layouts: { point: { side: 'above', gap: 8 }, path: { side: 'outside' } }
77
+ }
78
+ },
79
+ markers: { marks: buildMarks },
73
80
  hover: {
74
81
  strategy: 'punctual',
82
+ // A crosshair through the hovered point, and its label - the point is a dot already.
83
+ marker: () => ({
84
+ back: [HoverDefaults.rule('x'), HoverDefaults.rule('y')],
85
+ front: [HoverDefaults.text()]
86
+ }),
75
87
  label: (state) => {
76
88
  const { x, y } = state.channels;
77
89
  return (row) => `${x.accessor(row)}, ${y.accessor(row)}`;
@@ -1,14 +1,5 @@
1
- import type { ScatterPlotState, ScatterPlotCtx } from '../../../types/plots/correlation/scatter/state';
2
- import type { TextMarkerConfig } from '../../../types/markers/common';
3
- import { ValueLabels } from '../../../layout/styles/ValueLabels';
1
+ import type { ScatterPlotState, ScatterPlotEnv } from '../../../types/plots/correlation/scatter/state';
4
2
  import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
5
- /**
6
- * ScatterPlot's `styles.values` as one `'text'` marker over the same
7
- * processed rows its dots draw, each labelled with its own `y` value. A label
8
- * sits beside its point and flips to the other side where its text would
9
- * cross the plot's edge (`edgeAware`).
10
- */
11
- export declare function buildValueLabels<TData extends Record<string, unknown>>(labels: ValueLabels, state: ScatterPlotState<TData>): TextMarkerConfig[];
12
3
  /**
13
4
  * ScatterPlot's "markers" phase: one `'dot'` marker per visual segment of the
14
5
  * rows the "transforms" phase left, in their own order, so a reordering
@@ -16,4 +7,4 @@ export declare function buildValueLabels<TData extends Record<string, unknown>>(
16
7
  * "statistics" phase already put in `state.markers` - and its value labels on
17
8
  * top of everything.
18
9
  */
19
- export declare function buildMarks<TData extends Record<string, unknown>>(state: ScatterPlotState<TData>, ctx: ScatterPlotCtx): AxisBasedMarkersConfig[];
10
+ export declare function buildMarks<TData extends Record<string, unknown>>(state: ScatterPlotState<TData>, env: ScatterPlotEnv): AxisBasedMarkersConfig[];
@@ -1,38 +1,28 @@
1
- import { ProcessedPositions } from '../../../phases/transforms/ProcessedPositions';
2
1
  import { SERIES_KEY } from '../../../phases/transforms/TransformsPhase';
3
2
  import { ValueLabels } from '../../../layout/styles/ValueLabels';
4
3
  /**
5
- * ScatterPlot's `styles.values` as one `'text'` marker over the same
6
- * processed rows its dots draw, each labelled with its own `y` value. A label
7
- * sits beside its point and flips to the other side where its text would
8
- * cross the plot's edge (`edgeAware`).
4
+ * ScatterPlot's `styles.values` as labels of its dots: each labelled row with its own value, by default on a free
5
+ * spot around its point (`declutter: 'around'`) - a label with none is left out - or around its rim (`'path'`).
9
6
  */
10
- export function buildValueLabels(labels, state) {
11
- const { processedData } = state;
12
- if (!processedData?.data.length)
7
+ function buildValueLabels(labels, state, env) {
8
+ const rows = new Set(state.labelled ?? []);
9
+ if (rows.size === 0)
13
10
  return [];
14
- const { font, format } = labels;
15
- const position = ProcessedPositions.of(processedData);
16
11
  const nameOf = (d) => String(d[SERIES_KEY]);
17
- const valueOf = ValueLabels.valueOf(state);
12
+ const label = labels.label(env, (d) => labels.text(d), {
13
+ fill: 'currentColor',
14
+ seriesColor: (d) => state.colorForSeries?.(nameOf(d)),
15
+ fontSize: 11,
16
+ fontWeight: 500
17
+ });
18
+ if (labels.layout !== 'point')
19
+ return [label];
18
20
  return [
19
21
  {
20
- type: 'text',
21
- data: processedData.data,
22
- x: position.x,
23
- y: position.y,
24
- text: (d) => format(Number(valueOf(d)), nameOf(d), d),
25
- styles: {
26
- ...font,
27
- fill: font?.fill ?? 'currentColor',
28
- fontSize: font?.fontSize ?? 11,
29
- fontWeight: font?.fontWeight ?? 500,
30
- dx: font?.dx ?? 8,
31
- dy: font?.dy ?? 0,
32
- textAnchor: labels.textAnchor ?? 'start',
33
- lineAnchor: font?.lineAnchor ?? 'middle'
34
- },
35
- edgeAware: true
22
+ ...label,
23
+ select: (row) => rows.has(row),
24
+ // A label on its own dot stays there; one beside it moves around it to a free spot.
25
+ declutter: labels.side === 'on' ? undefined : 'around'
36
26
  }
37
27
  ];
38
28
  }
@@ -43,9 +33,9 @@ export function buildValueLabels(labels, state) {
43
33
  * "statistics" phase already put in `state.markers` - and its value labels on
44
34
  * top of everything.
45
35
  */
46
- export function buildMarks(state, ctx) {
36
+ export function buildMarks(state, env) {
47
37
  const { channels, colorForSeries, scales, flat } = state;
48
- const { cfg, interaction } = ctx;
38
+ const { cfg, interaction } = env;
49
39
  const r = channels.r;
50
40
  const rFn = r === undefined || typeof r === 'number' ? r : r.accessor;
51
41
  const opacityFn = channels.opacity?.accessor;
@@ -98,6 +88,7 @@ export function buildMarks(state, ctx) {
98
88
  markers.push({
99
89
  type: 'dot',
100
90
  data: seg.data,
91
+ hover: 'style',
101
92
  // Forwarded raw (a field key or function, whatever `series.x`/`.y`
102
93
  // is) rather than pre-resolved into a closure - `DotMarker.svelte`
103
94
  // and svelteplot's own `resolve()` already handle a field-key
@@ -125,5 +116,8 @@ export function buildMarks(state, ctx) {
125
116
  });
126
117
  }
127
118
  });
128
- return markers;
119
+ const labels = state.valueLabels?.show
120
+ ? buildValueLabels(state.valueLabels, state, env)
121
+ : undefined;
122
+ return ValueLabels.onMarkers(markers, 'dot', labels);
129
123
  }
@@ -1,19 +1,33 @@
1
- import type { HoverPosition } from '../../../types/phases';
1
+ import type { HoverPosition, ProcessedRow } from '../../../types/phases';
2
2
  import type { BoxPlotState, BoxSummaryRow } from '../../../types/plots/distribution/box/state';
3
+ import type { BoxSummary } from '../../../types/plots/distribution/box/props';
4
+ import type { PointGeometryOf } from '../../../types/markers/labels/point';
3
5
  /**
4
- * The boxes a box plot draws, one summary row per series, category and facet
5
- * panel - the same quartiles and whiskers svelteplot's `BoxX`/`BoxY` compute,
6
- * so its hover and tooltip read a box rather than every value inside it.
6
+ * The boxes a box plot draws, one per series, category and facet panel - the same quartiles and whiskers
7
+ * svelteplot's `BoxX`/`BoxY` compute, worked out once as each value's context ({@link context}), so its hover and
8
+ * tooltip read a box rather than every value inside it ({@link of}).
7
9
  */
8
10
  export declare class BoxSummaries {
9
- /** One summary per box, in the order the processed rows first reach it. */
11
+ #private;
12
+ /**
13
+ * Each value's context: the summary of the box it is in - its category, quartiles, whisker ends (the farthest
14
+ * values within 1.5 IQR of the box), how many values it has and the ones past the whiskers.
15
+ */
16
+ static context<TData extends Record<string, unknown>>(state: BoxPlotState<TData>): (row: ProcessedRow) => BoxSummary | undefined;
17
+ /**
18
+ * One summary row per box, in the order the processed rows first reach it, read off its first value's context.
19
+ * Its own context is the box's, with the median for its value, and where it sits for `x` and `y`.
20
+ */
10
21
  static of<TData extends Record<string, unknown>>(state: BoxPlotState<TData>): BoxSummaryRow[];
22
+ /** Where each part of a box its value labels can write sits: beside the box, at the height of its value. */
23
+ static partPlacements<TData extends Record<string, unknown>>(state: BoxPlotState<TData>): Record<string, Record<string, unknown>>;
24
+ /** Where a box's part label sits: at its category, level with the part's value, beside its band. */
25
+ static labelPoint<TData extends Record<string, unknown>>(state: BoxPlotState<TData>): (value: symbol) => PointGeometryOf;
11
26
  /** Where a box's summary sits for hover: at its category, level with its median. */
12
27
  static position<TData extends Record<string, unknown>>(state: BoxPlotState<TData>): HoverPosition;
13
28
  /**
14
- * How a hovered box reads by default: its series, when the plot groups its
15
- * rows; its category; then its five numbers, highest first, and how many
16
- * values fall past the whiskers.
29
+ * How a hovered box reads by default, from its context: its series, when the plot groups its rows; its
30
+ * category; then its five numbers, highest first, and how many values fall past the whiskers.
17
31
  */
18
32
  static format<TData extends Record<string, unknown>>(state: BoxPlotState<TData>): (row: BoxSummaryRow) => string;
19
33
  }
@@ -1,31 +1,41 @@
1
+ import { ValueLabels } from '../../../layout/styles/ValueLabels';
1
2
  import { ProcessedPositions } from '../../../phases/transforms/ProcessedPositions';
2
3
  import { quantile } from 'd3-array';
3
4
  import { SERIES_ID_KEY, SERIES_KEY } from '../../../phases/transforms/TransformsPhase';
4
5
  import { PANEL_KEY } from '../../../phases/facet/FacetPanels';
5
6
  import { PlotAxes } from '../../../phases/data/PlotAxes';
6
7
  import { TooltipState } from '../../../layout/tooltip/TooltipState.svelte';
8
+ import { RowContext } from '../../../phases/context/RowContext';
7
9
  import { isGapValue } from '../../utils/gaps';
8
10
  /** The whiskers reach this many interquartile ranges past the box, as svelteplot draws them. */
9
11
  const WHISKER_IQR = 1.5;
10
12
  /** A summary value as a tooltip shows it: at most two decimals. */
11
13
  const round = (value) => String(Math.round(value * 100) / 100);
12
14
  /**
13
- * The boxes a box plot draws, one summary row per series, category and facet
14
- * panel - the same quartiles and whiskers svelteplot's `BoxX`/`BoxY` compute,
15
- * so its hover and tooltip read a box rather than every value inside it.
15
+ * The boxes a box plot draws, one per series, category and facet panel - the same quartiles and whiskers
16
+ * svelteplot's `BoxX`/`BoxY` compute, worked out once as each value's context ({@link context}), so its hover and
17
+ * tooltip read a box rather than every value inside it ({@link of}).
16
18
  */
17
19
  export class BoxSummaries {
18
- /** One summary per box, in the order the processed rows first reach it. */
19
- static of(state) {
20
+ /** Which box a processed row is in: its series, panel and category. */
21
+ static #keyOf(state) {
22
+ const categoryAxis = PlotAxes.categoryOf(state);
23
+ const categoryOf = ProcessedPositions.of(state.processedData, PlotAxes.valueOf(state))[categoryAxis];
24
+ return (row) => JSON.stringify([row[SERIES_ID_KEY], row[PANEL_KEY] ?? null, String(categoryOf(row))]);
25
+ }
26
+ /**
27
+ * Each value's context: the summary of the box it is in - its category, quartiles, whisker ends (the farthest
28
+ * values within 1.5 IQR of the box), how many values it has and the ones past the whiskers.
29
+ */
30
+ static context(state) {
20
31
  const processed = state.processedData;
21
32
  if (!processed?.data.length)
22
- return [];
33
+ return () => undefined;
23
34
  const valueAxis = PlotAxes.valueOf(state);
24
- const categoryAxis = PlotAxes.categoryOf(state);
25
35
  const position = ProcessedPositions.of(processed, valueAxis);
26
36
  const valueOf = position[valueAxis];
27
- const categoryOf = position[categoryAxis];
28
- const categoryField = processed.channels[categoryAxis];
37
+ const categoryOf = position[PlotAxes.categoryOf(state)];
38
+ const keyOf = BoxSummaries.#keyOf(state);
29
39
  const boxes = new Map();
30
40
  for (const row of processed.data) {
31
41
  const value = valueOf(row);
@@ -34,41 +44,90 @@ export class BoxSummaries {
34
44
  const n = Number(value);
35
45
  if (!Number.isFinite(n))
36
46
  continue;
37
- const key = JSON.stringify([
38
- row[SERIES_ID_KEY],
39
- row[PANEL_KEY] ?? null,
40
- String(categoryOf(row))
41
- ]);
47
+ const key = keyOf(row);
42
48
  const box = boxes.get(key);
43
49
  if (box)
44
50
  box.values.push(n);
45
51
  else
46
- boxes.set(key, { first: row, values: [n] });
52
+ boxes.set(key, { category: categoryOf(row), values: [n] });
53
+ }
54
+ const summaries = new Map([...boxes].map(([key, { category, values }]) => [
55
+ key,
56
+ BoxSummaries.#summarize(category, values)
57
+ ]));
58
+ return (row) => summaries.get(keyOf(row));
59
+ }
60
+ static #summarize(category, values) {
61
+ const sorted = values.sort((a, b) => a - b);
62
+ const p25 = quantile(sorted, 0.25);
63
+ const median = quantile(sorted, 0.5);
64
+ const p75 = quantile(sorted, 0.75);
65
+ const reach = (p75 - p25) * WHISKER_IQR;
66
+ const inside = sorted.filter((v) => v >= p25 - reach && v <= p75 + reach);
67
+ return {
68
+ category,
69
+ min: inside[0] ?? p25,
70
+ p25,
71
+ median,
72
+ p75,
73
+ max: inside.at(-1) ?? p75,
74
+ count: sorted.length,
75
+ outliers: sorted.filter((v) => v < p25 - reach || v > p75 + reach)
76
+ };
77
+ }
78
+ /**
79
+ * One summary row per box, in the order the processed rows first reach it, read off its first value's context.
80
+ * Its own context is the box's, with the median for its value, and where it sits for `x` and `y`.
81
+ */
82
+ static of(state) {
83
+ const processed = state.processedData;
84
+ if (!processed?.data.length)
85
+ return [];
86
+ const valueAxis = PlotAxes.valueOf(state);
87
+ const categoryAxis = PlotAxes.categoryOf(state);
88
+ const categoryField = processed.channels[categoryAxis];
89
+ const keyOf = BoxSummaries.#keyOf(state);
90
+ const firsts = new Map();
91
+ for (const row of processed.data) {
92
+ const key = keyOf(row);
93
+ if (!firsts.has(key) && RowContext.of(row).median !== undefined)
94
+ firsts.set(key, row);
47
95
  }
48
- return [...boxes.values()].map(({ first, values }) => {
49
- const sorted = values.sort((a, b) => a - b);
50
- const p25 = quantile(sorted, 0.25);
51
- const median = quantile(sorted, 0.5);
52
- const p75 = quantile(sorted, 0.75);
53
- const reach = (p75 - p25) * WHISKER_IQR;
54
- const inside = sorted.filter((v) => v >= p25 - reach && v <= p75 + reach);
55
- const category = categoryOf(first);
56
- return {
96
+ return [...firsts.values()].map((first) => {
97
+ // The box's context, not its first value's row.
98
+ const context = { ...RowContext.of(first), row: undefined };
99
+ const { category, min, p25, median, p75, max, count, outliers } = context;
100
+ const summary = {
57
101
  ...(typeof categoryField === 'string' ? { [categoryField]: category } : {}),
58
102
  [SERIES_KEY]: first[SERIES_KEY],
59
103
  [SERIES_ID_KEY]: first[SERIES_ID_KEY],
60
104
  ...(first[PANEL_KEY] !== undefined ? { [PANEL_KEY]: first[PANEL_KEY] } : {}),
61
105
  category,
62
- min: inside[0] ?? p25,
106
+ min,
63
107
  p25,
64
108
  median,
65
109
  p75,
66
- max: inside.at(-1) ?? p75,
67
- count: sorted.length,
68
- outliers: sorted.filter((v) => v < p25 - reach || v > p75 + reach)
110
+ max,
111
+ count,
112
+ outliers
69
113
  };
114
+ RowContext.tag(summary, {
115
+ ...context,
116
+ [valueAxis]: median,
117
+ [categoryAxis]: category,
118
+ value: median
119
+ });
120
+ return summary;
70
121
  });
71
122
  }
123
+ /** Where each part of a box its value labels can write sits: beside the box, at the height of its value. */
124
+ static partPlacements(state) {
125
+ return ValueLabels.besideBand(state, ['min', 'p25', 'median', 'p75', 'max', 'outliers']);
126
+ }
127
+ /** Where a box's part label sits: at its category, level with the part's value, beside its band. */
128
+ static labelPoint(state) {
129
+ return ValueLabels.bandPoint(state, 'category');
130
+ }
72
131
  /** Where a box's summary sits for hover: at its category, level with its median. */
73
132
  static position(state) {
74
133
  const valueAxis = PlotAxes.valueOf(state);
@@ -79,22 +138,22 @@ export class BoxSummaries {
79
138
  return at;
80
139
  }
81
140
  /**
82
- * How a hovered box reads by default: its series, when the plot groups its
83
- * rows; its category; then its five numbers, highest first, and how many
84
- * values fall past the whiskers.
141
+ * How a hovered box reads by default, from its context: its series, when the plot groups its rows; its
142
+ * category; then its five numbers, highest first, and how many values fall past the whiskers.
85
143
  */
86
144
  static format(state) {
87
145
  const grouped = state.channels?.grouping !== undefined;
88
146
  return (row) => {
89
- const lines = grouped ? [String(row[SERIES_KEY])] : [];
90
- lines.push(TooltipState.value(row.category));
91
- lines.push(`max: ${round(row.max)}`);
92
- lines.push(`p75: ${round(row.p75)}`);
93
- lines.push(`median: ${round(row.median)}`);
94
- lines.push(`p25: ${round(row.p25)}`);
95
- lines.push(`min: ${round(row.min)}`);
96
- if (row.outliers.length)
97
- lines.push(`outliers: ${row.outliers.length}`);
147
+ const box = RowContext.of(row);
148
+ const lines = grouped ? [String(box.series)] : [];
149
+ lines.push(TooltipState.value(box.category));
150
+ lines.push(`max: ${round(box.max)}`);
151
+ lines.push(`p75: ${round(box.p75)}`);
152
+ lines.push(`median: ${round(box.median)}`);
153
+ lines.push(`p25: ${round(box.p25)}`);
154
+ lines.push(`min: ${round(box.min)}`);
155
+ if (box.outliers.length)
156
+ lines.push(`outliers: ${box.outliers.length}`);
98
157
  return lines.join('\n');
99
158
  };
100
159
  }