@fundar/data-chart-telling 0.0.24 → 0.0.26

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 (170) hide show
  1. package/dist/charts/bar/Chart.svelte +2 -0
  2. package/dist/charts/heatmap/Chart.svelte +3 -1
  3. package/dist/charts/heatmap/Chart.svelte.d.ts +1 -2
  4. package/dist/charts/line/Chart.svelte +2 -0
  5. package/dist/charts/pyramid/Chart.svelte +3 -1
  6. package/dist/charts/pyramid/Chart.svelte.d.ts +1 -2
  7. package/dist/charts/scatter/Chart.svelte +95 -0
  8. package/dist/charts/scatter/Chart.svelte.d.ts +28 -0
  9. package/dist/configuration/config.svelte.js +13 -1
  10. package/dist/configuration/themes/index.d.ts +76 -0
  11. package/dist/index.d.ts +20 -14
  12. package/dist/index.js +9 -3
  13. package/dist/layout/coordinates/CoordinatesLayout.svelte +95 -0
  14. package/dist/layout/{plot/RuleLayout.svelte.d.ts → coordinates/CoordinatesLayout.svelte.d.ts} +13 -16
  15. package/dist/layout/coordinates/margins.d.ts +16 -0
  16. package/dist/layout/coordinates/margins.js +11 -0
  17. package/dist/{plots/geo → layout/coordinates}/projections/argentina.d.ts +1 -1
  18. package/dist/layout/{geo → coordinates}/resolveProjection.d.ts +1 -1
  19. package/dist/layout/coordinates/resolveProjection.js +24 -0
  20. package/dist/{plots/geo → layout/coordinates}/zoom.svelte.d.ts +1 -1
  21. package/dist/layout/geometry/GeometryLayout.svelte +231 -0
  22. package/dist/layout/geometry/GeometryLayout.svelte.d.ts +62 -0
  23. package/dist/layout/plot/BasePlotLayout.svelte +142 -155
  24. package/dist/layout/plot/BasePlotLayout.svelte.d.ts +61 -51
  25. package/dist/layout/plot/margins.d.ts +0 -16
  26. package/dist/layout/plot/margins.js +0 -11
  27. package/dist/layout/{plot/AxisLayout.svelte → scales/ScalesLayout.svelte} +23 -16
  28. package/dist/layout/{plot/AxisLayout.svelte.d.ts → scales/ScalesLayout.svelte.d.ts} +4 -5
  29. package/dist/layout/scales/buildScale.d.ts +8 -0
  30. package/dist/layout/scales/buildScale.js +43 -0
  31. package/dist/layout/styles/StylesLayout.svelte +75 -0
  32. package/dist/layout/styles/StylesLayout.svelte.d.ts +15 -0
  33. package/dist/{plots/geo → layout/styles}/TileLayer.svelte +2 -2
  34. package/dist/{plots/geo → layout/styles}/TileLayer.svelte.d.ts +1 -1
  35. package/dist/layout/tooltip/Tooltip.svelte +16 -20
  36. package/dist/layout/tooltip/Tooltip.svelte.d.ts +5 -0
  37. package/dist/layout/tooltip/TooltipLayout.svelte +7 -1
  38. package/dist/layout/tooltip/TooltipLayout.svelte.d.ts +4 -0
  39. package/dist/layout/tooltip/controller.svelte.d.ts +4 -5
  40. package/dist/layout/tooltip/controller.svelte.js +28 -16
  41. package/dist/layout/tooltip/resolveTooltipPlacement.d.ts +24 -0
  42. package/dist/layout/tooltip/resolveTooltipPlacement.js +49 -0
  43. package/dist/layout/tooltip/utils.d.ts +27 -19
  44. package/dist/layout/tooltip/utils.js +16 -15
  45. package/dist/markers/BarMarker.svelte +92 -0
  46. package/dist/markers/BarMarker.svelte.d.ts +7 -0
  47. package/dist/markers/CellMarker.svelte +21 -0
  48. package/dist/markers/CellMarker.svelte.d.ts +7 -0
  49. package/dist/markers/ContourMarker.svelte +28 -0
  50. package/dist/markers/ContourMarker.svelte.d.ts +7 -0
  51. package/dist/markers/DeltaMarker.svelte +32 -39
  52. package/dist/markers/DeltaMarker.svelte.d.ts +4 -5
  53. package/dist/markers/DotMarker.svelte +35 -6
  54. package/dist/markers/DotMarker.svelte.d.ts +11 -2
  55. package/dist/markers/GeoFeaturesMarker.svelte +19 -0
  56. package/dist/markers/GeoFeaturesMarker.svelte.d.ts +7 -0
  57. package/dist/markers/GeoHoverMarker.svelte +25 -0
  58. package/dist/markers/GeoHoverMarker.svelte.d.ts +9 -0
  59. package/dist/markers/GraticuleMarker.svelte +18 -0
  60. package/dist/markers/GraticuleMarker.svelte.d.ts +7 -0
  61. package/dist/markers/HoverMarker.svelte +32 -33
  62. package/dist/markers/HoverMarker.svelte.d.ts +15 -9
  63. package/dist/markers/LineMarker.svelte +19 -0
  64. package/dist/markers/LineMarker.svelte.d.ts +7 -0
  65. package/dist/markers/LinkMarker.svelte +42 -0
  66. package/dist/markers/LinkMarker.svelte.d.ts +7 -0
  67. package/dist/markers/RuleMarker.svelte +24 -0
  68. package/dist/markers/RuleMarker.svelte.d.ts +10 -0
  69. package/dist/markers/SphereMarker.svelte +15 -0
  70. package/dist/markers/SphereMarker.svelte.d.ts +7 -0
  71. package/dist/markers/TextMarker.svelte +1 -1
  72. package/dist/markers/TextMarker.svelte.d.ts +1 -1
  73. package/dist/markers/VectorMarker.svelte +20 -0
  74. package/dist/markers/VectorMarker.svelte.d.ts +7 -0
  75. package/dist/markers/inset/InsetMarker.svelte +38 -45
  76. package/dist/markers/inset/InsetMarker.svelte.d.ts +6 -2
  77. package/dist/plots/bar/Plot.svelte +54 -84
  78. package/dist/plots/bar/Plot.svelte.d.ts +4 -5
  79. package/dist/plots/bar/ValueLabels.svelte +1 -1
  80. package/dist/plots/bar/ValueLabels.svelte.d.ts +1 -1
  81. package/dist/plots/bar/buildBarMarkers.d.ts +30 -0
  82. package/dist/plots/bar/buildBarMarkers.js +100 -0
  83. package/dist/plots/bar/layout.svelte.d.ts +1 -2
  84. package/dist/plots/bar/layout.svelte.js +1 -1
  85. package/dist/plots/geo/Plot.svelte +113 -387
  86. package/dist/plots/geo/Plot.svelte.d.ts +5 -4
  87. package/dist/plots/geo/buildGeoMarkers.d.ts +33 -0
  88. package/dist/plots/geo/buildGeoMarkers.js +47 -0
  89. package/dist/plots/heatmap/Plot.svelte +24 -49
  90. package/dist/plots/heatmap/Plot.svelte.d.ts +4 -5
  91. package/dist/plots/heatmap/ValueLabels.svelte +2 -2
  92. package/dist/plots/heatmap/ValueLabels.svelte.d.ts +2 -2
  93. package/dist/plots/heatmap/buildCellMarkers.d.ts +21 -0
  94. package/dist/plots/heatmap/buildCellMarkers.js +25 -0
  95. package/dist/plots/line/Plot.svelte +68 -141
  96. package/dist/plots/line/Plot.svelte.d.ts +4 -4
  97. package/dist/plots/line/ValueLabels.svelte +94 -49
  98. package/dist/plots/line/ValueLabels.svelte.d.ts +6 -1
  99. package/dist/plots/line/buildLineMarkers.d.ts +15 -0
  100. package/dist/plots/line/buildLineMarkers.js +57 -0
  101. package/dist/plots/pyramid/Plot.svelte +41 -117
  102. package/dist/plots/pyramid/Plot.svelte.d.ts +4 -5
  103. package/dist/plots/pyramid/ValueLabels.svelte +1 -1
  104. package/dist/plots/pyramid/ValueLabels.svelte.d.ts +1 -1
  105. package/dist/plots/pyramid/buildPyramidBarMarkers.d.ts +19 -0
  106. package/dist/plots/pyramid/buildPyramidBarMarkers.js +32 -0
  107. package/dist/plots/scatter/Plot.svelte +206 -0
  108. package/dist/plots/{bar/BarSegments.svelte.d.ts → scatter/Plot.svelte.d.ts} +9 -20
  109. package/dist/plots/scatter/ValueLabels.svelte +106 -0
  110. package/dist/{layout/plot/GridLayout.svelte.d.ts → plots/scatter/ValueLabels.svelte.d.ts} +11 -5
  111. package/dist/plots/scatter/buildScatterMarkers.d.ts +11 -0
  112. package/dist/plots/scatter/buildScatterMarkers.js +37 -0
  113. package/dist/plots/scatter/resolveRadiusPaddedDomain.d.ts +10 -0
  114. package/dist/plots/scatter/resolveRadiusPaddedDomain.js +23 -0
  115. package/dist/plots/utils/declutter.d.ts +82 -33
  116. package/dist/plots/utils/declutter.js +182 -40
  117. package/dist/plots/utils/geoAccessors.d.ts +1 -1
  118. package/dist/plots/utils/geoAccessors.js +1 -1
  119. package/dist/plots/utils/segments.d.ts +17 -10
  120. package/dist/plots/utils/segments.js +26 -16
  121. package/dist/types/charts/common.d.ts +1 -1
  122. package/dist/types/charts/props.d.ts +5 -6
  123. package/dist/types/configuration/styling.d.ts +24 -2
  124. package/dist/types/{plots/scales/geo.d.ts → layout/coordinates.d.ts} +33 -1
  125. package/dist/types/layout/geometry.d.ts +17 -0
  126. package/dist/types/{plots/axis.d.ts → layout/scales.d.ts} +11 -0
  127. package/dist/types/layout/styles.d.ts +126 -0
  128. package/dist/types/layout/tooltip.d.ts +20 -1
  129. package/dist/types/markers/all.d.ts +11 -0
  130. package/dist/types/markers/base.d.ts +45 -0
  131. package/dist/types/markers/base.js +1 -0
  132. package/dist/types/markers/common.d.ts +64 -16
  133. package/dist/types/markers/geo.d.ts +75 -100
  134. package/dist/types/markers/line.d.ts +34 -0
  135. package/dist/types/markers/line.js +1 -0
  136. package/dist/types/markers/props.d.ts +1 -1
  137. package/dist/types/plots/data/common.d.ts +1 -1
  138. package/dist/types/plots/data/scatter.d.ts +13 -0
  139. package/dist/types/plots/data/scatter.js +1 -0
  140. package/dist/types/plots/props.d.ts +46 -74
  141. package/dist/types/plots/segments/common.d.ts +6 -12
  142. package/dist/types/plots/segments/config.d.ts +1 -1
  143. package/dist/types/plots/styling.d.ts +10 -0
  144. package/dist/{plots/utils → utils}/delta.d.ts +2 -2
  145. package/dist/{plots/utils → utils}/delta.js +1 -1
  146. package/dist/utils/edgeAwareAnchor.d.ts +37 -0
  147. package/dist/utils/edgeAwareAnchor.js +65 -0
  148. package/dist/utils/grouping.d.ts +1 -1
  149. package/dist/utils/interpolate.d.ts +1 -1
  150. package/package.json +3 -1
  151. package/dist/layout/geo/GeoLayout.svelte +0 -51
  152. package/dist/layout/geo/GeoLayout.svelte.d.ts +0 -24
  153. package/dist/layout/geo/resolveProjection.js +0 -48
  154. package/dist/layout/plot/GridLayout.svelte +0 -30
  155. package/dist/layout/plot/RuleLayout.svelte +0 -111
  156. package/dist/plots/bar/BarSegments.svelte +0 -69
  157. package/dist/types/plots/delta.d.ts +0 -19
  158. package/dist/types/plots/styles/common.d.ts +0 -52
  159. package/dist/types/plots/styles/geo.d.ts +0 -42
  160. /package/dist/{plots/geo → layout/coordinates}/projections/argentina.js +0 -0
  161. /package/dist/{plots/geo → layout/coordinates}/projections/fit.d.ts +0 -0
  162. /package/dist/{plots/geo → layout/coordinates}/projections/fit.js +0 -0
  163. /package/dist/{plots/geo → layout/coordinates}/rotate.svelte.d.ts +0 -0
  164. /package/dist/{plots/geo → layout/coordinates}/rotate.svelte.js +0 -0
  165. /package/dist/{plots/geo → layout/coordinates}/zoom.svelte.js +0 -0
  166. /package/dist/types/{plots/axis.js → layout/coordinates.js} +0 -0
  167. /package/dist/types/{plots/delta.js → layout/geometry.js} +0 -0
  168. /package/dist/types/{plots/scales/geo.js → layout/scales.js} +0 -0
  169. /package/dist/types/{plots/styles/common.js → layout/styles.js} +0 -0
  170. /package/dist/types/{plots/styles/geo.js → markers/all.js} +0 -0
