@fundar/data-chart-telling 0.0.25 → 0.0.27

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 (198) hide show
  1. package/dist/charts/BaseChart.svelte +18 -10
  2. package/dist/charts/BaseChart.svelte.d.ts +4 -2
  3. package/dist/charts/bar/Chart.svelte +2 -0
  4. package/dist/charts/heatmap/Chart.svelte +5 -3
  5. package/dist/charts/heatmap/Chart.svelte.d.ts +1 -2
  6. package/dist/charts/line/Chart.svelte +2 -0
  7. package/dist/charts/pyramid/Chart.svelte +5 -3
  8. package/dist/charts/pyramid/Chart.svelte.d.ts +1 -2
  9. package/dist/charts/scatter/Chart.svelte +95 -0
  10. package/dist/charts/scatter/Chart.svelte.d.ts +28 -0
  11. package/dist/configuration/config.svelte.js +28 -0
  12. package/dist/configuration/themes/dark.js +4 -0
  13. package/dist/configuration/themes/index.d.ts +112 -0
  14. package/dist/index.d.ts +26 -16
  15. package/dist/index.js +11 -3
  16. package/dist/layout/coordinates/CoordinatesLayout.svelte +93 -0
  17. package/dist/layout/{plot/RuleLayout.svelte.d.ts → coordinates/CoordinatesLayout.svelte.d.ts} +13 -16
  18. package/dist/layout/coordinates/margins.d.ts +16 -0
  19. package/dist/layout/coordinates/margins.js +11 -0
  20. package/dist/{plots/geo → layout/coordinates}/projections/argentina.d.ts +1 -1
  21. package/dist/layout/{geo → coordinates}/resolveProjection.d.ts +1 -1
  22. package/dist/layout/coordinates/resolveProjection.js +24 -0
  23. package/dist/{plots/geo → layout/coordinates}/zoom.svelte.d.ts +1 -1
  24. package/dist/layout/facet/FacetIndicator.svelte +31 -0
  25. package/dist/layout/facet/FacetIndicator.svelte.d.ts +8 -0
  26. package/dist/layout/facet/FacetLayout.svelte +249 -24
  27. package/dist/layout/facet/FacetLayout.svelte.d.ts +7 -1
  28. package/dist/layout/geometry/GeometryLayout.svelte +231 -0
  29. package/dist/layout/geometry/GeometryLayout.svelte.d.ts +62 -0
  30. package/dist/layout/legend/ContinuousSection.svelte +1 -1
  31. package/dist/layout/legend/ContinuousSection.svelte.d.ts +1 -1
  32. package/dist/layout/legend/DiscreteSection.svelte +1 -1
  33. package/dist/layout/legend/DiscreteSection.svelte.d.ts +1 -1
  34. package/dist/layout/legend/LegendLayout.svelte +1 -1
  35. package/dist/layout/legend/LegendLayout.svelte.d.ts +1 -1
  36. package/dist/layout/legend/interaction.svelte.d.ts +9 -1
  37. package/dist/layout/legend/interaction.svelte.js +9 -1
  38. package/dist/layout/plot/BasePlotLayout.svelte +142 -155
  39. package/dist/layout/plot/BasePlotLayout.svelte.d.ts +61 -51
  40. package/dist/layout/plot/margins.d.ts +0 -16
  41. package/dist/layout/plot/margins.js +0 -11
  42. package/dist/layout/{plot/AxisLayout.svelte → scales/ScalesLayout.svelte} +23 -16
  43. package/dist/layout/{plot/AxisLayout.svelte.d.ts → scales/ScalesLayout.svelte.d.ts} +4 -5
  44. package/dist/layout/scales/buildScale.d.ts +8 -0
  45. package/dist/layout/scales/buildScale.js +43 -0
  46. package/dist/layout/styles/StylesLayout.svelte +75 -0
  47. package/dist/layout/styles/StylesLayout.svelte.d.ts +15 -0
  48. package/dist/{plots/geo → layout/styles}/TileLayer.svelte +2 -2
  49. package/dist/{plots/geo → layout/styles}/TileLayer.svelte.d.ts +1 -1
  50. package/dist/layout/timeline/Timeline.svelte +1 -1
  51. package/dist/layout/timeline/Timeline.svelte.d.ts +1 -1
  52. package/dist/layout/timeline/TimelineLayout.svelte +1 -1
  53. package/dist/layout/timeline/TimelineLayout.svelte.d.ts +1 -1
  54. package/dist/layout/tooltip/Tooltip.svelte +16 -20
  55. package/dist/layout/tooltip/Tooltip.svelte.d.ts +5 -0
  56. package/dist/layout/tooltip/TooltipLayout.svelte +7 -1
  57. package/dist/layout/tooltip/TooltipLayout.svelte.d.ts +4 -0
  58. package/dist/layout/tooltip/controller.svelte.d.ts +4 -5
  59. package/dist/layout/tooltip/controller.svelte.js +42 -16
  60. package/dist/layout/tooltip/resolveTooltipPlacement.d.ts +24 -0
  61. package/dist/layout/tooltip/resolveTooltipPlacement.js +49 -0
  62. package/dist/layout/tooltip/utils.d.ts +27 -19
  63. package/dist/layout/tooltip/utils.js +16 -15
  64. package/dist/markers/BarMarker.svelte +92 -0
  65. package/dist/markers/BarMarker.svelte.d.ts +7 -0
  66. package/dist/markers/CellMarker.svelte +21 -0
  67. package/dist/markers/CellMarker.svelte.d.ts +7 -0
  68. package/dist/markers/ContourMarker.svelte +28 -0
  69. package/dist/markers/ContourMarker.svelte.d.ts +7 -0
  70. package/dist/markers/DeltaMarker.svelte +32 -39
  71. package/dist/markers/DeltaMarker.svelte.d.ts +4 -5
  72. package/dist/markers/DotMarker.svelte +35 -6
  73. package/dist/markers/DotMarker.svelte.d.ts +11 -2
  74. package/dist/markers/GeoFeaturesMarker.svelte +19 -0
  75. package/dist/markers/GeoFeaturesMarker.svelte.d.ts +7 -0
  76. package/dist/markers/GeoHoverMarker.svelte +25 -0
  77. package/dist/markers/GeoHoverMarker.svelte.d.ts +9 -0
  78. package/dist/markers/GraticuleMarker.svelte +18 -0
  79. package/dist/markers/GraticuleMarker.svelte.d.ts +7 -0
  80. package/dist/markers/HoverMarker.svelte +34 -35
  81. package/dist/markers/HoverMarker.svelte.d.ts +15 -9
  82. package/dist/markers/LineMarker.svelte +19 -0
  83. package/dist/markers/LineMarker.svelte.d.ts +7 -0
  84. package/dist/markers/LinkMarker.svelte +42 -0
  85. package/dist/markers/LinkMarker.svelte.d.ts +7 -0
  86. package/dist/markers/RuleMarker.svelte +24 -0
  87. package/dist/markers/RuleMarker.svelte.d.ts +10 -0
  88. package/dist/markers/SphereMarker.svelte +15 -0
  89. package/dist/markers/SphereMarker.svelte.d.ts +7 -0
  90. package/dist/markers/TextMarker.svelte +1 -1
  91. package/dist/markers/TextMarker.svelte.d.ts +1 -1
  92. package/dist/markers/VectorMarker.svelte +20 -0
  93. package/dist/markers/VectorMarker.svelte.d.ts +7 -0
  94. package/dist/markers/inset/InsetMarker.svelte +38 -45
  95. package/dist/markers/inset/InsetMarker.svelte.d.ts +6 -2
  96. package/dist/plots/bar/Plot.svelte +96 -87
  97. package/dist/plots/bar/Plot.svelte.d.ts +4 -5
  98. package/dist/plots/bar/ValueLabels.svelte +70 -24
  99. package/dist/plots/bar/ValueLabels.svelte.d.ts +11 -2
  100. package/dist/plots/bar/buildBarMarkers.d.ts +30 -0
  101. package/dist/plots/bar/buildBarMarkers.js +100 -0
  102. package/dist/plots/bar/layout.svelte.d.ts +1 -2
  103. package/dist/plots/bar/layout.svelte.js +1 -1
  104. package/dist/plots/geo/Plot.svelte +113 -387
  105. package/dist/plots/geo/Plot.svelte.d.ts +5 -4
  106. package/dist/plots/geo/buildGeoMarkers.d.ts +33 -0
  107. package/dist/plots/geo/buildGeoMarkers.js +47 -0
  108. package/dist/plots/heatmap/Plot.svelte +27 -52
  109. package/dist/plots/heatmap/Plot.svelte.d.ts +4 -5
  110. package/dist/plots/heatmap/ValueLabels.svelte +5 -5
  111. package/dist/plots/heatmap/ValueLabels.svelte.d.ts +3 -3
  112. package/dist/plots/heatmap/buildCellMarkers.d.ts +21 -0
  113. package/dist/plots/heatmap/buildCellMarkers.js +25 -0
  114. package/dist/plots/line/Plot.svelte +73 -141
  115. package/dist/plots/line/Plot.svelte.d.ts +4 -4
  116. package/dist/plots/line/ValueLabels.svelte +84 -85
  117. package/dist/plots/line/ValueLabels.svelte.d.ts +10 -1
  118. package/dist/plots/line/buildLineMarkers.d.ts +15 -0
  119. package/dist/plots/line/buildLineMarkers.js +57 -0
  120. package/dist/plots/pyramid/Plot.svelte +80 -121
  121. package/dist/plots/pyramid/Plot.svelte.d.ts +4 -5
  122. package/dist/plots/pyramid/ValueLabels.svelte +71 -25
  123. package/dist/plots/pyramid/ValueLabels.svelte.d.ts +11 -2
  124. package/dist/plots/pyramid/buildPyramidBarMarkers.d.ts +19 -0
  125. package/dist/plots/pyramid/buildPyramidBarMarkers.js +32 -0
  126. package/dist/plots/scatter/Plot.svelte +242 -0
  127. package/dist/{layout/plot/GridLayout.svelte.d.ts → plots/scatter/Plot.svelte.d.ts} +9 -7
  128. package/dist/plots/scatter/ValueLabels.svelte +149 -0
  129. package/dist/plots/{bar/BarSegments.svelte.d.ts → scatter/ValueLabels.svelte.d.ts} +17 -15
  130. package/dist/plots/scatter/buildScatterMarkers.d.ts +11 -0
  131. package/dist/plots/scatter/buildScatterMarkers.js +37 -0
  132. package/dist/plots/scatter/resolveRadiusPaddedDomain.d.ts +10 -0
  133. package/dist/plots/scatter/resolveRadiusPaddedDomain.js +23 -0
  134. package/dist/plots/utils/declutter.d.ts +55 -37
  135. package/dist/plots/utils/declutter.js +79 -64
  136. package/dist/plots/utils/geoAccessors.d.ts +1 -1
  137. package/dist/plots/utils/geoAccessors.js +1 -1
  138. package/dist/plots/utils/labelOverflow.svelte.d.ts +51 -0
  139. package/dist/plots/utils/labelOverflow.svelte.js +116 -0
  140. package/dist/plots/utils/legendDisabled.d.ts +1 -1
  141. package/dist/plots/utils/segments.d.ts +17 -10
  142. package/dist/plots/utils/segments.js +26 -16
  143. package/dist/types/charts/common.d.ts +3 -50
  144. package/dist/types/charts/props.d.ts +8 -8
  145. package/dist/types/configuration/styling.d.ts +26 -2
  146. package/dist/types/{plots/scales/geo.d.ts → layout/coordinates.d.ts} +33 -1
  147. package/dist/types/layout/facet.d.ts +41 -0
  148. package/dist/types/layout/geometry.d.ts +17 -0
  149. package/dist/types/layout/legend.d.ts +65 -1
  150. package/dist/types/{plots/axis.d.ts → layout/scales.d.ts} +11 -0
  151. package/dist/types/layout/styles.d.ts +115 -0
  152. package/dist/types/layout/timeline.d.ts +32 -0
  153. package/dist/types/layout/tooltip.d.ts +20 -1
  154. package/dist/types/markers/all.d.ts +11 -0
  155. package/dist/types/markers/all.js +1 -0
  156. package/dist/types/markers/base.d.ts +45 -0
  157. package/dist/types/markers/base.js +1 -0
  158. package/dist/types/markers/common.d.ts +64 -16
  159. package/dist/types/markers/geo.d.ts +75 -100
  160. package/dist/types/markers/line.d.ts +34 -0
  161. package/dist/types/markers/line.js +1 -0
  162. package/dist/types/markers/props.d.ts +1 -1
  163. package/dist/types/plots/data/common.d.ts +1 -1
  164. package/dist/types/plots/data/scatter.d.ts +13 -0
  165. package/dist/types/plots/data/scatter.js +1 -0
  166. package/dist/types/plots/props.d.ts +46 -74
  167. package/dist/types/plots/segments/common.d.ts +6 -12
  168. package/dist/types/plots/segments/config.d.ts +1 -1
  169. package/dist/types/plots/styling.d.ts +10 -0
  170. package/dist/{plots/utils → utils}/delta.d.ts +2 -2
  171. package/dist/{plots/utils → utils}/delta.js +1 -1
  172. package/dist/utils/edgeAwareAnchor.d.ts +37 -0
  173. package/dist/utils/edgeAwareAnchor.js +65 -0
  174. package/dist/utils/grouping.d.ts +1 -1
  175. package/dist/utils/interpolate.d.ts +2 -2
  176. package/package.json +3 -1
  177. package/dist/layout/geo/GeoLayout.svelte +0 -51
  178. package/dist/layout/geo/GeoLayout.svelte.d.ts +0 -24
  179. package/dist/layout/geo/resolveProjection.js +0 -48
  180. package/dist/layout/plot/GridLayout.svelte +0 -30
  181. package/dist/layout/plot/RuleLayout.svelte +0 -111
  182. package/dist/plots/bar/BarSegments.svelte +0 -69
  183. package/dist/types/charts/legend.d.ts +0 -60
  184. package/dist/types/plots/delta.d.ts +0 -19
  185. package/dist/types/plots/styles/common.d.ts +0 -52
  186. package/dist/types/plots/styles/geo.d.ts +0 -42
  187. /package/dist/{plots/geo → layout/coordinates}/projections/argentina.js +0 -0
  188. /package/dist/{plots/geo → layout/coordinates}/projections/fit.d.ts +0 -0
  189. /package/dist/{plots/geo → layout/coordinates}/projections/fit.js +0 -0
  190. /package/dist/{plots/geo → layout/coordinates}/rotate.svelte.d.ts +0 -0
  191. /package/dist/{plots/geo → layout/coordinates}/rotate.svelte.js +0 -0
  192. /package/dist/{plots/geo → layout/coordinates}/zoom.svelte.js +0 -0
  193. /package/dist/types/{charts/legend.js → layout/coordinates.js} +0 -0
  194. /package/dist/types/{plots/axis.js → layout/facet.js} +0 -0
  195. /package/dist/types/{plots/delta.js → layout/geometry.js} +0 -0
  196. /package/dist/types/{plots/scales/geo.js → layout/scales.js} +0 -0
  197. /package/dist/types/{plots/styles/common.js → layout/styles.js} +0 -0
  198. /package/dist/types/{plots/styles/geo.js → layout/timeline.js} +0 -0
