@fundar/data-chart-telling 1.18.0 → 1.20.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 (252) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +2 -1
  3. package/dist/configuration/config.svelte.js +1 -1
  4. package/dist/index.d.ts +4 -2
  5. package/dist/index.js +2 -1
  6. package/dist/layout/coordinates/CoordinatesLayout.svelte +3 -1
  7. package/dist/layout/facet/FacetLayout.svelte +54 -41
  8. package/dist/layout/geometry/GeometryLayout.svelte +57 -22
  9. package/dist/layout/geometry/ScaleFreeContext.d.ts +31 -0
  10. package/dist/layout/geometry/ScaleFreeContext.js +65 -0
  11. package/dist/layout/geometry/ScaleFreeMarks.svelte +14 -0
  12. package/dist/layout/geometry/ScaleFreeMarks.svelte.d.ts +7 -0
  13. package/dist/layout/geometry/ScaleGate.svelte.d.ts +8 -0
  14. package/dist/layout/geometry/ScaleGate.svelte.js +95 -0
  15. package/dist/layout/geometry/ScaleGateMarks.svelte +15 -0
  16. package/dist/layout/geometry/ScaleGateMarks.svelte.d.ts +7 -0
  17. package/dist/layout/hover/HoverShapes.d.ts +20 -8
  18. package/dist/layout/hover/HoverShapes.js +119 -59
  19. package/dist/layout/legend/ContinuousSection.svelte +10 -0
  20. package/dist/layout/legend/LegendState.svelte.d.ts +13 -1
  21. package/dist/layout/legend/LegendState.svelte.js +32 -0
  22. package/dist/layout/margins/Box.d.ts +4 -0
  23. package/dist/layout/margins/Box.js +14 -0
  24. package/dist/layout/margins/PlotMargins.svelte.js +26 -1
  25. package/dist/layout/plot/{Plot.svelte → AbstractPlot.svelte} +83 -6
  26. package/dist/layout/plot/{Plot.svelte.d.ts → AbstractPlot.svelte.d.ts} +4 -4
  27. package/dist/layout/plot/PublicPlotContext.d.ts +5 -0
  28. package/dist/layout/plot/PublicPlotContext.js +7 -0
  29. package/dist/layout/styles/ValueLabels.d.ts +109 -58
  30. package/dist/layout/styles/ValueLabels.js +299 -141
  31. package/dist/markers/MarkerKinds.d.ts +79 -1
  32. package/dist/markers/MarkerKinds.js +149 -23
  33. package/dist/markers/area/AreaMarker.svelte +1 -1
  34. package/dist/markers/band/BandMarker.svelte +2 -1
  35. package/dist/markers/bar/BarMarker.svelte +8 -6
  36. package/dist/markers/bollinger/BollingerMarker.svelte +1 -1
  37. package/dist/markers/box/BoxMarker.svelte +1 -1
  38. package/dist/markers/dot/DotRadius.d.ts +11 -0
  39. package/dist/markers/dot/DotRadius.js +21 -0
  40. package/dist/markers/geo/GeoHoverMarker.svelte +14 -11
  41. package/dist/markers/labels/CurveLabel.svelte +56 -0
  42. package/dist/markers/labels/CurveLabel.svelte.d.ts +9 -0
  43. package/dist/markers/labels/LabelMerge.d.ts +11 -0
  44. package/dist/markers/labels/LabelMerge.js +87 -0
  45. package/dist/markers/labels/MarkerLabel.svelte +36 -0
  46. package/dist/markers/labels/MarkerLabel.svelte.d.ts +10 -0
  47. package/dist/markers/labels/MarkerLabels.d.ts +52 -0
  48. package/dist/markers/labels/MarkerLabels.js +97 -0
  49. package/dist/markers/labels/geometry/BoxGeometry.d.ts +8 -0
  50. package/dist/markers/labels/geometry/BoxGeometry.js +101 -0
  51. package/dist/markers/labels/geometry/CurveGeometry.d.ts +34 -0
  52. package/dist/markers/labels/geometry/CurveGeometry.js +193 -0
  53. package/dist/markers/labels/geometry/PointGeometry.d.ts +9 -0
  54. package/dist/markers/labels/geometry/PointGeometry.js +22 -0
  55. package/dist/markers/labels/layouts/BoxLayout.d.ts +10 -0
  56. package/dist/markers/labels/layouts/BoxLayout.js +49 -0
  57. package/dist/markers/labels/layouts/PathLayout.d.ts +11 -0
  58. package/dist/markers/labels/layouts/PathLayout.js +91 -0
  59. package/dist/markers/labels/layouts/PointLayout.d.ts +19 -0
  60. package/dist/markers/labels/layouts/PointLayout.js +94 -0
  61. package/dist/markers/regression/RegressionMarker.svelte +1 -1
  62. package/dist/markers/text/Covering.d.ts +13 -0
  63. package/dist/markers/text/Covering.js +29 -0
  64. package/dist/markers/text/Declutter.d.ts +24 -3
  65. package/dist/markers/text/Declutter.js +46 -3
  66. package/dist/markers/text/TextMarker.svelte +90 -17
  67. package/dist/markers/text/TextMarker.svelte.d.ts +1 -1
  68. package/dist/markers/tick/TickMarker.svelte +1 -1
  69. package/dist/markers/utils/MarkerChannels.d.ts +1 -1
  70. package/dist/markers/utils/MarkerChannels.js +3 -2
  71. package/dist/markers/violin/ViolinMarker.svelte +1 -1
  72. package/dist/markers/waffle/WaffleMarker.svelte +1 -1
  73. package/dist/phases/context/RowContext.d.ts +2 -0
  74. package/dist/phases/context/RowContext.js +4 -2
  75. package/dist/phases/context/rowContextKey.d.ts +2 -0
  76. package/dist/phases/context/rowContextKey.js +2 -0
  77. package/dist/phases/markers/MarkerStyles.js +14 -11
  78. package/dist/phases/markers/MarkersPhase.d.ts +2 -3
  79. package/dist/phases/markers/MarkersPhase.js +17 -11
  80. package/dist/phases/sharedScales/SharedStepScales.d.ts +10 -0
  81. package/dist/phases/sharedScales/SharedStepScales.js +43 -0
  82. package/dist/phases/statistics/MarkerGrouping.js +5 -32
  83. package/dist/phases/statistics/MarkerInheritance.d.ts +11 -0
  84. package/dist/phases/statistics/MarkerInheritance.js +25 -0
  85. package/dist/phases/statistics/MarkerOrientation.d.ts +26 -0
  86. package/dist/phases/statistics/MarkerOrientation.js +29 -0
  87. package/dist/phases/statistics/StatisticsPhase.js +15 -15
  88. package/dist/phases/transforms/TransformsPhase.js +5 -3
  89. package/dist/plots/correlation/heatmap/Plot.svelte +2 -2
  90. package/dist/plots/correlation/heatmap/Plot.svelte.d.ts +0 -1
  91. package/dist/plots/correlation/heatmap/defaults.js +14 -1
  92. package/dist/plots/correlation/heatmap/markers.d.ts +0 -6
  93. package/dist/plots/correlation/heatmap/markers.js +5 -9
  94. package/dist/plots/correlation/scatter/Plot.svelte +2 -2
  95. package/dist/plots/correlation/scatter/Plot.svelte.d.ts +0 -1
  96. package/dist/plots/correlation/scatter/defaults.js +9 -3
  97. package/dist/plots/correlation/scatter/markers.d.ts +0 -9
  98. package/dist/plots/correlation/scatter/markers.js +21 -28
  99. package/dist/plots/distribution/box/BoxSummaries.d.ts +5 -0
  100. package/dist/plots/distribution/box/BoxSummaries.js +9 -0
  101. package/dist/plots/distribution/box/Plot.svelte +2 -2
  102. package/dist/plots/distribution/box/Plot.svelte.d.ts +0 -1
  103. package/dist/plots/distribution/box/defaults.js +11 -0
  104. package/dist/plots/distribution/box/markers.js +10 -1
  105. package/dist/plots/distribution/density/Plot.svelte +2 -2
  106. package/dist/plots/distribution/density/Plot.svelte.d.ts +0 -1
  107. package/dist/plots/distribution/density/defaults.js +16 -0
  108. package/dist/plots/distribution/density/markers.js +5 -1
  109. package/dist/plots/distribution/histogram/Plot.svelte +2 -2
  110. package/dist/plots/distribution/histogram/Plot.svelte.d.ts +0 -1
  111. package/dist/plots/distribution/histogram/defaults.js +16 -11
  112. package/dist/plots/distribution/histogram/markers.js +3 -3
  113. package/dist/plots/distribution/pyramid/Plot.svelte +2 -2
  114. package/dist/plots/distribution/pyramid/Plot.svelte.d.ts +0 -1
  115. package/dist/plots/distribution/pyramid/defaults.js +9 -2
  116. package/dist/plots/distribution/pyramid/markers.d.ts +0 -8
  117. package/dist/plots/distribution/pyramid/markers.js +6 -16
  118. package/dist/plots/distribution/violin/Plot.svelte +2 -2
  119. package/dist/plots/distribution/violin/Plot.svelte.d.ts +0 -1
  120. package/dist/plots/distribution/violin/defaults.js +11 -0
  121. package/dist/plots/distribution/violin/markers.js +27 -3
  122. package/dist/plots/evolution/area/Plot.svelte +2 -2
  123. package/dist/plots/evolution/area/Plot.svelte.d.ts +0 -1
  124. package/dist/plots/evolution/area/defaults.js +4 -7
  125. package/dist/plots/evolution/area/markers.js +3 -1
  126. package/dist/plots/evolution/line/Plot.svelte +2 -2
  127. package/dist/plots/evolution/line/Plot.svelte.d.ts +0 -1
  128. package/dist/plots/evolution/line/defaults.js +4 -6
  129. package/dist/plots/evolution/line/markers.js +3 -1
  130. package/dist/plots/evolution/slope/Plot.svelte +2 -2
  131. package/dist/plots/evolution/slope/Plot.svelte.d.ts +0 -1
  132. package/dist/plots/evolution/slope/defaults.js +4 -3
  133. package/dist/plots/evolution/slope/markers.d.ts +4 -3
  134. package/dist/plots/evolution/slope/markers.js +16 -9
  135. package/dist/plots/evolution/sparkline/Plot.svelte +2 -2
  136. package/dist/plots/evolution/sparkline/Plot.svelte.d.ts +0 -1
  137. package/dist/plots/evolution/sparkline/defaults.js +1 -1
  138. package/dist/plots/evolution/sparkline/markers.js +3 -1
  139. package/dist/plots/generic/Plot.svelte +42 -0
  140. package/dist/plots/generic/Plot.svelte.d.ts +25 -0
  141. package/dist/plots/generic/defaults.d.ts +4 -0
  142. package/dist/plots/generic/defaults.js +7 -0
  143. package/dist/plots/map/geo/Core.svelte +29 -6
  144. package/dist/plots/map/geo/Plot.svelte +40 -12
  145. package/dist/plots/map/geo/Plot.svelte.d.ts +0 -1
  146. package/dist/plots/map/geo/phases/data.d.ts +2 -1
  147. package/dist/plots/map/geo/phases/data.js +8 -2
  148. package/dist/plots/map/geo/phases/scales.js +6 -2
  149. package/dist/plots/map/geo/slices.d.ts +27 -3
  150. package/dist/plots/map/geo/slices.js +58 -5
  151. package/dist/plots/part-of-a-whole/treemap/Plot.svelte +2 -2
  152. package/dist/plots/part-of-a-whole/treemap/Plot.svelte.d.ts +0 -1
  153. package/dist/plots/part-of-a-whole/treemap/defaults.js +2 -2
  154. package/dist/plots/ranking/bar/Plot.svelte +2 -2
  155. package/dist/plots/ranking/bar/Plot.svelte.d.ts +0 -1
  156. package/dist/plots/ranking/bar/defaults.js +13 -3
  157. package/dist/plots/ranking/bar/markers.js +12 -29
  158. package/dist/plots/ranking/bullet/Plot.svelte +2 -2
  159. package/dist/plots/ranking/bullet/Plot.svelte.d.ts +0 -1
  160. package/dist/plots/ranking/bullet/defaults.js +20 -0
  161. package/dist/plots/ranking/bullet/markers.js +7 -1
  162. package/dist/plots/ranking/cleveland/ClevelandMarkers.d.ts +2 -9
  163. package/dist/plots/ranking/cleveland/ClevelandMarkers.js +65 -47
  164. package/dist/plots/ranking/cleveland/Plot.svelte +2 -2
  165. package/dist/plots/ranking/cleveland/Plot.svelte.d.ts +0 -1
  166. package/dist/plots/ranking/cleveland/defaults.js +33 -2
  167. package/dist/plots/ranking/dumbbell/Plot.svelte +2 -2
  168. package/dist/plots/ranking/dumbbell/Plot.svelte.d.ts +0 -1
  169. package/dist/plots/ranking/forest/Plot.svelte +2 -2
  170. package/dist/plots/ranking/forest/Plot.svelte.d.ts +0 -1
  171. package/dist/plots/ranking/lollipop/Plot.svelte +2 -2
  172. package/dist/plots/ranking/lollipop/Plot.svelte.d.ts +0 -1
  173. package/dist/types/layout/context.d.ts +2 -0
  174. package/dist/types/layout/plot.d.ts +2 -0
  175. package/dist/types/layout/styles/bullet.d.ts +4 -1
  176. package/dist/types/layout/styles/geo.d.ts +3 -3
  177. package/dist/types/layout/styles/values.d.ts +51 -11
  178. package/dist/types/markers/axis/area.d.ts +5 -1
  179. package/dist/types/markers/axis/bar.d.ts +5 -2
  180. package/dist/types/markers/axis/bollinger.d.ts +2 -1
  181. package/dist/types/markers/axis/box.d.ts +2 -1
  182. package/dist/types/markers/axis/cell.d.ts +4 -1
  183. package/dist/types/markers/axis/line.d.ts +4 -1
  184. package/dist/types/markers/axis/rect.d.ts +4 -1
  185. package/dist/types/markers/axis/regression.d.ts +2 -1
  186. package/dist/types/markers/axis/tick.d.ts +2 -1
  187. package/dist/types/markers/axis/trail.d.ts +4 -1
  188. package/dist/types/markers/axis/violin.d.ts +2 -1
  189. package/dist/types/markers/axis/waffle.d.ts +5 -1
  190. package/dist/types/markers/basic/dot.d.ts +4 -1
  191. package/dist/types/markers/basic/text.d.ts +25 -21
  192. package/dist/types/markers/channels/commonMarkerConfigWithLabel.d.ts +5 -9
  193. package/dist/types/markers/channels/commonMarkerConfigWithOrientation.d.ts +5 -0
  194. package/dist/types/markers/channels/commonMarkerConfigWithOrientation.js +1 -0
  195. package/dist/types/markers/labels/box.d.ts +57 -0
  196. package/dist/types/markers/labels/box.js +1 -0
  197. package/dist/types/markers/labels/common.d.ts +15 -0
  198. package/dist/types/markers/labels/common.js +1 -0
  199. package/dist/types/markers/labels/path.d.ts +52 -0
  200. package/dist/types/markers/labels/path.js +1 -0
  201. package/dist/types/markers/labels/point.d.ts +40 -0
  202. package/dist/types/markers/labels/point.js +1 -0
  203. package/dist/types/markers/traits.d.ts +10 -0
  204. package/dist/types/phases/markers/phase.d.ts +1 -8
  205. package/dist/types/phases/statistics/phase.d.ts +8 -3
  206. package/dist/types/phases/styles/phase.d.ts +15 -3
  207. package/dist/types/phases/transforms/phase.d.ts +2 -0
  208. package/dist/types/plots/constants.d.ts +0 -12
  209. package/dist/types/plots/correlation/heatmap/props.d.ts +3 -2
  210. package/dist/types/plots/correlation/scatter/props.d.ts +3 -2
  211. package/dist/types/plots/data/geo.d.ts +19 -5
  212. package/dist/types/plots/distribution/box/props.d.ts +6 -2
  213. package/dist/types/plots/distribution/density/props.d.ts +2 -1
  214. package/dist/types/plots/distribution/histogram/props.d.ts +3 -2
  215. package/dist/types/plots/distribution/pyramid/props.d.ts +3 -2
  216. package/dist/types/plots/distribution/violin/props.d.ts +4 -1
  217. package/dist/types/plots/evolution/area/props.d.ts +3 -2
  218. package/dist/types/plots/evolution/line/props.d.ts +3 -2
  219. package/dist/types/plots/generic/props.d.ts +16 -0
  220. package/dist/types/plots/generic/props.js +1 -0
  221. package/dist/types/plots/generic/state.d.ts +4 -0
  222. package/dist/types/plots/generic/state.js +1 -0
  223. package/dist/types/plots/map/geo/props.d.ts +9 -3
  224. package/dist/types/plots/map/geo/state.d.ts +3 -1
  225. package/dist/types/plots/part-of-a-whole/treemap/props.d.ts +4 -2
  226. package/dist/types/plots/ranking/bar/props.d.ts +3 -2
  227. package/dist/types/plots/ranking/cleveland/props.d.ts +3 -2
  228. package/dist/types/plots/ranking/dumbbell/props.d.ts +3 -2
  229. package/dist/types/plots/ranking/forest/props.d.ts +5 -2
  230. package/dist/types/plots/ranking/lollipop/props.d.ts +3 -2
  231. package/dist/utils/ConfigEquality.d.ts +11 -0
  232. package/dist/utils/ConfigEquality.js +37 -0
  233. package/docs/architecture.md +9 -9
  234. package/docs/facets.md +12 -0
  235. package/docs/geo.md +35 -7
  236. package/docs/legend.md +6 -0
  237. package/docs/markers.md +2 -4
  238. package/docs/plots/ranking/bar.md +1 -1
  239. package/docs/plots/ranking/cleveland.md +1 -1
  240. package/docs/state.md +1 -1
  241. package/docs/styles-and-segments.md +1 -14
  242. package/docs/timeline.md +1 -1
  243. package/docs/transforms-and-statistics.md +1 -1
  244. package/docs/value-labels.md +159 -0
  245. package/examples/BarValues.svelte +1 -1
  246. package/llms-full.txt +5 -5
  247. package/llms.txt +1 -0
  248. package/package.json +3 -1
  249. package/dist/markers/utils/BoxLabel.d.ts +0 -58
  250. package/dist/markers/utils/BoxLabel.js +0 -175
  251. package/dist/markers/utils/MarkerLabel.svelte +0 -33
  252. package/dist/markers/utils/MarkerLabel.svelte.d.ts +0 -10