@@ -1,6 +1,5 @@
1
1
  import type { MarkerProps } from '../types/markers/props';
2
- import type { AxisValue } from '../types/plots/axis';
3
- import type { DotStyle, FontStyle, StrokeStyle } from '../types/plots/styling';
2
+ import type { HoverMarkerConfig } from '../types/markers/common';
4
3
  import type { HoverDisplayPoint } from '../types/layout/tooltip';
5
4
  /**
6
5
  * In-SVG hover highlight, rendered *inside* a `<Plot>`. Because it positions
@@ -8,15 +7,22 @@ import type { HoverDisplayPoint } from '../types/layout/tooltip';
8
7
  * correctly in every facet — that is what makes cross-facet sync work without
9
8
  * any pixel math. Draws optional x/y crosshair rules plus a dot + value label
10
9
  * for each matched point.
10
+ *
11
+ * Takes the raw `marker` config plus the couple of things it can't know on
12
+ * its own — `activePoint` (the specific matched point actually under the
13
+ * cursor, for the crosshair position; see `controller.svelte.ts`'s own doc
14
+ * comment) and whether the active hover strategy implies a crosshair on
15
+ * each axis by default — the same "component owns its own rendering"
16
+ * pattern `BarMarker`/`RuleMarker` follow. `activePoint`/
17
+ * `impliesCrosshairX`/`impliesCrosshairY` are all optional and default to
18
+ * "no crosshair".
11
19
  */