@@ -0,0 +1,47 @@
1
+ /**
2
+ * `graticule`/`sphere`/`contour` opt into drawing underneath the base map's
3
+ * own fill/stroke instead of on top of it — see each config's own `behind`
4
+ * doc comment.
5
+ */
6
+ function isBehindMarker(marker) {
7
+ return ((marker.type === 'graticule' || marker.type === 'sphere' || marker.type === 'contour') &&
8
+ marker.behind === true);
9
+ }
10
+ /**
11
+ * Every marker for one `GeoPlot` render, in final draw order: any authored
12
+ * marker with `behind: true`, then the base map itself (one `'geo'` marker
13
+ * per resolved feature style), then the rest of the authored markers in
14
+ * order, with any `hover` marker moved last.
15
+ */
16
+ export function buildGeoMarkers(args) {
17
+ const { styleGroups, resolveBaseFill, fillOpacity, stroke, strokeWidth, axisColor, onFeatureClick, markers, hoverActive, hoverPointById, isOverInset, setHoverMatch, clearHoverMatch, } = args;
18
+ const featureMarkers = styleGroups.map((group) => ({
19
+ type: 'geo',
20
+ data: group.data,
21
+ fill: (d) => group.style.fill ?? resolveBaseFill(d),
22
+ fillOpacity: group.style.fillOpacity ?? fillOpacity ?? 1,
23
+ stroke: group.style.stroke ?? stroke ?? axisColor,
24
+ strokeWidth: group.style.strokeWidth ?? strokeWidth ?? 0.5,
25
+ onclick: onFeatureClick ? (e, d) => onFeatureClick(d, e) : undefined,
26
+ onpointerenter: hoverActive
27
+ ? (e, d) => {
28
+ if (isOverInset(e))
29
+ return;
30
+ const hp = hoverPointById.get(d.__id);
31
+ if (hp)
32
+ setHoverMatch([hp.row]);
33
+ }
34
+ : undefined,
35
+ onpointerleave: hoverActive
36
+ ? (e) => {
37
+ if (isOverInset(e))
38
+ return;
39
+ clearHoverMatch();
40
+ }
41
+ : undefined,
42
+ }));
43
+ const behindMarkers = markers.filter(isBehindMarker);
44
+ const frontMarkers = markers.filter((m) => !isBehindMarker(m) && m.type !== 'geo-hover');
45
+ const hoverMarker = markers.find((m) => m.type === 'geo-hover');
46
+ return [...behindMarkers, ...featureMarkers, ...frontMarkers, ...(hoverMarker ? [hoverMarker] : [])];
47
+ }
@@ -1,5 +1,4 @@
1
1
  <script lang="ts" generics="TData extends Record<string, unknown>">