@@ -12,8 +12,13 @@ function readField(field, row) {
12
12
  }
13
13
  /**
14
14
  * What a GeoPlot hands `Plot` to slice by: one stand-in row per value
15
- * combination found in the features and markers' rows, and timeline/facet
16
- * accessors that read a stand-in or a real row.
15
+ * combination, and timeline/facet accessors that read a stand-in or a real
16
+ * row. A dimension's values are the ones the timeline or facet names
17
+ * (`timeline.values`, `facet.values`, `rowValues`, `colValues`), in
18
+ * their order, then any more found in the features and markers' rows - so
19
+ * the slices exist even when no feature carries the field. Each combination
20
+ * is also returned as a `slices` entry: its place and the context its
21
+ * features are read in.
17
22
  */
18
23
  export function buildGeoSlicing(args) {
19
24
  const { rows, timeline, facet } = args;
@@ -31,17 +36,47 @@ export function buildGeoSlicing(args) {
31
36
  const reader = (dimension) => (row) => SLICE in row
32
37
  ? row[SLICE][dimension]
33
38
  : readField(fields[dimension], row);
39
+ /** The values the timeline or facet names for a dimension, ahead of the ones found. */
40
+ const named = {
41
+ time: timeline?.values,
42
+ by: facet && isFacetWrap(facet) ? facet.values : undefined,
43
+ rows: facet && !isFacetWrap(facet) ? facet.rowValues : undefined,
44
+ cols: facet && !isFacetWrap(facet) ? facet.colValues : undefined
45
+ };
46
+ const time = new TimelineTime(timeline?.dateFormat);
34
47
  let combos = [{}];
35
48
  for (const dimension of Object.keys(fields)) {
36
49
  const read = rows.map((row) => readField(fields[dimension], row)).filter((v) => v != null);
37
50
  // Dates on the same step are one value.
38
- const time = new TimelineTime(timeline?.dateFormat);
39
- const keyOf = (value) => dimension === 'time' ? String(time.toTime(value) ?? value) : value;
40
- const values = [...new Map(read.map((value) => [keyOf(value), value])).values()];
51
+ const keyOf = (value) => dimension === 'time' ? String(time.toTime(value) ?? value) : String(value);
52
+ const values = [
53
+ ...new Map([...(named[dimension] ?? []), ...read].map((value) => [keyOf(value), value])).values()
54
+ ];
41
55
  combos = combos.flatMap((combo) => values.map((value) => ({ ...combo, [dimension]: value })));
42
56
  }
57
+ const slices = combos.map((combo) => {
58
+ const tick = combo.time === undefined ? undefined : time.toTime(combo.time);
59
+ const by = combo.by === undefined ? undefined : String(combo.by);
60
+ const row = combo.rows === undefined ? undefined : String(combo.rows);
61
+ const col = combo.cols === undefined ? undefined : String(combo.cols);
62
+ const value = by ?? (row !== undefined && col !== undefined ? `${row} · ${col}` : (row ?? col));
63
+ const place = {
64
+ ...(tick === undefined ? {} : { time: tick }),
65
+ ...(value === undefined
66
+ ? {}
67
+ : {
68
+ facet: {
69
+ facetValue: value,
70
+ ...(row === undefined ? {} : { row }),
71
+ ...(col === undefined ? {} : { col })
72
+ }
73
+ })
74
+ };
75
+ return { place, context: GeoSlices.contextOf(place) };
76
+ });
43
77
  return {
44
78
  rows: combos.map((combo) => ({ [SLICE]: combo })),
79
+ slices,
45
80
  timeline: timeline && {
46
81
  ...timeline,
47
82
  accessor: reader('time'),
@@ -57,3 +92,21 @@ export function buildGeoSlicing(args) {
57
92
  })
58
93
  };
59
94
  }
95
+ export const GeoSlices = {
96
+ /** The context a callback reads a feature in, for the slice at `place`: empty when there is none. */
97
+ contextOf(place) {
98
+ const { time, facet } = place;
99
+ return {
100
+ ...(facet
101
+ ? {
102
+ facet: {
103
+ value: facet.facetValue,
104
+ ...(facet.row === undefined ? {} : { row: facet.row }),
105
+ ...(facet.col === undefined ? {} : { col: facet.col })
106
+ }
107
+ }
108
+ : {}),
109
+ ...(time === undefined ? {} : { time })
110
+ };
111
+ }
112
+ };
@@ -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 { buildTreemapDefaults } from './defaults';
4
4
  import type {
5
5
  TreemapPlotProps,
@@ -30,7 +30,7 @@
30
30
  }: TreemapPlotProps<TData> = $props();
31
31
  </script>
32
32
 
33
- <Plot
33
+ <AbstractPlot
34
34
  core={core as TreemapPlotCoreProps<TData>}
35
35
  defaults={buildTreemapDefaults}
36
36
  bind:margins
@@ -1,4 +1,3 @@
1
- import Plot from '../../../layout/plot/Plot.svelte';
2
1
  import type { TreemapPlotProps } from '../../../types/plots/part-of-a-whole/treemap/props';
3
2
  declare function $$render<TData extends Record<string, unknown>>(): {
4
3
  props: TreemapPlotProps<TData>;
@@ -87,7 +87,7 @@ function buildMarks(state, env) {
87
87
  };
88
88
  /** The label of a series' tiles: the name over the share, fitted - or the share alone where the name does not fit. */
89
89
  const labelOf = (labels) => {
90
- const shape = labels.shapeLabel(env, (row) => `${contextOf(row).name}
90
+ const shape = labels.label(env, (row) => `${contextOf(row).name}
91
91
  ${shareText(row)}`, { fontSize: look.fontSize });
92
92
  return {
93
93
  ...shape,
@@ -155,7 +155,7 @@ export function buildTreemapDefaults() {
155
155
  }),
156
156
  fit: () => ({ x: { domain: [0, 1], fitted: true }, y: { domain: [0, 1], fitted: true } })
157
157
  },
158
- styles: { values: { show: true } },
158
+ styles: { values: { show: true, layout: 'box' } },
159
159
  markers: { marks: buildMarks },
160
160
  hover: {
161
161
  strategy: 'punctual',
@@ -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 { buildBarDefaults } from './defaults';
4
4
  import type { BarPlotProps, BarPlotCoreProps } from '../../../types/plots/ranking/bar/props';
5
5
 
@@ -24,7 +24,7 @@
24
24
  }: BarPlotProps<TData> = $props();
25
25
  </script>
26
26
 
27
- <Plot
27
+ <AbstractPlot
28
28
  core={core as BarPlotCoreProps<TData>}
29
29
  defaults={buildBarDefaults}
30
30
  bind:margins
@@ -1,4 +1,3 @@
1
- import Plot from '../../../layout/plot/Plot.svelte';
2
1
  import type { BarPlotProps } from '../../../types/plots/ranking/bar/props';
3
2
  declare function $$render<TData extends Record<string, unknown>>(): {
4
3
  props: BarPlotProps<TData>;
@@ -59,17 +59,27 @@ export function buildBarDefaults() {
59
59
  }),
60
60
  fit: (state) => fitValueDomain(state)
61
61
  },
62
+ transforms: { labels: {} },
62
63
  styles: {
63
64
  values: {
65
+ layout: (state) => ProcessedPositions.span(state.processedData, PlotAxes.valueOf(state))
66
+ .stacked
67
+ ? 'box'
68
+ : 'path',
69
+ layouts: {
70
+ path: { edge: 'tip', side: 'outside' },
71
+ box: { anchor: 'middle' },
72
+ point: { anchor: 'top', side: 'above' }
73
+ },
64
74
  // An `'outside'` label sits past its bar's end, which the tallest bar takes to the edge.
65
75
  reach: (labels, state) => {
66
- if (labels.anchor !== 'outside')
76
+ if (!labels.pastTip)
67
77
  return [];
68
78
  const axis = PlotAxes.valueOf(state);
69
79
  if (ProcessedPositions.span(state.processedData, axis).stacked)
70
80
  return [];
71
81
  if (!state.axes?.isHorizontal) {
72
- const offset = labels.offset('y');
82
+ const offset = labels.offset();
73
83
  const boxes = [
74
84
  labels.edgeBox('0', { x: 'middle', y: 'before' }, { y: -offset }, ['top'])
75
85
  ];
@@ -82,7 +92,7 @@ export function buildBarDefaults() {
82
92
  return boxes;
83
93
  }
84
94
  // A bar below zero reaches out to the left, with its label turned that way.
85
- const offset = labels.offset('x');
95
+ const offset = labels.offset();
86
96
  return labels
87
97
  .extremes(state.resolvedSeries ?? [], PlotAxes.valueOf(state), 'both')
88
98
  .map(({ series, value }) => value < 0
@@ -4,7 +4,6 @@ import { GAP_KIND, GAP_VALUE, SERIES_ID_KEY } from '../../../phases/transforms/T
4
4
  import { seriesIdOf } from '../../../utils/grouping';
5
5
  import { resolveGapAreaStyle } from '../../utils/gaps';
6
6
  import { PlotAxes } from '../../../phases/data/PlotAxes';
7
- import { ValueLabels } from '../../../layout/styles/ValueLabels';
8
7
  const GAP_KINDS = ['start', 'middle', 'end'];
9
8
  /**
10
9
  * Where a bar plot's bars sit, read off the state its phases leave: each
@@ -58,11 +57,11 @@ hover, env) {
58
57
  data: [row],
59
58
  category: categoryOf(row),
60
59
  hover: hover.active && 'trigger',
61
- ...(slice
60
+ styles: (slice
62
61
  ? isHorizontal
63
62
  ? { insetTop: slice.start, insetBottom: slice.end }
64
63
  : { insetLeft: slice.start, insetRight: slice.end }
65
- : {})
64
+ : undefined)
66
65
  };
67
66
  });
68
67
  }
@@ -89,21 +88,14 @@ export function buildMarks(state, env) {
89
88
  // which stays a field key whenever the plot has no transforms.
90
89
  const valueOf = (channel) => (geometry.stacked || channel === undefined ? top : channel);
91
90
  const gapValue = valueOf(channels[valueAxis]);
92
- // What each bar writes in itself: its value, anchored along the bar by `anchor` - past its tip
93
- // (`'outside'`), just inside it (`'start'`), at its base (`'end'`) or in the middle. A stacked bar's
94
- // label is always in the middle of its segment.
95
- const labels = state.valueLabels?.show ? state.valueLabels : undefined;
96
- const valueRead = ValueLabels.valueOf(state);
97
- const turns = !!labels && !geometry.stacked && ValueLabels.followsSign(labels.anchor);
98
- const labelAnchor = (negative) => ValueLabels.boxAnchor(labels?.anchor ?? 'outside', {
99
- isHorizontal,
100
- negative,
101
- stacked: geometry.stacked
102
- });
103
- const labelFor = (name, slice, negative) => labels && {
104
- ...labels.shapeLabel(env, (d) => labels.format(Number(valueRead(d)), d, name), {
91
+ // What each bar writes about itself: its value, in the layout of `styles.values` - by default past its tip, or
92
+ // in the middle of a stacked bar's segment.
93
+ const labels = state.valueLabels?.show && state.valueLabels.writes('value') ? state.valueLabels : undefined;
94
+ const labelFor = (name, slice) => labels && {
95
+ ...labels.label(env, (d) => labels.text(d), {
105
96
  fill: 'currentColor',
106
- ...labelAnchor(negative)
97
+ labelled: state.labelled,
98
+ seriesColor: () => colorForSeries(name)
107
99
  }),
108
100
  bandSlice: slice
109
101
  };
@@ -155,18 +147,9 @@ export function buildMarks(state, env) {
155
147
  };
156
148
  const real = seg.data.filter(isReal);
157
149
  const value = valueOf(group.series[valueAxis]);
158
- // A label past the tip turns with the sign of its value, so the bars below zero are a marker of their own.
159
- const parts = turns
160
- ? [false, true].map((negative) => ({
161
- negative,
162
- rows: real.filter((row) => Number(valueRead(row)) < 0 === negative)
163
- }))
164
- : [{ negative: false, rows: real }];
165
- for (const { negative, rows } of parts) {
166
- const marker = makeMarker(rows, slice, value, styles, labelFor(name, slice, negative));
167
- if (marker)
168
- segmentMarkers.push(marker);
169
- }
150
+ const marker = makeMarker(real, slice, value, styles, labelFor(name, slice));
151
+ if (marker)
152
+ segmentMarkers.push(marker);
170
153
  }
171
154
  const id = seriesIdOf(group.series);
172
155
  const fills = [...(gapFillMaps.get(id)?.values() ?? [])];
@@ -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 { buildBulletDefaults } from './defaults';
4
4
  import type { BulletPlotProps, BulletPlotCoreProps } from '../../../types/plots/ranking/bullet/props';
5
5
 
@@ -25,7 +25,7 @@
25
25
  }: BulletPlotProps<TData> = $props();
26
26
  </script>
27
27
 
28
- <Plot
28
+ <AbstractPlot
29
29
  core={core as BulletPlotCoreProps<TData>}
30
30
  defaults={buildBulletDefaults}
31
31
  bind:margins
@@ -1,4 +1,3 @@
1
- import Plot from '../../../layout/plot/Plot.svelte';
2
1
  import type { BulletPlotProps } from '../../../types/plots/ranking/bullet/props';
3
2
  declare function $$render<TData extends Record<string, unknown>>(): {
4
3
  props: BulletPlotProps<TData>;
@@ -1,3 +1,5 @@
1
+ import { ValueLabels } from '../../../layout/styles/ValueLabels';
2
+ import { RowNumbers } from '../../utils/RowNumbers';
1
3
  import { buildBarDefaults, fitValueDomain } from '../bar/defaults';
2
4
  import { buildMarks, buildReach } from './markers';
3
5
  /**
@@ -19,6 +21,24 @@ export function buildBulletDefaults() {
19
21
  }),
20
22
  fit: (state, env) => fitValueDomain(state, buildReach(state, env))
21
23
  },
24
+ // Each bar's target and range bounds, for its tooltip, hover and value labels to read.
25
+ context: (state) => ((row) => ({
26
+ target: RowNumbers.of(state.target, row),
27
+ ranges: (state.ranges ?? [])
28
+ .map((range) => RowNumbers.of(range, row))
29
+ .filter((bound) => bound !== undefined)
30
+ .sort((a, b) => a - b)
31
+ })),
32
+ styles: {
33
+ ...bar.styles,
34
+ values: {
35
+ ...bar.styles?.values,
36
+ parts: {
37
+ placements: (state) => ValueLabels.besideBand(state, ['target', 'ranges']),
38
+ default: ['value']
39
+ }
40
+ }
41
+ },
22
42
  markers: { ...bar.markers, marks: buildMarks }
23
43
  };
24
44
  }
@@ -1,3 +1,4 @@
1
+ import { ValueLabels } from '../../../layout/styles/ValueLabels';
1
2
  import { buildMarks as buildBarMarks, buildBarGeometry } from '../bar/markers';
2
3
  import { GAP_VALUE } from '../../../phases/transforms/TransformsPhase';
3
4
  import { RowNumbers } from '../../utils/RowNumbers';
@@ -85,5 +86,10 @@ export function buildMarks(state, env) {
85
86
  }
86
87
  }
87
88
  ];
88
- return [...ranges, ...bars, ...ticks];
89
+ // Its other parts `styles.values` labels - the target, the range bounds - beside the band; the bars write the value.
90
+ const geometry = buildBarGeometry(state, env.cfg.bar.seriesPadding);
91
+ const labels = state.valueLabels?.show
92
+ ? state.valueLabels.partMarkers(env, (state.processedData?.data ?? []).filter((row) => row[GAP_VALUE] === undefined && geometry.spanOf(row) !== undefined), ValueLabels.bandPoint(state, geometry.categoryOf), { skip: ['value'] })
93
+ : [];
94
+ return [...ranges, ...bars, ...ticks, ...labels];
89
95
  }
@@ -1,6 +1,6 @@
1
1
  import { ValueLabels } from '../../../layout/styles/ValueLabels';
2
2
  import type { ClevelandPlotState, ClevelandPlotEnv, ClevelandOptions } from '../../../types/plots/ranking/cleveland/state';
3
- import type { AxisBasedMarkersConfig, TextMarkerConfig } from '../../../types/markers/common';
3
+ import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
4
4
  import type { EdgeBox } from '../../../types/layout/box';
5
5
  /**
6
6
  * The marks of the Cleveland family of plots, which differ only by their
@@ -8,7 +8,7 @@ import type { EdgeBox } from '../../../types/layout/box';
8
8
  * highest value (from the baseline, for a lollipop) with a dot at each value.
9
9
  * A category with one dot gets a stem in that dot's series color, restyled by
10
10
  * the segment its dot belongs to; a category with several gets one neutral
11
- * connector. The "markers" phase takes its `marks`, `valueLabels` and `reach`.
11
+ * connector. The "markers" phase takes its `marks` and `reach`.
12
12
  */
13
13
  export declare class ClevelandMarkers {
14
14
  #private;
@@ -17,13 +17,6 @@ export declare class ClevelandMarkers {
17
17
  static resetWarnings(): void;
18
18
  /** The plot's connectors, then its dots, which draw on top of them. */
19
19
  marks: <TData extends Record<string, unknown>>(state: ClevelandPlotState<TData>, env: ClevelandPlotEnv) => AxisBasedMarkersConfig[];
20
- /**
21
- * The value labels of the dots `styles.values.of` picks, one `'text'` marker
22
- * per place a dot can hold in its range - past the top end (above, or right
23
- * of it), past the bottom end (below, or left of it) and, for the dots
24
- * between, beside it - and per series, when they take their series' color.
25
- */
26
- valueLabels: <TData extends Record<string, unknown>>(labels: ValueLabels, state: ClevelandPlotState<TData>, env: ClevelandPlotEnv) => TextMarkerConfig[];
27
20
  /**
28
21
  * Room for the labels past the dots at the end of the value axis, so the
29
22
  * farthest one fits inside the figure instead of crossing its edge. Only the
@@ -1,3 +1,4 @@
1
+ import { isGapValue } from '../../utils/gaps';
1
2
  import { PlotAxes } from '../../../phases/data/PlotAxes';
2
3
  import { ProcessedPositions } from '../../../phases/transforms/ProcessedPositions';
3
4
  import { SERIES_KEY } from '../../../phases/transforms/TransformsPhase';
@@ -5,6 +6,7 @@ import { ValueLabels } from '../../../layout/styles/ValueLabels';
5
6
  import { ClevelandRanges } from './ClevelandRanges';
6
7
  import { ClevelandConnector } from './ClevelandConnector';
7
8
  import { BasicStyles } from '../../../layout/styles/BasicStyles';
9
+ import { PointLayout } from '../../../markers/labels/layouts/PointLayout';
8
10
  /** The messages already warned about, so a plot that redraws warns once. */
9
11
  const warned = new Set();
10
12
  /**
@@ -13,7 +15,7 @@ const warned = new Set();
13
15
  * highest value (from the baseline, for a lollipop) with a dot at each value.
14
16
  * A category with one dot gets a stem in that dot's series color, restyled by
15
17
  * the segment its dot belongs to; a category with several gets one neutral
16
- * connector. The "markers" phase takes its `marks`, `valueLabels` and `reach`.
18
+ * connector. The "markers" phase takes its `marks` and `reach`.
17
19
  */
18
20
  export class ClevelandMarkers {
19
21
  #options;
@@ -202,12 +204,24 @@ export class ClevelandMarkers {
202
204
  };
203
205
  });
204
206
  });
207
+ const values = state.valueLabels?.show ? state.valueLabels : undefined;
208
+ const labels = values?.writes('value') ? this.#valueLabels(values, state, env) : undefined;
209
+ // The other parts of a row it labels - an interval's ends - beside them; the dots write the value.
210
+ const processed = state.processedData;
211
+ const position = ProcessedPositions.of(processed, PlotAxes.valueOf(state));
212
+ const parts = values
213
+ ? values.partMarkers(env, processed.data.filter((row) => !isGapValue(position[PlotAxes.valueOf(state)](row))), ValueLabels.bandPoint(state, position[PlotAxes.categoryOf(state)]), {
214
+ skip: ['value'],
215
+ seriesColor: (row) => state.colorForSeries(String(row[SERIES_KEY]))
216
+ })
217
+ : [];
205
218
  return [
206
219
  ...rules,
207
220
  ...caps,
208
221
  ...arrows,
209
- ...dots,
210
- ...this.#connectorLabels(state, env, connector, looks, isHorizontal)
222
+ ...ValueLabels.onMarkers(dots, 'dot', labels),
223
+ ...this.#connectorLabels(state, env, connector, looks, isHorizontal),
224
+ ...parts
211
225
  ];
212
226
  };
213
227
  /** The dash, cap and join a connector is drawn with, leaving out what is not set. */
@@ -282,68 +296,72 @@ export class ClevelandMarkers {
282
296
  ];
283
297
  }
284
298
  /**
285
- * The value labels of the dots `styles.values.of` picks, one `'text'` marker
286
- * per place a dot can hold in its range - past the top end (above, or right
287
- * of it), past the bottom end (below, or left of it) and, for the dots
288
- * between, beside it - and per series, when they take their series' color.
299
+ * The value labels of the dots `styles.values` picks, placed by where each dot sits in its range - or on the
300
+ * `side` the author asks of them all, or around each dot's rim (`'path'`).
289
301
  */
290
- valueLabels = (labels, state, env) => {
302
+ #valueLabels(labels, state, env) {
291
303
  if (!state.processedData?.data.length)
292
304
  return [];
293
305
  const { isHorizontal } = ClevelandMarkers.#geometry(state);
294
- const { category, value } = ClevelandMarkers.#channels(state);
295
306
  const { font, format } = labels;
296
307
  const read = ValueLabels.valueOf(state);
297
308
  const valueOf = ProcessedPositions.of(state.processedData, PlotAxes.valueOf(state))[PlotAxes.valueOf(state)];
298
- const gap = labels.offset(isHorizontal ? 'x' : 'y', env.cfg[this.#options.config].labelGap);
309
+ const gap = labels.offset(env.cfg[this.#options.config].labelGap);
299
310
  const nameOf = (d) => String(d[SERIES_KEY]);
300
311
  const picked = this.#picked(state);
301
- // Where a label sits from its dot, by role: along the range (past an end) or across it (between).
302
- const placement = {
303
- max: isHorizontal
304
- ? { dx: gap, dy: 0, textAnchor: 'start', lineAnchor: 'middle' }
305
- : { dx: 0, dy: -gap, textAnchor: 'middle', lineAnchor: 'bottom' },
306
- min: isHorizontal
307
- ? { dx: -gap, dy: 0, textAnchor: 'end', lineAnchor: 'middle' }
308
- : { dx: 0, dy: gap, textAnchor: 'middle', lineAnchor: 'top' },
309
- between: isHorizontal
310
- ? { dx: 0, dy: -gap, textAnchor: 'middle', lineAnchor: 'bottom' }
311
- : { dx: gap, dy: 0, textAnchor: 'start', lineAnchor: 'middle' }
312
- };
313
- // What fills the labels of a series: the one fill all share, its own color, or the axis'.
314
- const fillOf = (series) => labels.fill ??
315
- (labels.color === 'series' ? state.colorForSeries(series) : undefined) ??
316
- 'currentColor';
317
- const names = labels.color === 'series' && !labels.fill
318
- ? (state.resolvedSeries ?? []).map((s) => s.name)
319
- : [''];
320
- return ['max', 'min', 'between'].flatMap((role) => names.flatMap((name) => {
321
- const data = picked
322
- .filter(({ entry, dot }) => ClevelandRanges.roleOf(entry, dot.value) === role &&
323
- (name === '' || dot.series === name))
324
- .map(({ dot }) => dot.row);
325
- if (data.length === 0)
312
+ // Along the range past an end, across it between them.
313
+ const side = labels.placement.side;
314
+ const own = side && { ...PointLayout.sideOf(side, gap) };
315
+ const placement = own
316
+ ? { max: own, min: own, between: own }
317
+ : {
318
+ max: isHorizontal
319
+ ? { dx: gap, dy: 0, textAnchor: 'start', lineAnchor: 'middle' }
320
+ : { dx: 0, dy: -gap, textAnchor: 'middle', lineAnchor: 'bottom' },
321
+ min: isHorizontal
322
+ ? { dx: -gap, dy: 0, textAnchor: 'end', lineAnchor: 'middle' }
323
+ : { dx: 0, dy: gap, textAnchor: 'middle', lineAnchor: 'top' },
324
+ between: isHorizontal
325
+ ? { dx: 0, dy: -gap, textAnchor: 'middle', lineAnchor: 'bottom' }
326
+ : { dx: gap, dy: 0, textAnchor: 'start', lineAnchor: 'middle' }
327
+ };
328
+ const fill = labels.fill ??
329
+ (labels.color === 'series'
330
+ ? (d) => state.colorForSeries(nameOf(d))
331
+ : 'currentColor');
332
+ if (labels.layout === 'path') {
333
+ const text = (d) => format(Number(read(d) ?? valueOf(d)), d, nameOf(d));
334
+ const rim = labels.label(env, text, { fontSize: 12 });
335
+ return [{ ...rim, styles: { ...rim.styles, fill: font?.fill ?? fill } }];
336
+ }
337
+ const { styles: shared, ...hooks } = labels.hooks(env);
338
+ return ['max', 'min', 'between'].flatMap((role) => {
339
+ const rows = new Set(picked
340
+ .filter(({ entry, dot }) => ClevelandRanges.roleOf(entry, dot.value) === role)
341
+ .map(({ dot }) => dot.row));
342
+ if (rows.size === 0)
326
343
  return [];
327
344
  const at = placement[role];
328
345
  return [
329
346
  {
330
- type: 'text',
331
- data,
332
- ...(isHorizontal ? { x: value, y: category } : { x: category, y: value }),
347
+ ...hooks,
348
+ layout: 'point',
349
+ select: (row) => rows.has(row),
333
350
  text: (d) => format(Number(read(d) ?? valueOf(d)), d, nameOf(d)),
334
351
  styles: {
352
+ ...shared,
335
353
  ...font,
336
- fill: fillOf(name),
354
+ fill,
337
355
  fontSize: font?.fontSize ?? 12,
338
- dx: font?.dx ?? at.dx,
339
- dy: font?.dy ?? at.dy,
340
- textAnchor: labels.textAnchor ?? at.textAnchor,
341
- lineAnchor: font?.lineAnchor ?? at.lineAnchor
356
+ dx: at.dx,
357
+ dy: at.dy,
358
+ textAnchor: at.textAnchor,
359
+ lineAnchor: at.lineAnchor
342
360
  }
343
361
  }
344
362
  ];
345
- }));
346
- };
363
+ });
364
+ }
347
365
  /**
348
366
  * Room for the labels past the dots at the end of the value axis, so the
349
367
  * farthest one fits inside the figure instead of crossing its edge. Only the
@@ -355,10 +373,10 @@ export class ClevelandMarkers {
355
373
  if (top.length === 0)
356
374
  return [];
357
375
  if (!state.axes?.isHorizontal) {
358
- const at = { y: -labels.offset('y', gap) };
376
+ const at = { y: -labels.offset(gap) };
359
377
  return [labels.edgeBox('0', { x: 'middle', y: 'before' }, at, ['top'])];
360
378
  }
361
- const at = { x: labels.offset('x', gap) };
379
+ const at = { x: labels.offset(gap) };
362
380
  const texts = new Set(top.map(({ dot }) => labels.format(dot.value, dot.row, dot.series)));
363
381
  return [...texts].map((text) => labels.edgeBox(text, { x: 'after', y: 'middle' }, at, ['right']));
364
382
  };
@@ -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 { buildClevelandDefaults } from './defaults';
4
4
  import type {
5
5
  ClevelandPlotProps,
@@ -28,7 +28,7 @@
28
28
  }: ClevelandPlotProps<TData> = $props();
29
29
  </script>
30
30
 
31
- <Plot
31
+ <AbstractPlot
32
32
  core={core as ClevelandPlotCoreProps<TData>}
33
33
  defaults={buildClevelandDefaults}
34
34
  bind:margins
@@ -1,4 +1,3 @@
1
- import Plot from '../../../layout/plot/Plot.svelte';
2
1
  import type { ClevelandPlotProps } from '../../../types/plots/ranking/cleveland/props';
3
2
  declare function $$render<TData extends Record<string, unknown>>(): {
4
3
  props: ClevelandPlotProps<TData>;
@@ -1,3 +1,5 @@
1
+ import { ValueLabels } from '../../../layout/styles/ValueLabels';
2
+ import { RowNumbers } from '../../utils/RowNumbers';
1
3
  import { ClevelandMarkers } from './ClevelandMarkers';
2
4
  import { HoverDefaults } from '../../../layout/hover/HoverDefaults';
3
5
  import { ClevelandRanges } from './ClevelandRanges';
@@ -63,8 +65,37 @@ export function buildClevelandDefaults(options = { name: 'ClevelandPlot', config
63
65
  },
64
66
  // A dot is labelled by what its category's dots say: the first, the highest... of each.
65
67
  transforms: { labels: { group: 'category', skip: ClevelandRanges.skipGaps } },
66
- styles: { values: { reach: markers.reach } },
67
- markers: { marks: markers.marks, valueLabels: markers.valueLabels },
68
+ // An interval's ends, for its tooltip, hover and value labels to read.
69
+ context: options.interval
70
+ ? (state) => {
71
+ const valueOf = ValueLabels.valueOf(state);
72
+ return ((row) => {
73
+ const [lower, upper] = RowNumbers.interval(state, row, Number(valueOf(row)));
74
+ return { lower, upper };
75
+ });
76
+ }
77
+ : undefined,
78
+ styles: {
79
+ values: {
80
+ layout: 'point',
81
+ layouts: { path: { side: 'outside' } },
82
+ // An interval's ends sit past it, each on its own side.
83
+ parts: options.interval
84
+ ? {
85
+ placements: (state) => {
86
+ const sideways = state.axes?.isHorizontal;
87
+ return {
88
+ lower: { side: sideways ? 'before' : 'below' },
89
+ upper: { side: sideways ? 'after' : 'above' }
90
+ };
91
+ },
92
+ default: ['value']
93
+ }
94
+ : undefined,
95
+ reach: (labels, state) => (labels.layout === 'point' ? markers.reach(labels, state) : [])
96
+ }
97
+ },
98
+ markers: { marks: markers.marks },
68
99
  // A rule along the value axis at the hovered dot, and a dot and a label on each one of its category.
69
100
  hover: { marker: (state) => HoverDefaults.valueRule(PlotAxes.valueOf(state)) }
70
101
  };
@@ -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 { buildDumbbellDefaults } from './defaults';
4
4
  import type {
5
5
  DumbbellPlotProps,
@@ -28,7 +28,7 @@
28
28
  }: DumbbellPlotProps<TData> = $props();
29
29
  </script>
30
30
 
31
- <Plot
31
+ <AbstractPlot
32
32
  core={core as DumbbellPlotCoreProps<TData>}
33
33
  defaults={buildDumbbellDefaults}
34
34
  bind:margins
@@ -1,4 +1,3 @@
1
- import Plot from '../../../layout/plot/Plot.svelte';
2
1
  import type { DumbbellPlotProps } from '../../../types/plots/ranking/dumbbell/props';
3
2
  declare function $$render<TData extends Record<string, unknown>>(): {
4
3
  props: DumbbellPlotProps<TData>;