12
20
  interface Props extends MarkerProps<HoverDisplayPoint> {
13
- ruleX?: AxisValue | null;
14
- ruleY?: AxisValue | null;
15
- showLabels?: boolean;
16
- format?: (point: HoverDisplayPoint) => string;
17
- ruleStyle?: StrokeStyle;
18
- dotStyle?: DotStyle;
19
- fontStyle?: FontStyle;
21
+ /** Narrowed to just the fields this component reads — `HoverMarkerConfig` also carries routing/scoping fields (`series`/`scope`/`strategy`/`sync`) it has no use for. */
22
+ marker: Pick<HoverMarkerConfig, 'ruleX' | 'ruleY' | 'showLabels' | 'format' | 'ruleStyle' | 'dotStyle' | 'fontStyle'>;
23
+ activePoint?: HoverDisplayPoint | null;
24
+ impliesCrosshairX?: boolean;
25
+ impliesCrosshairY?: boolean;
20
26
  }
21
27
  declare const HoverMarker: import("svelte").Component<Props, {}, "">;
22
28
  type HoverMarker = ReturnType<typeof HoverMarker>;
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import { Line } from 'svelteplot';
3
+ import type { LineMarkerConfig } from '../types/markers/common';
4
+
5
+ /** Renders one `'line'` marker as a `<Line>` call — just the stroke. Endpoint dots are a separate `'dot'` marker. */
6
+ let { marker }: { marker: LineMarkerConfig } = $props();
7
+ </script>
8
+
9
+ <Line
10
+ data={marker.data}
11
+ x={marker.x}
12
+ y={marker.y}
13
+ stroke={marker.style?.stroke}
14
+ strokeWidth={marker.style?.strokeWidth}
15
+ strokeOpacity={marker.style?.strokeOpacity}
16
+ strokeDasharray={marker.style?.strokeDasharray}
17
+ strokeLinejoin={marker.style?.strokeLinejoin}
18
+ strokeLinecap={marker.style?.strokeLinecap}
19
+ />
@@ -0,0 +1,7 @@
1
+ import type { LineMarkerConfig } from '../types/markers/common';
2
+ type $$ComponentProps = {
3
+ marker: LineMarkerConfig;
4
+ };
5
+ declare const LineMarker: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type LineMarker = ReturnType<typeof LineMarker>;
7
+ export default LineMarker;
@@ -0,0 +1,42 @@
1
+ <script lang="ts">
2
+ import { Geo } from 'svelteplot';
3
+ import { resolveAccessor } from '../plots/utils/accessors';
4
+ import { greatCircleInterpolate } from '../plots/utils/geoAccessors';
5
+ import type { GeoLinkMarkerConfig } from '../types/markers/geo';
6
+
7
+ /** Renders one `'link'` marker — geodesic-aware lines between point pairs, as a synthetic `LineString` fed through `<Geo>`. */
8
+ let { marker }: { marker: GeoLinkMarkerConfig } = $props();
9
+
10
+ const getX1 = $derived(resolveAccessor(marker.x1));
11
+ const getY1 = $derived(resolveAccessor(marker.y1));
12
+ const getX2 = $derived(resolveAccessor(marker.x2));
13
+ const getY2 = $derived(resolveAccessor(marker.y2));
14
+
15
+ const linkFeatures = $derived(
16
+ marker.data.map((d) => ({
17
+ type: 'Feature' as const,
18
+ properties: {},
19
+ geometry: {
20
+ type: 'LineString' as const,
21
+ coordinates:
22
+ marker.curve === 'linear'
23
+ ? [
24
+ [Number(getX1(d)), Number(getY1(d))],
25
+ [Number(getX2(d)), Number(getY2(d))],
26
+ ]
27
+ : greatCircleInterpolate(Number(getX1(d)), Number(getY1(d)), Number(getX2(d)), Number(getY2(d))),
28
+ },
29
+ })),
30
+ );
31
+ </script>
32
+
33
+ <!-- No `fill` prop: svelteplot treats any string as scale-worthy unless it's a real CSS color, so a literal `'none'` would run through the color scale instead of staying unfilled. Omitting it leaves svelteplot's own unfilled default in place. -->
34
+ <Geo
35
+ class="link"
36
+ data={linkFeatures}
37
+ stroke={marker.style?.stroke ?? 'currentColor'}
38
+ strokeWidth={marker.style?.strokeWidth ?? 1}
39
+ strokeOpacity={marker.style?.strokeOpacity}
40
+ strokeDasharray={marker.style?.strokeDasharray}
41
+ style="vector-effect: non-scaling-stroke"
42
+ />
@@ -0,0 +1,7 @@
1
+ import type { GeoLinkMarkerConfig } from '../types/markers/geo';
2
+ type $$ComponentProps = {
3
+ marker: GeoLinkMarkerConfig;
4
+ };
5
+ declare const LinkMarker: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type LinkMarker = ReturnType<typeof LinkMarker>;
7
+ export default LinkMarker;
@@ -0,0 +1,24 @@
1
+ <script lang="ts">
2
+ import { RuleX, RuleY } from 'svelteplot';
3
+ import type { AxisValue } from '../types/layout/scales';
4
+ import type { StrokeStyle } from '../types/plots/styling';
5
+
6
+ /**
7
+ * A single vertical (`axis: 'x'`) or horizontal (`axis: 'y'`) reference
8
+ * line at `value` — shared by author-configured `'rule'` markers and
9
+ * `HoverMarker`'s crosshair. Falls back to `'currentColor'`/`1` when the
10
+ * caller supplies no style.
11
+ */
12
+ let { axis, value, style }: { axis: 'x' | 'y'; value: AxisValue; style?: StrokeStyle } = $props();
13
+
14
+ const RuleMark = $derived(axis === 'x' ? RuleX : RuleY);
15
+ </script>
16
+
17
+ <RuleMark
18
+ data={[{ v: value }]}
19
+ {...axis === 'x' ? { x: (d: { v: AxisValue }) => d.v } : { y: (d: { v: AxisValue }) => d.v }}
20
+ stroke={style?.stroke ?? 'currentColor'}
21
+ strokeWidth={style?.strokeWidth ?? 1}
22
+ strokeOpacity={style?.strokeOpacity ?? 1}
23
+ strokeDasharray={style?.strokeDasharray}
24
+ />
@@ -0,0 +1,10 @@
1
+ import type { AxisValue } from '../types/layout/scales';
2
+ import type { StrokeStyle } from '../types/plots/styling';
3
+ type $$ComponentProps = {
4
+ axis: 'x' | 'y';
5
+ value: AxisValue;
6
+ style?: StrokeStyle;
7
+ };
8
+ declare const RuleMarker: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type RuleMarker = ReturnType<typeof RuleMarker>;
10
+ export default RuleMarker;
@@ -0,0 +1,15 @@
1
+ <script lang="ts">
2
+ import { Sphere } from 'svelteplot';
3
+ import type { GeoSphereMarkerConfig } from '../types/markers/geo';
4
+
5
+ /** Renders one `'sphere'` marker — the outer globe outline. */
6
+ let { marker }: { marker: GeoSphereMarkerConfig } = $props();
7
+ </script>
8
+
9
+ <Sphere
10
+ fill={marker.style?.fill}
11
+ fillOpacity={marker.style?.fillOpacity}
12
+ stroke={marker.style?.stroke ?? 'currentColor'}
13
+ strokeWidth={marker.style?.strokeWidth ?? 1}
14
+ style="vector-effect: non-scaling-stroke"
15
+ />
@@ -0,0 +1,7 @@
1
+ import type { GeoSphereMarkerConfig } from '../types/markers/geo';
2
+ type $$ComponentProps = {
3
+ marker: GeoSphereMarkerConfig;
4
+ };
5
+ declare const SphereMarker: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type SphereMarker = ReturnType<typeof SphereMarker>;
7
+ export default SphereMarker;
@@ -1,7 +1,7 @@
1
1
  <script lang="ts" generics="TData = Record<string, unknown>">