2
- import { Cell } from 'svelteplot';
3
2
  import { resolveAccessor } from '../utils/accessors';
4
3
  import { validateSegments } from '../utils/segments';
5
4
  import { buildSeries } from '../../utils/grouping';
@@ -7,19 +6,16 @@
7
6
  import { getConfiguration } from '../../configuration/config.svelte';
8
7
  import { getLegendInteraction } from '../../layout/legend/interaction.svelte';
9
8
  import { disabledFillOverride } from '../utils/legendDisabled';
9
+ import { buildCellMarkers } from './buildCellMarkers';
10
10
  import BasePlotLayout from '../../layout/plot/BasePlotLayout.svelte';
11
- import AxisLayout from '../../layout/plot/AxisLayout.svelte';
12
- import GridLayout from '../../layout/plot/GridLayout.svelte';
13
- import RuleLayout from '../../layout/plot/RuleLayout.svelte';
14
11
  import ValueLabels from './ValueLabels.svelte';
15
12
  import { hasHoverMarker, resolveHoverStrategy, resolveHoverSync } from '../../layout/tooltip/utils';
16
13
  import type { BasePlotProps } from '../../types/plots/props';
17
- import type { AxisValue } from '../../types/plots/axis';
14
+ import type { AxisValue, AxisBasedScalesConfig } from '../../types/layout/scales';
18
15
  import type { SeriesProps, DataProps } from '../../types/plots/data/common';
19
- import type { AxisBasedScalesConfig } from '../../types/plots/props';
20
- import type { BasePlotStyles } from '../../types/plots/styles/common';
16
+ import type { BasePlotStylesConfig } from '../../types/layout/styles';
21
17
  import type { CellSegmentStyle } from '../../types/plots/segments/common';
22
- import type { Marker } from '../../types/markers/common';
18
+ import type { AxisBasedMarkersConfig } from '../../types/markers/common';
23
19
 
24
20
  /**
25
21
  * A categorical grid of value-coloured cells. Accepts either a pre-built
@@ -33,9 +29,9 @@
33
29
  type Props = BasePlotProps<
34
30
  SeriesProps<TData> | DataProps<TData>,
35
31
  TData,
36
- BasePlotStyles,
32
+ BasePlotStylesConfig,
37
33
  CellSegmentStyle,
38
- Marker,
34
+ AxisBasedMarkersConfig,
39
35
  AxisBasedScalesConfig<TData>
40
36
  >;
41
37
 
@@ -77,7 +73,7 @@
77
73
  const minColor = $derived(styles.colors?.min ?? cfg.continuous.min);
78
74
  const maxColor = $derived(styles.colors?.max ?? cfg.continuous.max);
79
75
  const showVals = $derived(styles.values?.show ?? true);
80
- const fmtVal = $derived(styles.values?.format ?? ((v: number) => `${v}`));
76
+ const formatValue = $derived(styles.values?.format ?? ((v: number) => `${v}`));
81
77
 
82
78
  const resolvedSeries = $derived(series ?? buildSeries(data!, x!, y!, undefined));
83
79
  const flat = $derived(resolvedSeries.flatMap((s) => s.data));
@@ -131,7 +127,7 @@
131
127
  x: getX(d) as AxisValue,
132
128
  y: getY(d) as AxisValue,
133
129
  row: d,
134
- label: fmtVal(Number(getZ(d)), ''),
130
+ label: formatValue(Number(getZ(d)), ''),
135
131
  })),
136
132
  );
137
133
 
@@ -185,17 +181,10 @@
185
181
  );
186
182
 
187
183
  /**
188
- * One `<Cell>` call per *distinct resolved style*, not one call for the
189
- * whole dataset. svelteplot registers `fillOpacity`/`strokeWidth` under
190
- * its shared "opacity"/numeric scale machinery passed as a per-row
191
- * accessor that genuinely varies within a single mark instance, that
192
- * scale collapses every row to the same (wrong) value instead of the
193
- * literal numbers set on each segment. A literal, per-call constant (one
194
- * call per style bucket) bypasses that entirely, which is exactly how
195
- * bar/line avoid the same trap (one mark per visual segment). `fill`
196
- * stays a per-cell accessor within each group — unlike fillOpacity, it
197
- * must keep varying continuously with the underlying value wherever a
198
- * segment doesn't explicitly override it.
184
+ * One marker per *distinct resolved style*, not one for the whole
185
+ * dataset svelteplot's opacity/numeric scale machinery requires
186
+ * `fillOpacity`/`strokeWidth` to be constant per mark instance, unlike
187
+ * `fill`, which stays a per-row accessor.
199
188
  */
200
189
  type StyleGroup = { style: CellSegmentStyle; data: TData[] };
201
190
  const styleGroups = $derived.by((): StyleGroup[] => {
@@ -224,54 +213,40 @@
224
213
  y: { type: 'band', domain: yDomain, ...(scales.y ?? {}) },
225
214
  });
226
215
 
227
- const hoverActive = $derived(tooltip != null || hasHoverMarker(markers));
228
- const hoverStrategy = $derived(resolveHoverStrategy(tooltip?.strategy, markers, 'punctual'));
229
- const hoverSync = $derived(resolveHoverSync(tooltip?.sync, markers));
216
+ const hover = $derived({
217
+ active: tooltip != null || hasHoverMarker(markers),
218
+ points: hoverPoints,
219
+ label: (d: TData) => formatValue(Number(getZ(d)), ''),
220
+ strategy: resolveHoverStrategy(tooltip?.strategy, markers, 'punctual'),
221
+ sync: resolveHoverSync(tooltip?.sync, markers),
222
+ tooltip,
223
+ });
224
+
225
+ const cellMarkers = $derived(buildCellMarkers({ styleGroups, getX, getY, getZ, colorScale }));
230
226
  </script>
231
227
 
232
228
  <BasePlotLayout
233
229
  {width}
234
230
  {height}
231
+ {styles}
235
232
  data={flat}
236
233
  {getX}
237
234
  {getY}