2
2
  import { Text } from 'svelteplot';
3
3
  import type { MarkerProps } from '../types/markers/props';
4
- import type { AxisValue } from '../types/plots/axis';
4
+ import type { AxisValue } from '../types/layout/scales';
5
5
  import type { FontStyle } from '../types/plots/styling';
6
6
 
7
7
  interface Props extends MarkerProps<TData> {
@@ -1,5 +1,5 @@
1
1
  import type { MarkerProps } from '../types/markers/props';
2
- import type { AxisValue } from '../types/plots/axis';
2
+ import type { AxisValue } from '../types/layout/scales';
3
3
  import type { FontStyle } from '../types/plots/styling';
4
4
  declare function $$render<TData = Record<string, unknown>>(): {
5
5
  props: MarkerProps<TData> & {
@@ -0,0 +1,20 @@
1
+ <script lang="ts">
2
+ import { Vector } from 'svelteplot';
3
+ import type { GeoVectorMarkerConfig } from '../types/markers/geo';
4
+
5
+ /** Renders one `'vector'` marker — arrows/spikes at points. */
6
+ let { marker }: { marker: GeoVectorMarkerConfig } = $props();
7
+ </script>
8
+
9
+ <Vector
10
+ data={marker.data}
11
+ x={marker.x}
12
+ y={marker.y}
13
+ length={marker.length}
14
+ rotate={marker.rotate}
15
+ shape={marker.shape ?? 'arrow'}
16
+ anchor={marker.anchor}
17
+ fill={marker.style?.fill ?? 'currentColor'}
18
+ stroke={marker.style?.stroke}
19
+ strokeWidth={marker.style?.strokeWidth}
20
+ />
@@ -0,0 +1,7 @@
1
+ import type { GeoVectorMarkerConfig } from '../types/markers/geo';
2
+ type $$ComponentProps = {
3
+ marker: GeoVectorMarkerConfig;
4
+ };
5
+ declare const VectorMarker: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type VectorMarker = ReturnType<typeof VectorMarker>;
7
+ export default VectorMarker;
@@ -4,12 +4,13 @@
4
4
  import { insetBox } from './insetLayout';
5
5
  import { filterGeometryParts } from '../../plots/utils/geoAccessors';
6
6
  import TooltipLayout from '../../layout/tooltip/TooltipLayout.svelte';
7
- import type { GeoInsetMarker } from '../../types/markers/geo';
7
+ import type { GeoInsetMarkerConfig } from '../../types/markers/geo';
8
8
  import type { GeoFeature } from '../../types/plots/data/geo';
9
+ import type { TooltipAnchorX, TooltipAnchorY } from '../../types/layout/tooltip';
9
10
 
10
11
  /**
11
12
  * A small, independently-projected map panel anchored to a corner/edge of
12
- * the plot — see `GeoInsetMarker`'s doc comment. Renders raw `<path>`s via
13
+ * the plot — see `GeoInsetMarkerConfig`'s doc comment. Renders raw `<path>`s via
13
14
  * its own `d3-geo` `geoPath`, deliberately not svelteplot's `<Geo>` mark:
14
15
  * that mark is bound to the plot's *shared* projection scale, but an inset
15
16
  * needs its own, independently fit to just what it's showing.
@@ -22,8 +23,10 @@
22
23
  left = 0,
23
24
  top = 0,
24
25
  defaultFill,
26
+ tooltipAnchorX,
27
+ tooltipAnchorY,
25
28
  }: {
26
- marker: GeoInsetMarker<TProps>;
29
+ marker: GeoInsetMarkerConfig<TProps>;
27
30
  /**
28
31
  * The plot's own already-tagged features (`__id` set), with full,
29
32
  * unreduced geometry regardless of the plot's own `excludeParts` — used
@@ -45,6 +48,9 @@
45
48
  left?: number;
46
49
  top?: number;
47
50
  defaultFill: string;
51
+ /** The plot's own top-level `tooltip.anchorX`/`anchorY` — the fallback this inset's own tooltip inherits when its own is unset, before the system default. */
52
+ tooltipAnchorX?: TooltipAnchorX;
53
+ tooltipAnchorY?: TooltipAnchorY;
48
54
  } = $props();
49
55
 
50
56
  const insetFeatures = $derived.by((): GeoFeature<Record<string, unknown>>[] => {
@@ -84,44 +90,33 @@
84
90
  return geoPath(projection);
85
91
  });
86
92
 
87
- /**
88
- * Nearest-feature hover, local to this inset only — mirrors the main
89
- * map's own "always nearest feature centroid" behaviour (see `Plot.svelte`),
90
- * but computed against this inset's own fitted `path`/projection instead
91
- * of the plot's shared one, since svelteplot's `<Pointer>` (which drives
92
- * the main map's hover) only ever matches the main projection's own
93
- * feature positions — it has no way to reach content an inset draws at a
94
- * wholly different scale/location.
95
- */
96
- const centroids = $derived.by(() => {
97
- if (!path) return [];
98
- return insetFeatures.map((f) => ({ feature: f, centroid: path.centroid(f as GeoJSON.Feature) }));
99
- });
100
-
101
93
  let hovered = $state<{ feature: GeoFeature<Record<string, unknown>>; x: number; y: number } | null>(null);
102
94
 
103
- // Any point within the inset box always matches its nearest feature —
104
- // there's no reason for a hover inside a small, self-contained panel to
105
- // ever come up empty, so the threshold is just the box's own diagonal.
106
- const maxDistance = $derived(Math.hypot(box.width, box.height));
95
+ /**
96
+ * Purely a coordinate reference (`pointer-events: none` never itself a
97
+ * hover target) sized exactly to the inset's own box, so `onInsetPointer*`
98
+ * below can convert a screen-space pointer event into the same box-local
99
+ * `x`/`y` space `TooltipLayout`'s `bounds` expects, the same way
100
+ * `BasePlotLayout`'s own cursor tracking does for the main map.
101
+ */
102
+ let boxRectEl = $state<SVGRectElement>();
107
103
 
108
- function onInsetPointerMove(e: PointerEvent) {
109
- const rect = (e.currentTarget as SVGRectElement).getBoundingClientRect();
110
- const x = e.clientX - rect.left;
111
- const y = e.clientY - rect.top;
112
- let nearest: (typeof centroids)[number] | null = null;
113
- let nearestDist = Infinity;
114
- for (const c of centroids) {
115
- const d = Math.hypot(c.centroid[0] - x, c.centroid[1] - y);
116
- if (d < nearestDist) {
117
- nearestDist = d;
118
- nearest = c;
119
- }
120
- }
121
- hovered = nearest && nearestDist <= maxDistance ? { feature: nearest.feature, x, y } : null;
104
+ /**
105
+ * Which exact feature `<path>` the browser's own SVG hit-testing resolved
106
+ * the event to mirrors the main map's own `<Geo onpointerenter>` (see
107
+ * `Plot.svelte`) rather than approximating it by nearest centroid
108
+ * distance, which used to make hover pick the wrong (if geometrically
109
+ * nearer-by-centroid) neighboring feature, exactly the bug that fix
110
+ * addressed for the main map.
111
+ */
112
+ function onInsetFeaturePointer(e: PointerEvent, f: GeoFeature<Record<string, unknown>>) {
113
+ const rect = boxRectEl?.getBoundingClientRect();
114
+ const x = e.clientX - (rect?.left ?? 0);
115
+ const y = e.clientY - (rect?.top ?? 0);
116
+ hovered = { feature: f, x, y };
122
117
  }
123
118
 
124
- function onInsetPointerLeave() {
119
+ function onInsetFeatureLeave() {
125
120
  hovered = null;
126
121
  }
127
122
  </script>
@@ -129,6 +124,7 @@
129
124
  {#if path}
130
125
  <g class="dct-geo-inset" transform="translate({box.left + left}, {box.top + top})">
131
126
  {#each insetFeatures as f, i (i)}
127
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
132
128
  <path
133
129
  d={path(f as GeoJSON.Feature) ?? undefined}
134
130
  fill={marker.style?.fill ?? defaultFill}
@@ -136,19 +132,14 @@
136
132
  stroke={marker.style?.stroke ?? 'currentColor'}
137
133
  stroke-width={marker.style?.strokeWidth ?? 0.5}
138
134
  style="vector-effect: non-scaling-stroke"
135
+ onpointerenter={marker.tooltip ? (e: PointerEvent) => onInsetFeaturePointer(e, f) : undefined}
136
+ onpointermove={marker.tooltip ? (e: PointerEvent) => onInsetFeaturePointer(e, f) : undefined}
137
+ onpointerleave={marker.tooltip ? onInsetFeatureLeave : undefined}
139
138
  />
140
139
  {/each}
141
140
  {#if marker.tooltip}
142
141
  <!-- svelte-ignore a11y_no_static_element_interactions -->
143
- <rect
144
- x={0}
145
- y={0}
146
- width={box.width}
147
- height={box.height}
148
- fill="transparent"
149
- onpointermove={onInsetPointerMove}
150
- onpointerleave={onInsetPointerLeave}
151
- />
142
+ <rect x={0} y={0} width={box.width} height={box.height} fill="transparent" pointer-events="none" bind:this={boxRectEl} />
152
143
  {/if}
153
144
  {#if marker.tooltip}
154
145
  {@const tooltip = marker.tooltip}
@@ -162,6 +153,8 @@
162
153
  rows={hovered ? [hovered.feature] : []}
163
154
  format={tooltip.format}
164
155
  content={tooltip.content}
156
+ anchorX={tooltip.anchorX ?? tooltipAnchorX}
157
+ anchorY={tooltip.anchorY ?? tooltipAnchorY}
165
158
  />
166
159
  </div>
167
160
  </foreignObject>
@@ -1,8 +1,9 @@
1
- import type { GeoInsetMarker } from '../../types/markers/geo';
1
+ import type { GeoInsetMarkerConfig } from '../../types/markers/geo';
2
2
  import type { GeoFeature } from '../../types/plots/data/geo';
3
+ import type { TooltipAnchorX, TooltipAnchorY } from '../../types/layout/tooltip';
3
4
  declare function $$render<TProps extends Record<string, unknown>>(): {
4
5
  props: {
5
- marker: GeoInsetMarker<TProps>;
6
+ marker: GeoInsetMarkerConfig<TProps>;
6
7
  /**
7
8
  * The plot's own already-tagged features (`__id` set), with full,
8
9
  * unreduced geometry regardless of the plot's own `excludeParts` — used
@@ -26,6 +27,9 @@ declare function $$render<TProps extends Record<string, unknown>>(): {
26
27
  left?: number;
27
28
  top?: number;
28
29
  defaultFill: string;
30
+ /** The plot's own top-level `tooltip.anchorX`/`anchorY` — the fallback this inset's own tooltip inherits when its own is unset, before the system default. */
31
+ tooltipAnchorX?: TooltipAnchorX;
32
+ tooltipAnchorY?: TooltipAnchorY;
29
33
  };
30
34
  exports: {};
31
35
  bindings: "";
@@ -2,31 +2,24 @@
2
2
  lang="ts"
3
3
  generics="TData extends Record<string, unknown>"
4
4
  >
5
- import { SvelteMap } from 'svelte/reactivity';
6
5
  import { paletteColor } from '../../utils/color';
7
6
  import { buildSeries } from '../../utils/grouping';
8
7
  import { getLegendInteraction } from '../../layout/legend/interaction.svelte';
9
8
  import { disabledFillOverride } from '../utils/legendDisabled';
10
- import { BarX, BarY } from 'svelteplot';
11
9
  import { resolveAccessor } from '../utils/accessors';
12
10
  import { getConfiguration } from '../../configuration/config.svelte';
13
- import { buildGroupedSeries, validateSegments, resolveSegmentsForSeries, matchesSegmentX } from '../utils/segments';
11
+ import { buildGroupedSeries, validateSegments, buildScopedMarkerGroups } from '../utils/segments';
14
12
  import { createBarLayout } from './layout.svelte';
15
13
  import { buildHoverPoints } from './hoverPoints';
16
- import BarSegments from './BarSegments.svelte';
14
+ import { buildBarMarkers } from './buildBarMarkers';
17
15
  import ValueLabels from './ValueLabels.svelte';
18
16
  import BasePlotLayout from '../../layout/plot/BasePlotLayout.svelte';
19
- import AxisLayout from '../../layout/plot/AxisLayout.svelte';
20
- import GridLayout from '../../layout/plot/GridLayout.svelte';
21
- import RuleLayout from '../../layout/plot/RuleLayout.svelte';
22
- import HoverMarker from '../../markers/HoverMarker.svelte';
23
17
  import { hasHoverMarker, resolveHoverStrategy, resolveHoverSync } from '../../layout/tooltip/utils';
24
- import type { Marker } from '../../types/markers/common';
18
+ import type { AxisBasedMarkersConfig } from '../../types/markers/common';
25
19
  import type { BasePlotProps } from '../../types/plots/props';
26
- import type { AxisScale } from '../../types/plots/axis';
20
+ import type { AxisScale, AxisBasedScalesConfig } from '../../types/layout/scales';
27
21
  import type { Series, SeriesProps, DataProps } from '../../types/plots/data/common';
28
- import type { AxisBasedScalesConfig } from '../../types/plots/props';
29
- import type { BasePlotStyles } from '../../types/plots/styles/common';
22
+ import type { BasePlotStylesConfig } from '../../types/layout/styles';
30
23
  import type { ValueAnchor } from '../../types/plots/constants';
31
24
  import type { BarSegmentStyle } from '../../types/plots/segments/common';
32
25
 
@@ -38,9 +31,9 @@
38
31
  type Props = BasePlotProps<
39
32
  SeriesProps<TData> | DataProps<TData>,
40
33
  TData,
41
- BasePlotStyles,
34
+ BasePlotStylesConfig,
42
35
  BarSegmentStyle,
43
- Marker,
36
+ AxisBasedMarkersConfig,
44
37
  AxisBasedScalesConfig<TData>
45
38
  >;
46
39
 
@@ -73,23 +66,21 @@
73
66
  const fmtVal = $derived(styles.values?.format ?? ((v: number) => `${v}`));
74
67
  const valAnchor = $derived<ValueAnchor>(styles.values?.anchor ?? 'outside');
75
68
 
76
- const seriesMarkers = $derived.by(() => {
77
- const map = new SvelteMap<string, Marker[]>();
78
- for (const marker of markers) {
79
- if (marker.type === 'hover' && marker.series) {
80
- if (!map.has(marker.series)) map.set(marker.series, []);
81
- map.get(marker.series)!.push(marker);
82
- }
83
- }
84
- return map;
85
- });
86
-
87
69
  // `xAcc`/`yAcc` are always data-semantic (category/value) — independent of
88
70
  // which *visual* axis ends up drawing them, decided by `categoricalAxis`.
89
71
  const xAcc = $derived(resolveAccessor(resolvedSeries[0]?.x ?? ((d: TData) => d)));
90
72
  const yAcc = $derived(resolveAccessor(resolvedSeries[0]?.y ?? ((d: TData) => d)));
91
73
  const flat = $derived(resolvedSeries.flatMap((s) => s.data));
92
74
 
75
+ const scopedGroups = $derived(
76
+ buildScopedMarkerGroups<TData, Series<TData>, BarSegmentStyle>({
77
+ groupedSeries,
78
+ segments,
79
+ colorFor: (_series, seriesIndex) => paletteColor(seriesIndex, cfg.palette),
80
+ segmentColorFor: (style, defaultColor) => style?.fill ?? defaultColor,
81
+ }),
82
+ );
83
+
93
84
  /**
94
85
  * Which visual axis draws the category, and therefore whether this renders
95
86
  * as vertical bars (`BarY`, category on X) or horizontal bars (`BarX`,
@@ -112,7 +103,6 @@
112
103
  return yCategorical ? 'y' : 'x';
113
104
  });
114
105
  const isHorizontal = $derived(categoricalAxis === 'y');
115
- const BarMark = $derived(isHorizontal ? BarX : BarY);
116
106
 
117
107
  /**
118
108
  * `'categorical'` is our own sentinel (BarPlot-only), not a real svelteplot
@@ -171,76 +161,56 @@
171
161
  ),
172
162
  );
173
163
 
174
- const hoverActive = $derived(tooltip != null || hasHoverMarker(markers));
175
- const hoverStrategy = $derived(resolveHoverStrategy(tooltip?.strategy, markers, categoricalAxis));
176
- const hoverSync = $derived(resolveHoverSync(tooltip?.sync, markers));
164
+ const hover = $derived({
165
+ active: tooltip != null || hasHoverMarker(markers),
166
+ points: hoverPoints,
167
+ label: (r: TData) => String(yAcc(r)),
168
+ strategy: resolveHoverStrategy(tooltip?.strategy, markers, categoricalAxis),
169
+ sync: resolveHoverSync(tooltip?.sync, markers),
170
+ tooltip,
171
+ // The category axis is a band scale — `<Pointer>`'s nearest-point-by-
172
+ // distance match (svelteplot's own projection of each candidate's band
173
+ // centre) can disagree by a couple of pixels with where `<BarX>`/`<BarY>`
174
+ // (via svelteplot's `RectPath`) actually render that band, which is
175
+ // enough to mismatch the neighbouring category once bands get thin
176
+ // (many categories). The `role: 'hitArea'` bar markers `buildBarMarkers`
177
+ // emits hit-test the category bands directly instead — see its own doc
178
+ // comment.
179
+ pointMatching: 'contains' as const,
180
+ });
177
181
  </script>
178
182
 
179
183
  <BasePlotLayout
180
184
  {width}
181
185
  {height}
186
+ {styles}
182
187
  data={flat}
183
188
  getX={plotGetX}
184
189
  getY={plotGetY}
185
- {hoverPoints}
186
- label={(r) => String(yAcc(r))}
187
- {tooltip}
188
- {hoverActive}
189
- {hoverStrategy}
190
- {hoverSync}
190
+ {hover}
191
191
  scales={resolvedScales}
192
192
  {margins}
193
193
  xTickRotate={resolvedScales?.x?.tickRotate}
194
+ markers={({ setHoverMatch, clearHoverMatch }) => [
195
+ ...buildBarMarkers({
196
+ groupedSeries,
197
+ barLayout,
198
+ isHorizontal,
199
+ hoverPoints,
200
+ palette: cfg.palette,
201
+ disabledFillFor: (name) => disabledFillOverride(legendInteraction?.disabledSeries.get(name)),
202
+ active: hover.active,
203
+ setHoverMatch,
204
+ clearHoverMatch,
205
+ }),
206
+ ...markers,
207
+ ]}
208
+ seriesData={resolvedSeries}
209
+ {scopedGroups}
194
210
  >
195
- {#snippet children({ matchedPoints, ruleX, ruleY, impliesRuleX, impliesRuleY, numericMargins })}
196
- <AxisLayout data={flat} getX={plotGetX} getY={plotGetY} {width} {height} {numericMargins} scales={resolvedScales} />
197
- <GridLayout scales={resolvedScales} />
198
- <RuleLayout {markers} seriesData={resolvedSeries} {matchedPoints} {ruleX} {ruleY} {impliesRuleX} {impliesRuleY}>
199
- {#each groupedSeries as group, seriesIndex (group.series.name)}
200
- {@const defaultColor = paletteColor(seriesIndex, cfg.palette)}
201
- {@const disabledFill = disabledFillOverride(legendInteraction?.disabledSeries.get(group.series.name))}
202
- <BarSegments {group} {seriesIndex} {barLayout} {isHorizontal} {BarMark} {defaultColor} {disabledFill} />
203
-
204
- {#if showVals}
211
+ {#if showVals}
212
+ {#each groupedSeries as group, seriesIndex (group.series.name)}
205
213
  <ValueLabels {group} {seriesIndex} {barLayout} {isHorizontal} {valAnchor} {fmtVal} values={styles.values} axisColor={cfg.axis.color} />
206
- {/if}
207
-
208
- {#each seriesMarkers.get(group.series.name) ?? [] as marker, i (`series-${group.series.name}-hover-${i}`)}
209
- {#if marker.type === 'hover'}
210
- {@const seriesPoints = matchedPoints.filter((p) => p.series === group.series.name)}
211
- {#if marker.scope === 'segment'}
212
- {@const segs = resolveSegmentsForSeries(segments, group.series.name)}
213
- {#each segs as seg, segIdx (`${group.series.name}-hover-segment-${segIdx}`)}
214
- {@const segPoints = seriesPoints.filter((p) => matchesSegmentX(xAcc(p.row), seg))}
215
- {#if segPoints.length > 0}
216
- <HoverMarker
217
- data={segPoints}
218
- ruleX={marker.ruleX !== false && (impliesRuleX || marker.ruleX === true) ? ruleX : null}
219
- ruleY={marker.ruleY !== false && (impliesRuleY || marker.ruleY === true) ? ruleY : null}
220
- showLabels={marker.showLabels}
221
- ruleStyle={marker.ruleStyle}
222
- dotStyle={marker.dotStyle}
223
- fontStyle={marker.fontStyle}
224
- seriesColor={seg.style?.fill ?? defaultColor}
225
- />
226
- {/if}
227
- {/each}
228
- {:else}
229
- {@const segs = resolveSegmentsForSeries(segments, group.series.name)}
230
- <HoverMarker
231
- data={seriesPoints}
232
- ruleX={marker.ruleX !== false && (impliesRuleX || marker.ruleX === true) ? ruleX : null}
233
- ruleY={marker.ruleY !== false && (impliesRuleY || marker.ruleY === true) ? ruleY : null}
234
- showLabels={marker.showLabels}
235
- ruleStyle={marker.ruleStyle}
236
- dotStyle={marker.dotStyle}
237
- fontStyle={marker.fontStyle}
238
- seriesColor={segs[0]?.style?.fill ?? defaultColor}
239
- />
240
- {/if}
241
- {/if}
242
214
  {/each}
243
- {/each}
244
- </RuleLayout>
245
- {/snippet}
215
+ {/if}
246
216
  </BasePlotLayout>
@@ -1,11 +1,10 @@
1
- import type { Marker } from '../../types/markers/common';
1
+ import type { AxisBasedMarkersConfig } from '../../types/markers/common';
2
2
  import type { BasePlotProps } from '../../types/plots/props';
3
+ import type { AxisBasedScalesConfig } from '../../types/layout/scales';
3
4
  import type { SeriesProps, DataProps } from '../../types/plots/data/common';
4
- import type { AxisBasedScalesConfig } from '../../types/plots/props';
5
- import type { BasePlotStyles } from '../../types/plots/styles/common';
6
- import type { BarSegmentStyle } from '../../types/plots/segments/common';
5
+ import type { BasePlotStylesConfig } from '../../types/layout/styles';
7
6
  declare function $$render<TData extends Record<string, unknown>>(): {
8
- props: BasePlotProps<SeriesProps<TData> | DataProps<TData>, TData, BasePlotStyles, BarSegmentStyle, 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: {};
@@ -2,7 +2,7 @@
2
2
  import { Text } from 'svelteplot';
3
3
  import { resolveAccessor } from '../utils/accessors';
4
4
  import type { Series } from '../../types/plots/data/common';
5
- import type { ValuesStyle } from '../../types/plots/styles/common';
5
+ import type { ValuesStyle } from '../../types/layout/styles';
6
6
  import type { ValueAnchor } from '../../types/plots/constants';
7
7
  import type { BarSegmentStyle } from '../../types/plots/segments/common';
8
8
  import type { VisualGroup } from '../../types/plots/segments/config';
@@ -1,5 +1,5 @@
1
1
  import type { Series } from '../../types/plots/data/common';
2
- import type { ValuesStyle } from '../../types/plots/styles/common';
2
+ import type { ValuesStyle } from '../../types/layout/styles';
3
3
  import type { ValueAnchor } from '../../types/plots/constants';
4
4
  import type { BarSegmentStyle } from '../../types/plots/segments/common';
5
5
  import type { VisualGroup } from '../../types/plots/segments/config';