238
- {hoverPoints}
239
- label={(d) => fmtVal(Number(getZ(d)), '')}
240
- {tooltip}
241
- {hoverActive}
242
- {hoverStrategy}
243
- {hoverSync}
235
+ {hover}
244
236
  scales={resolvedScales}
245
237
  {margins}
238
+ markers={[...cellMarkers, ...markers]}
239
+ seriesData={resolvedSeries}
246
240
  >
247
- {#snippet children({ matchedPoints, ruleX, ruleY, impliesRuleX, impliesRuleY, numericMargins })}
248
- <AxisLayout data={flat} {getX} {getY} {width} {height} {numericMargins} scales={resolvedScales} />
249
- <GridLayout scales={resolvedScales} />
250
- <RuleLayout {markers} seriesData={resolvedSeries} {matchedPoints} {ruleX} {ruleY} {impliesRuleX} {impliesRuleY} showHoverLabels={false}>
251
- {#each styleGroups as group, i (i)}
252
- <Cell
253
- data={group.data as unknown as Record<string | symbol, never>[]}
254
- x={getX}
255
- y={getY}
256
- fill={(d) => group.style.fill ?? colorScale(Number(getZ(d as TData)))}
257
- fillOpacity={group.style.fillOpacity ?? 1}
258
- stroke={group.style.stroke}
259
- strokeWidth={group.style.strokeWidth ?? 0}
260
- inset={0.5}
261
- />
262
- {/each}
263
-
264
241
  {#if showVals}
265
242
  <ValueLabels
266
243
  data={flat}
267
244
  {getX}
268
245
  {getY}
269
246
  {getZ}
270
- {fmtVal}
247
+ {formatValue}
271
248
  values={styles.values}
272
249
  defaultColor={cfg.continuous.labelColor}
273
250
  />
274
251
  {/if}
275
- </RuleLayout>
276
- {/snippet}
277
252
  </BasePlotLayout>
@@ -1,11 +1,10 @@
1
1
  import type { BasePlotProps } from '../../types/plots/props';
2
+ import type { AxisBasedScalesConfig } from '../../types/layout/scales';
2
3
  import type { SeriesProps, DataProps } from '../../types/plots/data/common';
3
- import type { AxisBasedScalesConfig } from '../../types/plots/props';
4
- import type { BasePlotStyles } from '../../types/plots/styles/common';
5
- import type { CellSegmentStyle } from '../../types/plots/segments/common';
6
- import type { Marker } from '../../types/markers/common';
4
+ import type { BasePlotStylesConfig } from '../../types/layout/styles';
5
+ import type { AxisBasedMarkersConfig } from '../../types/markers/common';
7
6
  declare function $$render<TData extends Record<string, unknown>>(): {
8
- props: BasePlotProps<SeriesProps<TData> | DataProps<TData>, TData, BasePlotStyles, CellSegmentStyle, Marker, AxisBasedScalesConfig<TData>>;
7
+ props: BasePlotProps<SeriesProps<TData> | DataProps<TData>, TData, BasePlotStylesConfig, import("../..").AreaStyle, AxisBasedMarkersConfig, AxisBasedScalesConfig<TData>>;
9
8
  exports: {};
10
9
  bindings: "";
11
10
  slots: {};
@@ -1,7 +1,7 @@
1
1
  <script lang="ts" generics="TData extends Record<string, unknown>">
2
2
  import { Text } from 'svelteplot';
3
- import type { AxisValue } from '../../types/plots/axis';
4
- import type { ValuesStyle } from '../../types/plots/styles/common';
3
+ import type { AxisValue } from '../../types/layout/scales';
4
+ import type { ValuesStyle } from '../../types/layout/styles';
5
5
 
6
6
  /**
7
7
  * Renders every cell's value label in one mark — heatmaps have no
@@ -14,7 +14,7 @@
14
14
  getX,
15
15
  getY,
16
16
  getZ,
17
- fmtVal,
17
+ formatValue,
18
18
  values,
19
19
  defaultColor,
20
20
  }: {
@@ -22,7 +22,7 @@
22
22
  getX: (d: TData) => AxisValue;
23
23
  getY: (d: TData) => AxisValue;
24
24
  getZ: (d: TData) => unknown;
25
- fmtVal: (value: number, seriesName: string) => string;
25
+ formatValue: (value: number, seriesName: string) => string;
26
26
  values: ValuesStyle | undefined;
27
27
  defaultColor: string;
28
28
  } = $props();
@@ -37,7 +37,7 @@
37
37
  {data}
38
38
  x={getX}
39
39
  y={getY}
40
- text={(d: TData) => fmtVal(Number(getZ(d)), '')}
40
+ text={(d: TData) => formatValue(Number(getZ(d)), '')}
41
41
  fill={font?.fill ?? defaultColor}
42
42
  fontSize={font?.fontSize ?? 12}
43
43
  fontWeight={font?.fontWeight ?? 600}
@@ -1,12 +1,12 @@
1
- import type { AxisValue } from '../../types/plots/axis';
2
- import type { ValuesStyle } from '../../types/plots/styles/common';
1
+ import type { AxisValue } from '../../types/layout/scales';
2
+ import type { ValuesStyle } from '../../types/layout/styles';
3
3
  declare function $$render<TData extends Record<string, unknown>>(): {
4
4
  props: {
5
5
  data: TData[];
6
6
  getX: (d: TData) => AxisValue;
7
7
  getY: (d: TData) => AxisValue;
8
8
  getZ: (d: TData) => unknown;
9
- fmtVal: (value: number, seriesName: string) => string;
9
+ formatValue: (value: number, seriesName: string) => string;
10
10
  values: ValuesStyle | undefined;
11
11
  defaultColor: string;
12
12
  };
@@ -0,0 +1,21 @@
1
+ import type { AxisValue } from '../../types/layout/scales';
2
+ import type { CellSegmentStyle } from '../../types/plots/segments/common';
3
+ import type { CellMarkerConfig } from '../../types/markers/common';
4
+ /** One resolved-style bucket of cells, as grouped by `HeatmapPlot`'s own `styleGroups`. */
5
+ export type CellStyleGroup<TData> = {
6
+ style: CellSegmentStyle;
7
+ data: TData[];
8
+ };
9
+ /**
10
+ * Every `'cell'` marker for one HeatmapPlot render: one marker per
11
+ * resolved-style bucket in `styleGroups`. `fill` stays a per-row accessor,
12
+ * falling back to `colorScale` wherever a segment doesn't set a literal
13
+ * `fill`.
14
+ */
15
+ export declare function buildCellMarkers<TData extends Record<string, unknown>>(args: {
16
+ styleGroups: CellStyleGroup<TData>[];
17
+ getX: (d: TData) => AxisValue;
18
+ getY: (d: TData) => AxisValue;
19
+ getZ: (d: TData) => AxisValue;
20
+ colorScale: (value: number) => string;
21
+ }): CellMarkerConfig[];
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Every `'cell'` marker for one HeatmapPlot render: one marker per
3
+ * resolved-style bucket in `styleGroups`. `fill` stays a per-row accessor,
4
+ * falling back to `colorScale` wherever a segment doesn't set a literal
5
+ * `fill`.
6
+ */
7
+ export function buildCellMarkers(args) {
8
+ const { styleGroups, getX, getY, getZ, colorScale } = args;
9
+ return styleGroups.map((group) => ({
10
+ type: 'cell',
11
+ data: group.data,
12
+ x: getX,
13
+ y: getY,
14
+ fill: (d) => group.style.fill ?? colorScale(Number(getZ(d))),
15
+ style: {
16
+ fillOpacity: group.style.fillOpacity ?? 1,
17
+ stroke: group.style.stroke,
18
+ strokeWidth: group.style.strokeWidth ?? 0,
19
+ strokeOpacity: group.style.strokeOpacity,
20
+ strokeDasharray: group.style.strokeDasharray,
21
+ borderRadius: group.style.borderRadius,
22
+ },
23
+ inset: 0.5,
24
+ }));
25
+ }
@@ -2,36 +2,32 @@
2
2
  lang="ts"
3
3
  generics="TData extends Record<string, unknown>"
4
4
  >
5
- import { SvelteMap } from 'svelte/reactivity';
6
- import { Line, Dot } from 'svelteplot';
7
5
  import { resolveAccessor } from '../utils/accessors';
8
6
  import { getConfiguration } from '../../configuration/config.svelte';
9
7
  import { paletteColor } from '../../utils/color';
10
8
  import { buildSeries } from '../../utils/grouping';
11
9
  import { getLegendInteraction } from '../../layout/legend/interaction.svelte';
12
10
  import { disabledStrokeOverride, disabledDotsOverride } from '../utils/legendDisabled';
11
+ import { buildLineMarkers } from './buildLineMarkers';
12
+ import { createLabelMarginTracker } from '../utils/labelOverflow.svelte';
13
13
  import BasePlotLayout from '../../layout/plot/BasePlotLayout.svelte';
14
- import AxisLayout from '../../layout/plot/AxisLayout.svelte';
15
- import GridLayout from '../../layout/plot/GridLayout.svelte';
16
- import RuleLayout from '../../layout/plot/RuleLayout.svelte';
17
- import HoverMarker from '../../markers/HoverMarker.svelte';
18
14
  import ValueLabels from './ValueLabels.svelte';
19
15
  import { hasHoverMarker, resolveHoverStrategy, resolveHoverSync } from '../../layout/tooltip/utils';
20
- import { buildGroupedSeries, validateSegments, resolveSegmentsForSeries, matchesSegmentX, getDataForSegments } from '../utils/segments';
16
+ import { buildGroupedSeries, validateSegments, buildScopedMarkerGroups } from '../utils/segments';
21
17
  import type { BasePlotProps } from '../../types/plots/props';
22
- import type { AxisValue } from '../../types/plots/axis';
18
+ import type { AxisValue, AxisBasedScalesConfig } from '../../types/layout/scales';
23
19
  import type { Series, SeriesProps, DataProps } from '../../types/plots/data/common';
24
- import type { AxisBasedScalesConfig } from '../../types/plots/props';
25
- import type { BasePlotStyles } from '../../types/plots/styles/common';
20
+ import type { BasePlotStylesConfig } from '../../types/layout/styles';
26
21
  import type { LineSegmentStyle } from '../../types/plots/segments/common';
27
- import type { Marker } from '../../types/markers/common';
22
+ import type { LineMarkersConfig } from '../../types/markers/line';
23
+ import type { LegendDisabledStyle } from '../../types/layout/legend';
28
24
 
29
25
  type Props = BasePlotProps<
30
26
  SeriesProps<TData> | DataProps<TData>,
31
27
  TData,
32
- BasePlotStyles,
28
+ BasePlotStylesConfig,
33
29
  LineSegmentStyle,
34
- Marker,
30
+ LineMarkersConfig,
35
31
  AxisBasedScalesConfig<TData>
36
32
  >;
37
33
 
@@ -60,19 +56,24 @@
60
56
  buildGroupedSeries<Series<TData>, LineSegmentStyle>(resolvedSeries, segments),
61
57
  );
62
58
 
63
- const seriesMarkers = $derived.by(() => {
64
- const map = new SvelteMap<string, Marker[]>();
65
- for (const marker of markers) {
66
- if ((marker.type === 'component' || marker.type === 'hover') && marker.series) {
67
- if (!map.has(marker.series)) map.set(marker.series, []);
68
- map.get(marker.series)!.push(marker);
69
- }
70
- }
71
- return map;
72
- });
73
-
74
59
  const cfg = $derived(getConfiguration());
75
60
  const showVals = $derived(styles.values?.show ?? false);
61
+ const hoverIsolate = $derived(styles.values?.hoverIsolate ?? false);
62
+
63
+ // A value label hovered while `styles.values.hoverIsolate` is on dims
64
+ // every other series exactly like a legend toggle would — local to this
65
+ // one `<Plot>` instance (not the shared HoverStore) so the gesture can't
66
+ // leak into unrelated tooltip/crosshair behavior.
67
+ let hoverIsolatedSeries = $state<string | null>(null);
68
+
69
+ function effectiveDisabledFor(name: string): LegendDisabledStyle | undefined {
70
+ return (
71
+ legendInteraction?.disabledSeries.get(name) ??
72
+ (hoverIsolate && hoverIsolatedSeries && hoverIsolatedSeries !== name
73
+ ? { opacity: cfg.legend.disabledOpacity }
74
+ : undefined)
75
+ );
76
+ }
76
77
 
77
78
  const xAcc = $derived(resolveAccessor(resolvedSeries[0]?.x ?? ((d: TData) => d)));
78
79
  const yAcc = $derived(resolveAccessor(resolvedSeries[0]?.y ?? ((d: TData) => d)));
@@ -102,134 +103,65 @@
102
103
  .filter(({ lastRow }) => lastRow !== undefined),
103
104
  );
104
105
 
105
- const hoverActive = $derived(tooltip != null || hasHoverMarker(markers));
106
- const hoverStrategy = $derived(resolveHoverStrategy(tooltip?.strategy, markers, 'x'));
107
- const hoverSync = $derived(resolveHoverSync(tooltip?.sync, markers));
106
+ const hover = $derived({
107
+ active: tooltip != null || hasHoverMarker(markers),
108
+ points: hoverPoints,
109
+ label: (r: TData) => String(yAcc(r)),
110
+ strategy: resolveHoverStrategy(tooltip?.strategy, markers, 'x'),
111
+ sync: resolveHoverSync(tooltip?.sync, markers),
112
+ tooltip,
113
+ });
114
+
115
+ const lineMarkers = $derived(
116
+ buildLineMarkers({
117
+ groupedSeries,
118
+ disabledStrokeFor: (name) => disabledStrokeOverride(effectiveDisabledFor(name)),
119
+ disabledDotsFor: (name) => disabledDotsOverride(effectiveDisabledFor(name)),
120
+ }),
121
+ );
122
+
123
+ const scopedGroups = $derived(
124
+ buildScopedMarkerGroups<TData, Series<TData>, LineSegmentStyle>({
125
+ groupedSeries,
126
+ segments,
127
+ colorFor: (_series, seriesIndex) => paletteColor(seriesIndex, cfg.palette),
128
+ segmentColorFor: (style, defaultColor) => style?.stroke?.stroke ?? defaultColor,
129
+ }),
130
+ );
131
+
132
+ // End-of-line labels can grow past the plot's own content box, into its
133
+ // margin — reserving real space there (rather than padding the domain,
134
+ // which would falsely imply data exists past the last real point) is
135
+ // what ValueLabels's own overflow measurement feeds into here, via a
136
+ // tracker local to this one `<Plot>` instance — see ValueLabels.svelte's
137
+ // own doc comment for why that locality (vs. svelteplot's shared margin
138
+ // context) matters.
139
+ const labelMargin = createLabelMarginTracker(() => margins);
108
140
  </script>
109
141
 
110
142
  <BasePlotLayout
111
143
  {width}
112
144
  {height}
145
+ {styles}
113
146
  data={flat}
114
147
  getX={xAcc}
115
148
  getY={(d) => Number(yAcc(d))}
116
- {hoverPoints}
117
- label={(r) => String(yAcc(r))}
118
- {tooltip}
119
- {hoverActive}
120
- {hoverStrategy}
121
- {hoverSync}
149
+ {hover}
122
150
  {scales}
123
- {margins}
151
+ margins={labelMargin.resolvedMargins}
124
152
  xTickRotate={scales?.x?.tickRotate}
153
+ markers={[...lineMarkers, ...markers]}
154
+ seriesData={resolvedSeries}
155
+ {scopedGroups}
125
156
  >
126
- {#snippet children({ matchedPoints, ruleX, ruleY, impliesRuleX, impliesRuleY, numericMargins })}
127
- <AxisLayout data={flat} getX={xAcc} getY={(d) => Number(yAcc(d))} {width} {height} {numericMargins} {scales} />
128
- <GridLayout {scales} />
129
- <RuleLayout {markers} seriesData={resolvedSeries} {matchedPoints} {ruleX} {ruleY} {impliesRuleX} {impliesRuleY}>
130
- {#each groupedSeries as group, seriesIndex (group.series.name)}
131
- {@const defaultColor = paletteColor(seriesIndex, cfg.palette)}
132
- {@const disabledOverride = legendInteraction?.disabledSeries.get(group.series.name)}
133
- {@const disabledStroke = disabledStrokeOverride(disabledOverride)}
134
- {@const disabledDots = disabledDotsOverride(disabledOverride)}
135
- {#each group.visualSegments as seg, i (`${group.series.name}-${i}`)}
136
- {@const strokeStyle = seg.style.stroke}
137
- {@const lineData = seg.data as unknown as Record<string | symbol, never>[]}
138
- <Line
139
- data={lineData}
140
- x={resolveAccessor(group.series.x)}
141
- y={resolveAccessor(group.series.y)}
142
- stroke={disabledStroke?.stroke ?? strokeStyle?.stroke ?? defaultColor}
143
- strokeWidth={disabledStroke?.strokeWidth ?? strokeStyle?.strokeWidth ?? cfg.line.strokeWidth}
144
- strokeOpacity={disabledStroke?.strokeOpacity ?? strokeStyle?.strokeOpacity ?? 1}
145
- strokeDasharray={disabledStroke?.strokeDasharray ?? strokeStyle?.strokeDasharray}
146
- strokeLinejoin={disabledStroke?.strokeLinejoin ?? strokeStyle?.strokeLinejoin}
147
- strokeLinecap={disabledStroke?.strokeLinecap ?? strokeStyle?.strokeLinecap}
148
- />
149
- {#if seg.style.dots}
150
- {@const dotStyle = seg.style.dots}
151
- <Dot
152
- data={seg.data}
153
- x={resolveAccessor(group.series.x)}
154
- y={resolveAccessor(group.series.y)}
155
- r={disabledDots?.dotRadius ?? dotStyle.dotRadius ?? cfg.line.dotRadius}
156
- fill={disabledDots?.dotFill ?? dotStyle.dotFill ?? strokeStyle?.stroke ?? defaultColor}
157
- symbol={disabledDots?.dotSymbol ?? dotStyle.dotSymbol ?? 'circle'}
158
- stroke={disabledDots?.dotStroke ?? dotStyle.dotStroke ?? strokeStyle?.stroke ?? defaultColor}
159
- strokeWidth={disabledDots?.dotStrokeWidth ?? dotStyle.dotStrokeWidth ?? 1}
160
- fillOpacity={disabledDots?.dotFillOpacity ?? dotStyle.dotFillOpacity ?? strokeStyle?.strokeOpacity ?? 1}
161
- strokeOpacity={disabledDots?.dotStrokeOpacity ?? dotStyle.dotStrokeOpacity ?? strokeStyle?.strokeOpacity ?? 1}
162
- strokeDasharray={disabledDots?.dotStrokeDasharray ?? dotStyle.dotStrokeDasharray}
163
- />
164
- {/if}
165
- {/each}
166
-
167
- {#each seriesMarkers.get(group.series.name) ?? [] as marker, i (`series-${group.series.name}-${marker.type}-${i}`)}
168
- {#if marker.type === 'component'}
169
- {#if marker.scope === 'segment'}
170
- {@const segs = resolveSegmentsForSeries(segments, group.series.name)}
171
- {@const xAccessor = resolveAccessor(group.series.x)}
172
- {@const segmentDataArrays = getDataForSegments(group.series.data, xAccessor, segs)}
173
- {#each segmentDataArrays as segmentData, segIdx (`${group.series.name}-marker-segment-${segIdx}`)}
174
- {#if segmentData.length > 0}
175
- {@const segmentColor = segs[segIdx]?.style?.stroke?.stroke ?? defaultColor}
176
- <marker.component
177
- {...marker.props}
178
- series={group.series.name}
179
- data={segmentData}
180
- seriesColor={segmentColor}
181
- />
182
- {/if}
183
- {/each}
184
- {:else}
185
- {@const segs = resolveSegmentsForSeries(segments, group.series.name)}
186
- {@const seriesColor = segs[0]?.style?.stroke?.stroke ?? defaultColor}
187
- <marker.component
188
- {...marker.props}
189
- series={group.series.name}
190
- data={group.series.data}
191
- seriesColor={seriesColor}
192
- />
193
- {/if}
194
- {:else if marker.type === 'hover'}
195
- {@const seriesPoints = matchedPoints.filter((p) => p.series === group.series.name)}
196
- {#if marker.scope === 'segment'}
197
- {@const segs = resolveSegmentsForSeries(segments, group.series.name)}
198
- {#each segs as seg, segIdx (`${group.series.name}-hover-segment-${segIdx}`)}
199
- {@const segPoints = seriesPoints.filter((p) => matchesSegmentX(p.x, seg))}
200
- {#if segPoints.length > 0}
201
- <HoverMarker
202
- data={segPoints}
203
- ruleX={marker.ruleX !== false && (impliesRuleX || marker.ruleX === true) ? ruleX : null}
204
- ruleY={marker.ruleY !== false && (impliesRuleY || marker.ruleY === true) ? ruleY : null}
205
- showLabels={marker.showLabels}
206
- ruleStyle={marker.ruleStyle}
207
- dotStyle={marker.dotStyle}
208
- fontStyle={marker.fontStyle}
209
- seriesColor={seg.style?.stroke?.stroke ?? defaultColor}
210
- />
211
- {/if}
212
- {/each}
213
- {:else}
214
- {@const segs = resolveSegmentsForSeries(segments, group.series.name)}
215
- <HoverMarker
216
- data={seriesPoints}
217
- ruleX={marker.ruleX !== false && (impliesRuleX || marker.ruleX === true) ? ruleX : null}
218
- ruleY={marker.ruleY !== false && (impliesRuleY || marker.ruleY === true) ? ruleY : null}
219
- showLabels={marker.showLabels}
220
- ruleStyle={marker.ruleStyle}
221
- dotStyle={marker.dotStyle}
222
- fontStyle={marker.fontStyle}
223
- seriesColor={segs[0]?.style?.stroke?.stroke ?? defaultColor}
224
- />
225
- {/if}
226
- {/if}
227
- {/each}
228
- {/each}
229
-
230
157
  {#if showVals}
231
- <ValueLabels {lastPoints} values={styles.values} />
158
+ <ValueLabels
159
+ {lastPoints}
160
+ values={styles.values}
161
+ onOverflow={labelMargin.report}
162
+ disabledFor={effectiveDisabledFor}
163
+ onHoverSeries={hoverIsolate ? (name) => { hoverIsolatedSeries = name; } : undefined}
164
+ onHoverEnd={hoverIsolate ? () => { hoverIsolatedSeries = null; } : undefined}
165
+ />
232
166
  {/if}
233
- </RuleLayout>
234
- {/snippet}
235
167
  </BasePlotLayout>
@@ -1,11 +1,11 @@
1
1
  import type { BasePlotProps } from '../../types/plots/props';
2
+ import type { AxisBasedScalesConfig } from '../../types/layout/scales';
2
3
  import type { SeriesProps, DataProps } from '../../types/plots/data/common';
3
- import type { AxisBasedScalesConfig } from '../../types/plots/props';
4
- import type { BasePlotStyles } from '../../types/plots/styles/common';
4
+ import type { BasePlotStylesConfig } from '../../types/layout/styles';
5
5
  import type { LineSegmentStyle } from '../../types/plots/segments/common';
6
- import type { Marker } from '../../types/markers/common';
6
+ import type { LineMarkersConfig } from '../../types/markers/line';
7
7
  declare function $$render<TData extends Record<string, unknown>>(): {
8
- props: BasePlotProps<SeriesProps<TData> | DataProps<TData>, TData, BasePlotStyles, LineSegmentStyle, Marker, AxisBasedScalesConfig<TData>>;
8
+ props: BasePlotProps<SeriesProps<TData> | DataProps<TData>, TData, BasePlotStylesConfig, LineSegmentStyle, LineMarkersConfig, AxisBasedScalesConfig<TData>>;
9
9
  exports: {};
10
10
  bindings: "";
11
11
  slots: {};