@fundar/data-chart-telling 0.0.14 → 0.0.16

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 (148) hide show
  1. package/README.md +121 -17
  2. package/dist/charts/{Chart.svelte → BaseChart.svelte} +2 -0
  3. package/dist/charts/{Chart.svelte.d.ts → BaseChart.svelte.d.ts} +3 -3
  4. package/dist/charts/bar/Chart.svelte +5 -5
  5. package/dist/charts/bar/Chart.svelte.d.ts +1 -2
  6. package/dist/charts/heatmap/Chart.svelte +8 -7
  7. package/dist/charts/heatmap/Chart.svelte.d.ts +2 -3
  8. package/dist/charts/line/Chart.svelte +5 -5
  9. package/dist/charts/line/Chart.svelte.d.ts +1 -2
  10. package/dist/charts/pyramid/Chart.svelte +7 -6
  11. package/dist/charts/pyramid/Chart.svelte.d.ts +2 -3
  12. package/dist/configuration/config.svelte.js +2 -1
  13. package/dist/configuration/themes/index.d.ts +60 -40
  14. package/dist/index.d.ts +27 -13
  15. package/dist/index.js +12 -7
  16. package/dist/layout/facet/FacetLayout.svelte +1 -1
  17. package/dist/layout/facet/FacetLayout.svelte.d.ts +1 -1
  18. package/dist/layout/geo/GeoLayout.svelte +47 -0
  19. package/dist/layout/geo/GeoLayout.svelte.d.ts +22 -0
  20. package/dist/layout/geo/resolveProjection.d.ts +10 -0
  21. package/dist/layout/geo/resolveProjection.js +48 -0
  22. package/dist/layout/legend/ContinuousSection.svelte +158 -17
  23. package/dist/layout/legend/DiscreteSection.svelte +56 -4
  24. package/dist/layout/legend/interaction.svelte.d.ts +7 -0
  25. package/dist/layout/legend/interaction.svelte.js +24 -0
  26. package/dist/layout/plot/AxisLayout.svelte +95 -0
  27. package/dist/layout/plot/AxisLayout.svelte.d.ts +39 -0
  28. package/dist/layout/plot/BasePlotLayout.svelte +302 -0
  29. package/dist/layout/plot/BasePlotLayout.svelte.d.ts +95 -0
  30. package/dist/layout/plot/GridLayout.svelte +30 -0
  31. package/dist/layout/plot/GridLayout.svelte.d.ts +27 -0
  32. package/dist/layout/plot/RuleLayout.svelte +111 -0
  33. package/dist/layout/plot/RuleLayout.svelte.d.ts +40 -0
  34. package/dist/layout/plot/margins.d.ts +34 -0
  35. package/dist/layout/plot/margins.js +26 -0
  36. package/dist/layout/timeline/TimelineLayout.svelte +1 -1
  37. package/dist/layout/timeline/TimelineLayout.svelte.d.ts +1 -1
  38. package/dist/layout/tooltip/Tooltip.svelte +15 -0
  39. package/dist/layout/tooltip/Tooltip.svelte.d.ts +7 -0
  40. package/dist/layout/tooltip/TooltipLayout.svelte +45 -0
  41. package/dist/layout/tooltip/TooltipLayout.svelte.d.ts +41 -0
  42. package/dist/layout/tooltip/controller.svelte.d.ts +4 -3
  43. package/dist/layout/tooltip/controller.svelte.js +5 -6
  44. package/dist/layout/tooltip/hover.svelte.d.ts +1 -1
  45. package/dist/layout/tooltip/hover.svelte.js +1 -1
  46. package/dist/layout/tooltip/utils.d.ts +2 -2
  47. package/dist/markers/ContourClipMarker.svelte +77 -0
  48. package/dist/markers/ContourClipMarker.svelte.d.ts +10 -0
  49. package/dist/{plots/markers → markers}/DeltaMarker.svelte +4 -4
  50. package/dist/{plots/markers → markers}/DeltaMarker.svelte.d.ts +3 -3
  51. package/dist/{plots/markers → markers}/DotMarker.svelte +3 -3
  52. package/dist/{plots/markers → markers}/DotMarker.svelte.d.ts +3 -3
  53. package/dist/markers/HoverMarker.svelte +136 -0
  54. package/dist/{plots/markers → markers}/HoverMarker.svelte.d.ts +4 -4
  55. package/dist/{plots/markers → markers}/PeakMarker.svelte +2 -2
  56. package/dist/{plots/markers → markers}/PeakMarker.svelte.d.ts +2 -2
  57. package/dist/{plots/markers → markers}/TextMarker.svelte +11 -4
  58. package/dist/{plots/markers → markers}/TextMarker.svelte.d.ts +3 -3
  59. package/dist/markers/inset/InsetMarker.svelte +170 -0
  60. package/dist/markers/inset/InsetMarker.svelte.d.ts +51 -0
  61. package/dist/markers/inset/insetLayout.d.ts +25 -0
  62. package/dist/markers/inset/insetLayout.js +39 -0
  63. package/dist/markers/inset/insetProjection.d.ts +8 -0
  64. package/dist/markers/inset/insetProjection.js +39 -0
  65. package/dist/plots/bar/BarSegments.svelte +69 -0
  66. package/dist/plots/bar/BarSegments.svelte.d.ts +40 -0
  67. package/dist/plots/bar/Plot.svelte +140 -74
  68. package/dist/plots/bar/Plot.svelte.d.ts +7 -3
  69. package/dist/plots/bar/ValueLabels.svelte +89 -0
  70. package/dist/plots/bar/ValueLabels.svelte.d.ts +39 -0
  71. package/dist/plots/bar/hoverPoints.d.ts +15 -0
  72. package/dist/plots/bar/hoverPoints.js +35 -0
  73. package/dist/plots/bar/layout.svelte.d.ts +37 -0
  74. package/dist/plots/bar/layout.svelte.js +132 -0
  75. package/dist/plots/geo/Plot.svelte +577 -0
  76. package/dist/plots/geo/Plot.svelte.d.ts +29 -0
  77. package/dist/plots/geo/TileLayer.svelte +77 -0
  78. package/dist/plots/geo/TileLayer.svelte.d.ts +11 -0
  79. package/dist/plots/geo/projections/argentina.d.ts +8 -0
  80. package/dist/plots/geo/projections/argentina.js +35 -0
  81. package/dist/plots/geo/projections/fit.d.ts +8 -0
  82. package/dist/plots/geo/projections/fit.js +14 -0
  83. package/dist/plots/geo/rotate.svelte.d.ts +26 -0
  84. package/dist/plots/geo/rotate.svelte.js +79 -0
  85. package/dist/plots/geo/zoom.svelte.d.ts +32 -0
  86. package/dist/plots/geo/zoom.svelte.js +50 -0
  87. package/dist/plots/heatmap/Plot.svelte +171 -58
  88. package/dist/plots/heatmap/Plot.svelte.d.ts +7 -3
  89. package/dist/plots/line/Plot.svelte +67 -30
  90. package/dist/plots/line/Plot.svelte.d.ts +7 -3
  91. package/dist/plots/pyramid/Plot.svelte +88 -35
  92. package/dist/plots/pyramid/Plot.svelte.d.ts +7 -3
  93. package/dist/plots/utils/delta.d.ts +1 -1
  94. package/dist/plots/utils/geoAccessors.d.ts +64 -0
  95. package/dist/plots/utils/geoAccessors.js +188 -0
  96. package/dist/plots/utils/geoSegments.d.ts +14 -0
  97. package/dist/plots/utils/geoSegments.js +17 -0
  98. package/dist/plots/utils/legendDisabled.d.ts +15 -0
  99. package/dist/plots/utils/legendDisabled.js +41 -0
  100. package/dist/plots/utils/segments.d.ts +14 -5
  101. package/dist/plots/utils/segments.js +25 -13
  102. package/dist/plots/utils/tiles.d.ts +89 -0
  103. package/dist/plots/utils/tiles.js +159 -0
  104. package/dist/plots/utils/topojson.d.ts +9 -0
  105. package/dist/plots/utils/topojson.js +30 -0
  106. package/dist/types/charts/common.d.ts +2 -1
  107. package/dist/types/charts/legend.d.ts +32 -1
  108. package/dist/types/charts/props.d.ts +9 -6
  109. package/dist/types/configuration/styling.d.ts +13 -5
  110. package/dist/types/layout/legend.d.ts +26 -0
  111. package/dist/types/layout/tooltip.d.ts +14 -21
  112. package/dist/types/{plots/markers.d.ts → markers/common.d.ts} +2 -18
  113. package/dist/types/markers/geo.d.ts +237 -0
  114. package/dist/types/markers/props.d.ts +11 -0
  115. package/dist/types/markers/props.js +1 -0
  116. package/dist/types/plots/axis.d.ts +55 -0
  117. package/dist/types/plots/axis.js +1 -0
  118. package/dist/types/plots/constants.d.ts +30 -0
  119. package/dist/types/plots/constants.js +1 -0
  120. package/dist/types/plots/data/common.d.ts +40 -0
  121. package/dist/types/plots/data/common.js +1 -0
  122. package/dist/types/plots/data/geo.d.ts +74 -0
  123. package/dist/types/plots/data/geo.js +1 -0
  124. package/dist/types/plots/delta.d.ts +2 -2
  125. package/dist/types/plots/props.d.ts +105 -16
  126. package/dist/types/plots/scales/geo.d.ts +44 -0
  127. package/dist/types/plots/scales/geo.js +1 -0
  128. package/dist/types/plots/segments/common.d.ts +25 -0
  129. package/dist/types/plots/segments/common.js +1 -0
  130. package/dist/types/plots/segments/config.d.ts +51 -0
  131. package/dist/types/plots/segments/config.js +1 -0
  132. package/dist/types/plots/styles/common.d.ts +37 -0
  133. package/dist/types/plots/styles/common.js +1 -0
  134. package/dist/types/plots/styles/geo.d.ts +42 -0
  135. package/dist/types/plots/styles/geo.js +1 -0
  136. package/dist/types/plots/styling.d.ts +45 -0
  137. package/dist/types/plots/styling.js +1 -0
  138. package/dist/utils/grouping.d.ts +2 -1
  139. package/dist/utils/interpolate.d.ts +2 -1
  140. package/package.json +27 -13
  141. package/dist/layout/plot/PlotLayout.svelte +0 -294
  142. package/dist/layout/plot/PlotLayout.svelte.d.ts +0 -70
  143. package/dist/plots/markers/HoverMarker.svelte +0 -98
  144. package/dist/types/plots/common.d.ts +0 -97
  145. package/dist/types/plots/styles.d.ts +0 -187
  146. /package/dist/types/{plots/common.js → layout/legend.js} +0 -0
  147. /package/dist/types/{plots/markers.js → markers/common.js} +0 -0
  148. /package/dist/types/{plots/styles.js → markers/geo.js} +0 -0
@@ -8,7 +8,7 @@
8
8
  TimelineChildSnippet,
9
9
  TimelineInterpolation,
10
10
  } from '../../types/charts/common';
11
- import type { Accessor } from '../../types/plots/common';
11
+ import type { Accessor } from '../../types/plots/data/common';
12
12
 
13
13
  /**
14
14
  * Couples a scrubber `progress` to a discrete sequence of time steps. It picks
@@ -1,5 +1,5 @@
1
1
  import type { Orientation, TimeValue, TimelineChildSnippet, TimelineInterpolation } from '../../types/charts/common';
2
- import type { Accessor } from '../../types/plots/common';
2
+ import type { Accessor } from '../../types/plots/data/common';
3
3
  declare function $$render<TRow extends Record<string, unknown>>(): {
4
4
  props: {
5
5
  data: TRow[];
@@ -19,6 +19,7 @@
19
19
  background,
20
20
  color,
21
21
  bounds,
22
+ plain = false,
22
23
  children,
23
24
  }: {
24
25
  x: number;
@@ -28,6 +29,13 @@
28
29
  color?: string;
29
30
  /** Box the tooltip must stay within — usually the hosting plot's width/height. */
30
31
  bounds?: { width: number; height: number };
32
+ /**
33
+ * Drops the default dark box (background/padding/border) so a custom
34
+ * `content` snippet fully owns its own look. Set whenever the caller
35
+ * supplies `content` — the drop shadow and border radius still apply,
36
+ * so a custom card can match its own corners via `--dct-tooltip-radius`.
37
+ */
38
+ plain?: boolean;
31
39
  children: Snippet;
32
40
  } = $props();
33
41
 
@@ -55,6 +63,7 @@
55
63
  {#if visible}
56
64
  <div
57
65
  class="dct-tooltip"
66
+ class:dct-tooltip--plain={plain}
58
67
  bind:clientWidth={boxWidth}
59
68
  bind:clientHeight={boxHeight}
60
69
  style:left="{placement.left}px"
@@ -81,4 +90,10 @@
81
90
  white-space: pre-line;
82
91
  line-height: 1.35;
83
92
  }
93
+
94
+ .dct-tooltip--plain {
95
+ background: transparent;
96
+ padding: 0;
97
+ border: none;
98
+ }
84
99
  </style>
@@ -10,6 +10,13 @@ type $$ComponentProps = {
10
10
  width: number;
11
11
  height: number;
12
12
  };
13
+ /**
14
+ * Drops the default dark box (background/padding/border) so a custom
15
+ * `content` snippet fully owns its own look. Set whenever the caller
16
+ * supplies `content` — the drop shadow and border radius still apply,
17
+ * so a custom card can match its own corners via `--dct-tooltip-radius`.
18
+ */
19
+ plain?: boolean;
13
20
  children: Snippet;
14
21
  };
15
22
  declare const Tooltip: import("svelte").Component<$$ComponentProps, {}, "">;
@@ -0,0 +1,45 @@
1
+ <script lang="ts" generics="TRow extends Record<string, unknown>">
2
+ import type { Snippet } from 'svelte';
3
+ import Tooltip from './Tooltip.svelte';
4
+
5
+ /**
6
+ * The "resolve + render" step every hoverable plot repeats verbatim:
7
+ * given the matched row(s) for the current hover and the author's
8
+ * `format`/`content` config, decide what goes inside the floating
9
+ * `Tooltip` — its own custom snippet (`content`, given every matched row)
10
+ * or the newline-joined `format` text (the default). Shared by
11
+ * `BasePlotLayout` (fed by the shared hover controller, used by every plot
12
+ * kind including `GeoPlot`) and `InsetMarker` (which matches hover locally,
13
+ * independent of that controller) so neither re-implements this branch.
14
+ */
15
+ let {
16
+ visible,
17
+ x,
18
+ y,
19
+ bounds,
20
+ rows,
21
+ format,
22
+ content,
23
+ }: {
24
+ visible: boolean;
25
+ x: number;
26
+ y: number;
27
+ /** Box the tooltip must stay within — see `Tooltip`'s own `bounds` doc. */
28
+ bounds: { width: number; height: number };
29
+ /** Every row the current hover matched — passed to `content` as-is; joined via `format` otherwise. */
30
+ rows: TRow[];
31
+ format: (row: TRow) => string;
32
+ /** Custom tooltip body; replaces the joined `format` text when given. */
33
+ content?: Snippet<[{ rows: TRow[] }]>;
34
+ } = $props();
35
+ </script>
36
+
37
+ {#if visible}
38
+ <Tooltip {x} {y} {bounds} plain={!!content}>
39
+ {#if content}
40
+ {@render content({ rows })}
41
+ {:else}
42
+ {rows.map(format).join('\n')}
43
+ {/if}
44
+ </Tooltip>
45
+ {/if}
@@ -0,0 +1,41 @@
1
+ import type { Snippet } from 'svelte';
2
+ declare function $$render<TRow extends Record<string, unknown>>(): {
3
+ props: {
4
+ visible: boolean;
5
+ x: number;
6
+ y: number;
7
+ /** Box the tooltip must stay within — see `Tooltip`'s own `bounds` doc. */
8
+ bounds: {
9
+ width: number;
10
+ height: number;
11
+ };
12
+ /** Every row the current hover matched — passed to `content` as-is; joined via `format` otherwise. */
13
+ rows: TRow[];
14
+ format: (row: TRow) => string;
15
+ /** Custom tooltip body; replaces the joined `format` text when given. */
16
+ content?: Snippet<[{
17
+ rows: TRow[];
18
+ }]>;
19
+ };
20
+ exports: {};
21
+ bindings: "";
22
+ slots: {};
23
+ events: {};
24
+ };
25
+ declare class __sveltets_Render<TRow extends Record<string, unknown>> {
26
+ props(): ReturnType<typeof $$render<TRow>>['props'];
27
+ events(): ReturnType<typeof $$render<TRow>>['events'];
28
+ slots(): ReturnType<typeof $$render<TRow>>['slots'];
29
+ bindings(): "";
30
+ exports(): {};
31
+ }
32
+ interface $$IsomorphicComponent {
33
+ new <TRow extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TRow>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TRow>['props']>, ReturnType<__sveltets_Render<TRow>['events']>, ReturnType<__sveltets_Render<TRow>['slots']>> & {
34
+ $$bindings?: ReturnType<__sveltets_Render<TRow>['bindings']>;
35
+ } & ReturnType<__sveltets_Render<TRow>['exports']>;
36
+ <TRow extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<TRow>['props']> & {}): ReturnType<__sveltets_Render<TRow>['exports']>;
37
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
38
+ }
39
+ declare const TooltipLayout: $$IsomorphicComponent;
40
+ type TooltipLayout<TRow extends Record<string, unknown>> = InstanceType<typeof TooltipLayout<TRow>>;
41
+ export default TooltipLayout;
@@ -1,5 +1,5 @@
1
1
  import type { HoverStore, HoverPoint } from '../../types/layout/tooltip';
2
- import type { AxisValue } from '../../types/plots/common';
2
+ import type { AxisValue } from '../../types/plots/axis';
3
3
  /**
4
4
  * The plot-agnostic hover state machine shared by every plot. Given reactive
5
5
  * accessors for the current hover store, this facet's id, and the local points,
@@ -18,7 +18,6 @@ export declare function createHoverController<T extends Record<string, unknown>>
18
18
  getY: () => (d: T) => AxisValue;
19
19
  /** Short label drawn next to the highlight dot (e.g. the value). */
20
20
  label: () => (row: T) => string;
21
- format: () => (row: T) => string;
22
21
  /** Named subset of series the tooltip text/content should be limited to. */
23
22
  seriesFilter?: () => string[] | undefined;
24
23
  /** This facet's own box, used to convert the cursor to/from a sync-broadcast fraction. */
@@ -34,6 +33,9 @@ export declare function createHoverController<T extends Record<string, unknown>>
34
33
  label: string;
35
34
  series: string | undefined;
36
35
  color: string | undefined;
36
+ dx: number | undefined;
37
+ dy: number | undefined;
38
+ row: T;
37
39
  }[];
38
40
  readonly matchedRows: HoverPoint<T>[];
39
41
  readonly ruleX: AxisValue | null;
@@ -41,7 +43,6 @@ export declare function createHoverController<T extends Record<string, unknown>>
41
43
  readonly impliesRuleX: boolean;
42
44
  readonly impliesRuleY: boolean;
43
45
  readonly showTooltip: boolean;
44
- readonly tooltipText: string;
45
46
  readonly tooltipPosition: {
46
47
  x: number;
47
48
  y: number;
@@ -31,9 +31,12 @@ export function createHoverController(args) {
31
31
  const matchedRows = $derived(filterBySeries(matched, args.seriesFilter?.()));
32
32
  // Points highlighted in THIS facet for the in-SVG marker — no series
33
33
  // filtering, since that's the tooltip text's concern, not the marker's.
34
- const highlight = $derived(matched.map(({ x, y, label, series, color }) => ({ x, y, label, series, color })));
34
+ // Keeps `row` (not just the display fields) so plot-kind templates can
35
+ // resolve a semantic value off the source row directly — e.g. a bar
36
+ // plot's category, regardless of which visual axis it's drawn on.
37
+ const highlight = $derived(matched.map(({ x, y, label, series, color, dx, dy, row }) => ({ x, y, label, series, color, dx, dy, row })));
35
38
  // Raw hover position — available whenever there are matched points.
36
- // PlotLayout gates actual display with impliesRuleX/Y + the marker's ruleX/Y props.
39
+ // BasePlotLayout gates actual display with impliesRuleX/Y + the marker's ruleX/Y props.
37
40
  const ruleX = $derived(matched.length > 0 ? args.hover().activeX : null);
38
41
  const ruleY = $derived(matched.length > 0 ? args.hover().activeY : null);
39
42
  // Whether the current strategy implies a crosshair rule on each axis by default.
@@ -47,7 +50,6 @@ export function createHoverController(args) {
47
50
  return cursor != null;
48
51
  return h.sync;
49
52
  });
50
- const tooltipText = $derived(matchedRows.length ? matchedRows.map((p) => args.format()(p.row)).join('\n') : '');
51
53
  function onPointer(data) {
52
54
  const h = args.hover();
53
55
  if (data.length) {
@@ -115,9 +117,6 @@ export function createHoverController(args) {
115
117
  get showTooltip() {
116
118
  return showTooltip;
117
119
  },
118
- get tooltipText() {
119
- return tooltipText;
120
- },
121
120
  get tooltipPosition() {
122
121
  return tooltipPosition;
123
122
  },
@@ -3,5 +3,5 @@ import type { HoverStore } from '../../types/layout/tooltip';
3
3
  export declare function provideHover(): HoverStore;
4
4
  /** Read the shared hover store, or `null` when there is no provider. */
5
5
  export declare function getHover(): HoverStore | null;
6
- /** A private hover store for a plot used outside of a {@link Chart}. */
6
+ /** A private hover store for a plot used outside of a {@link BaseChart}. */
7
7
  export declare function createHover(): HoverStore;
@@ -21,7 +21,7 @@ export function provideHover() {
21
21
  export function getHover() {
22
22
  return getContext(HOVER_KEY) ?? null;
23
23
  }
24
- /** A private hover store for a plot used outside of a {@link Chart}. */
24
+ /** A private hover store for a plot used outside of a {@link BaseChart}. */
25
25
  export function createHover() {
26
26
  const store = $state(blank());
27
27
  return store;
@@ -1,5 +1,5 @@
1
- import type { AxisValue } from '../../types/plots/common';
2
- import type { Marker, HoverMarkerConfig } from '../../types/plots/markers';
1
+ import type { AxisValue } from '../../types/plots/axis';
2
+ import type { Marker, HoverMarkerConfig } from '../../types/markers/common';
3
3
  import type { HoverStore, HoverStrategy, HoverPoint, TooltipProp, TooltipOptions } from '../../types/layout/tooltip';
4
4
  /** Value equality that also handles `Date` (compared by timestamp). */
5
5
  export declare function eq(a: AxisValue, b: AxisValue): boolean;
@@ -0,0 +1,77 @@
1
+ <script lang="ts">
2
+ import { geoPath, type GeoPath } from 'd3-geo';
3
+ import { usePlot } from 'svelteplot';
4
+ import type { Snippet } from 'svelte';
5
+ import type { GeoFeature } from '../types/plots/data/geo';
6
+
7
+ /**
8
+ * Masks a `contour` marker to just the landmass (`'land'`) or just the
9
+ * water (`'ocean'`) of the plot's own base map, via an SVG `clipPath` —
10
+ * lets a filled contour read as "temperature over land" without also
11
+ * painting straight across the ocean, or vice versa. A no-op passthrough
12
+ * when `mode` is unset, so this can unconditionally wrap every `contour`
13
+ * marker without an extra branch at each call site.
14
+ *
15
+ * Builds the clip path from the plot's own shared, already-fitted
16
+ * projection (`usePlot()` — the same hook `TileLayer.svelte` uses) rather
17
+ * than an independent one, so it lines up with the rest of the map
18
+ * regardless of projection/zoom. `'ocean'` unions the sphere and land
19
+ * paths into one `clip-rule: evenodd` shape — the sphere covers
20
+ * everything, then every land ring toggles back out, leaving only water.
21
+ */
22
+ let {
23
+ mode,
24
+ features,
25
+ children,
26
+ }: {
27
+ mode: 'land' | 'ocean' | undefined;
28
+ features: GeoFeature<Record<string, unknown>>[];
29
+ children: Snippet;
30
+ } = $props();
31
+
32
+ const plot = usePlot();
33
+ const clipId = `geo-contour-clip-${Math.random().toString(36).slice(2, 9)}`;
34
+
35
+ // Same runtime shape TileLayer.svelte relies on — svelteplot's own
36
+ // `PlotScale` type doesn't reflect it, but `plot.scales.projection` is a
37
+ // real `d3-geo`-compatible stream source at runtime.
38
+ const path: GeoPath = $derived(geoPath(plot.scales.projection as unknown as Parameters<typeof geoPath>[0]));
39
+ // Some real-world datasets carry exact-duplicate features for the same
40
+ // territory (seen in this repo's own `WORLD_COUNTRIES` fixture, for every
41
+ // South American country) — harmless for an ordinary filled `<Geo>` (the
42
+ // duplicate just redraws the same shape), but fatal here: two identical,
43
+ // oppositely-would-be-redundant copies of the same ring toggle each other
44
+ // back out under `clip-rule: evenodd`, silently punching a hole exactly
45
+ // where the duplicated territory is. Deduping by geometry keeps this
46
+ // correct regardless of whether the caller's data happens to be clean.
47
+ const uniqueGeometries = $derived(
48
+ Array.from(new Map(features.map((f) => [JSON.stringify(f.geometry), f as unknown as GeoJSON.Feature])).values()),
49
+ );
50
+ const landPath = $derived(
51
+ uniqueGeometries
52
+ .map((f) => path(f))
53
+ .filter((d): d is string => !!d)
54
+ .join(' '),
55
+ );
56
+ // `{type: 'Sphere'}` is d3-geo's own pseudo-geometry for "the whole
57
+ // globe" — not part of the GeoJSON spec, so `@types/geojson` has no type
58
+ // for it either.
59
+ const spherePath = $derived(path({ type: 'Sphere' } as unknown as GeoJSON.Feature) ?? '');
60
+ </script>
61
+
62
+ {#if mode}
63
+ <defs>
64
+ <clipPath id={clipId}>
65
+ {#if mode === 'land'}
66
+ <path d={landPath} clip-rule="evenodd" />
67
+ {:else}
68
+ <path d="{spherePath} {landPath}" clip-rule="evenodd" />
69
+ {/if}
70
+ </clipPath>
71
+ </defs>
72
+ <g clip-path="url(#{clipId})">
73
+ {@render children()}
74
+ </g>
75
+ {:else}
76
+ {@render children()}
77
+ {/if}
@@ -0,0 +1,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { GeoFeature } from '../types/plots/data/geo';
3
+ type $$ComponentProps = {
4
+ mode: 'land' | 'ocean' | undefined;
5
+ features: GeoFeature<Record<string, unknown>>[];
6
+ children: Snippet;
7
+ };
8
+ declare const ContourClipMarker: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type ContourClipMarker = ReturnType<typeof ContourClipMarker>;
10
+ export default ContourClipMarker;
@@ -5,10 +5,10 @@
5
5
  import { Line } from 'svelteplot';
6
6
  import DotMarker from './DotMarker.svelte';
7
7
  import TextMarker from './TextMarker.svelte';
8
- import { resolveXDeltas, resolveYDeltas } from '../utils/delta';
9
- import type { MarkerProps } from '../../types/plots/markers';
10
- import type { Series } from '../../types/plots/common';
11
- import type { DeltaConfig } from '../../types/plots/delta';
8
+ import { resolveXDeltas, resolveYDeltas } from '../plots/utils/delta';
9
+ import type { MarkerProps } from '../types/markers/props';
10
+ import type { Series } from '../types/plots/data/common';
11
+ import type { DeltaConfig } from '../types/plots/delta';
12
12
 
13
13
  interface Props extends MarkerProps<Series<TData>>, DeltaConfig {
14
14
  xDomain?: (number | string | Date)[];
@@ -1,6 +1,6 @@
1
- import type { MarkerProps } from '../../types/plots/markers';
2
- import type { Series } from '../../types/plots/common';
3
- import type { DeltaConfig } from '../../types/plots/delta';
1
+ import type { MarkerProps } from '../types/markers/props';
2
+ import type { Series } from '../types/plots/data/common';
3
+ import type { DeltaConfig } from '../types/plots/delta';
4
4
  declare function $$render<TData extends Record<string, unknown>>(): {
5
5
  props: MarkerProps<Series<TData>> & DeltaConfig & {
6
6
  xDomain?: (number | string | Date)[];
@@ -1,8 +1,8 @@
1
1
  <script lang="ts" generics="TData = Record<string, unknown>">
2
2
  import { Dot } from 'svelteplot';
3
- import type { MarkerProps } from '../../types/plots/markers';
4
- import type { AxisValue } from '../../types/plots/common';
5
- import type { DotStyle } from '../../types/plots/styles';
3
+ import type { MarkerProps } from '../types/markers/props';
4
+ import type { AxisValue } from '../types/plots/axis';
5
+ import type { DotStyle } from '../types/plots/styling';
6
6
 
7
7
  interface Props extends MarkerProps<TData> {
8
8
  x?: AxisValue | ((d: TData) => AxisValue);
@@ -1,6 +1,6 @@
1
- import type { MarkerProps } from '../../types/plots/markers';
2
- import type { AxisValue } from '../../types/plots/common';
3
- import type { DotStyle } from '../../types/plots/styles';
1
+ import type { MarkerProps } from '../types/markers/props';
2
+ import type { AxisValue } from '../types/plots/axis';
3
+ import type { DotStyle } from '../types/plots/styling';
4
4
  declare function $$render<TData = Record<string, unknown>>(): {
5
5
  props: MarkerProps<TData> & {
6
6
  x?: AxisValue | ((d: TData) => AxisValue);
@@ -0,0 +1,136 @@
1
+ <script lang="ts">
2
+ import { RuleX, RuleY } from 'svelteplot';
3
+ import DotMarker from './DotMarker.svelte';
4
+ import TextMarker from './TextMarker.svelte';
5
+ import { getConfiguration } from '../configuration/config.svelte';
6
+ import type { MarkerProps } from '../types/markers/props';
7
+ import type { AxisValue } from '../types/plots/axis';
8
+ import type { DotStyle, FontStyle, StrokeStyle } from '../types/plots/styling';
9
+ import type { HoverDisplayPoint } from '../types/layout/tooltip';
10
+
11
+ /**
12
+ * In-SVG hover highlight, rendered *inside* a `<Plot>`. Because it positions
13
+ * itself through the host plot's scales, it places the same coordinate
14
+ * correctly in every facet — that is what makes cross-facet sync work without
15
+ * any pixel math. Draws optional x/y crosshair rules plus a dot + value label
16
+ * for each matched point.
17
+ */
18
+ interface Props extends MarkerProps<HoverDisplayPoint> {
19
+ ruleX?: AxisValue | null;
20
+ ruleY?: AxisValue | null;
21
+ showLabels?: boolean;
22
+ format?: (point: HoverDisplayPoint) => string;
23
+ ruleStyle?: StrokeStyle;
24
+ dotStyle?: DotStyle;
25
+ fontStyle?: FontStyle;
26
+ }
27
+
28
+ let {
29
+ data = [],
30
+ ruleX = null,
31
+ ruleY = null,
32
+ showLabels = true,
33
+ format,
34
+ ruleStyle,
35
+ dotStyle,
36
+ fontStyle,
37
+ seriesColor,
38
+ }: Props = $props();
39
+
40
+ const cfg = $derived(getConfiguration());
41
+
42
+ /** Explicit style wins; otherwise fall back to the series color, then the point's own resolved color. */
43
+ const resolveColor = (explicit: string | undefined, point: HoverDisplayPoint) =>
44
+ explicit ?? seriesColor ?? point.color;
45
+
46
+ // Horizontal gap between the dot's edge and an 'outside'-anchored label —
47
+ // scales with the dot's own resolved radius so the label clears it.
48
+ const dotRadius = $derived(dotStyle?.dotRadius ?? cfg.hover.dot.dotRadius ?? 5);
49
+
50
+ /**
51
+ * Resolves `fontStyle.textAnchor: 'outside'` into a per-point anchor +
52
+ * offset, mirroring the value-label `'outside'` anchor's own left/right
53
+ * split (see pyramid/bar's own `ValueAnchor` handling): a positive `x`
54
+ * anchors `'start'` and nudges right, a negative `x` anchors `'end'` and
55
+ * nudges left — so a diverging mark's label sits on the side of the dot
56
+ * away from zero, on whichever side that dot itself landed. Only
57
+ * meaningful when the point's `x` is numeric (e.g. pyramid's magnitude
58
+ * axis); anything else falls through to the plain `fontStyle` unchanged.
59
+ *
60
+ * Also overrides `cfg.hover.font`'s default `dy: -18` (meant for a label
61
+ * floating *above* the dot) back to vertically centered, and clears the
62
+ * dot's own radius horizontally instead of the flat default nudge, so the
63
+ * label doesn't collide with the dot it's labeling.
64
+ */
65
+ function resolveFontStyle(point: HoverDisplayPoint) {
66
+ if (fontStyle?.textAnchor !== 'outside' || typeof point.x !== 'number') {
67
+ return { ...cfg.hover.font, ...fontStyle, fill: resolveColor(fontStyle?.fill, point) };
68
+ }
69
+ const outward = point.x >= 0;
70
+ const gap = dotRadius + 5;
71
+ return {
72
+ ...cfg.hover.font,
73
+ ...fontStyle,
74
+ fill: resolveColor(fontStyle?.fill, point),
75
+ textAnchor: outward ? ('start' as const) : ('end' as const),
76
+ lineAnchor: fontStyle?.lineAnchor ?? ('middle' as const),
77
+ dx: fontStyle?.dx ?? (outward ? gap : -gap),
78
+ dy: fontStyle?.dy ?? 0,
79
+ };
80
+ }
81
+ </script>
82
+
83
+ {#if ruleX != null}
84
+ <RuleX
85
+ data={[{ v: ruleX }]}
86
+ x={(d) => d.v}
87
+ stroke={ruleStyle?.stroke ?? cfg.hover.rule.stroke}
88
+ strokeOpacity={ruleStyle?.strokeOpacity ?? cfg.hover.rule.strokeOpacity}
89
+ strokeWidth={ruleStyle?.strokeWidth ?? cfg.hover.rule.strokeWidth}
90
+ strokeDasharray={ruleStyle?.strokeDasharray ?? cfg.hover.rule.strokeDasharray}
91
+ />
92
+ {/if}
93
+
94
+ {#if ruleY != null}
95
+ <RuleY
96
+ data={[{ v: ruleY }]}
97
+ y={(d) => d.v}
98
+ stroke={ruleStyle?.stroke ?? cfg.hover.rule.stroke}
99
+ strokeOpacity={ruleStyle?.strokeOpacity ?? cfg.hover.rule.strokeOpacity}
100
+ strokeWidth={ruleStyle?.strokeWidth ?? cfg.hover.rule.strokeWidth}
101
+ strokeDasharray={ruleStyle?.strokeDasharray ?? cfg.hover.rule.strokeDasharray}
102
+ />
103
+ {/if}
104
+
105
+ {#if data.length > 0}
106
+ {#each data as point (point.series ?? `${point.x}_${point.y}`)}
107
+ <!-- Pixel nudge for plot kinds whose series position isn't fully captured
108
+ by x/y alone (e.g. BarPlot's 'grouped' layout) — see HoverDisplayPoint.dx/dy. -->
109
+ <g transform="translate({point.dx ?? 0}, {point.dy ?? 0})">
110
+ <DotMarker
111
+ data={[point]}
112
+ x={(d) => d.x}
113
+ y={(d) => d.y}
114
+ style={{
115
+ ...cfg.hover.dot,
116
+ ...dotStyle,
117
+ dotFill: resolveColor(dotStyle?.dotFill, point),
118
+ dotStroke: resolveColor(dotStyle?.dotStroke, point),
119
+ }}
120
+ />
121
+ </g>
122
+ {/each}
123
+ {#if showLabels}
124
+ {#each data as point (point.series ?? `${point.x}_${point.y}`)}
125
+ <g transform="translate({point.dx ?? 0}, {point.dy ?? 0})">
126
+ <TextMarker
127
+ data={[point]}
128
+ x={(d) => d.x}
129
+ y={(d) => d.y}
130
+ text={(d) => format ? format(d) : (d.label ?? '')}
131
+ style={resolveFontStyle(point)}
132
+ />
133
+ </g>
134
+ {/each}
135
+ {/if}
136
+ {/if}
@@ -1,7 +1,7 @@
1
- import type { MarkerProps } from '../../types/plots/markers';
2
- import type { AxisValue } from '../../types/plots/common';
3
- import type { DotStyle, FontStyle, StrokeStyle } from '../../types/plots/styles';
4
- import type { HoverDisplayPoint } from '../../types/layout/tooltip';
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';
4
+ import type { HoverDisplayPoint } from '../types/layout/tooltip';
5
5
  /**
6
6
  * In-SVG hover highlight, rendered *inside* a `<Plot>`. Because it positions
7
7
  * itself through the host plot's scales, it places the same coordinate
@@ -1,8 +1,8 @@
1
1
  <script lang="ts">
2
2
  import DotMarker from './DotMarker.svelte';
3
3
  import TextMarker from './TextMarker.svelte';
4
- import type { MarkerProps } from '../../types/plots/markers';
5
- import type { DotStyle, FontStyle } from '../../types/plots/styles';
4
+ import type { MarkerProps } from '../types/markers/props';
5
+ import type { DotStyle, FontStyle } from '../types/plots/styling';
6
6
 
7
7
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
8
  interface Props extends MarkerProps<Record<string, any>> {
@@ -1,5 +1,5 @@
1
- import type { MarkerProps } from '../../types/plots/markers';
2
- import type { DotStyle, FontStyle } from '../../types/plots/styles';
1
+ import type { MarkerProps } from '../types/markers/props';
2
+ import type { DotStyle, FontStyle } from '../types/plots/styling';
3
3
  interface Props extends MarkerProps<Record<string, any>> {
4
4
  yProp?: string;
5
5
  xProp?: string;
@@ -1,8 +1,8 @@
1
1
  <script lang="ts" generics="TData = Record<string, unknown>">
2
2
  import { Text } from 'svelteplot';
3
- import type { MarkerProps } from '../../types/plots/markers';
4
- import type { AxisValue } from '../../types/plots/common';
5
- import type { FontStyle } from '../../types/plots/styles';
3
+ import type { MarkerProps } from '../types/markers/props';
4
+ import type { AxisValue } from '../types/plots/axis';
5
+ import type { FontStyle } from '../types/plots/styling';
6
6
 
7
7
  interface Props extends MarkerProps<TData> {
8
8
  x?: AxisValue | ((d: TData) => AxisValue);
@@ -28,7 +28,10 @@
28
28
  const fontSize = $derived(style?.fontSize ?? 12);
29
29
  const fontWeight = $derived(style?.fontWeight ?? 'normal');
30
30
  const fontStyleProp = $derived(style?.fontStyle ?? 'normal');
31
- const textAnchor = $derived(style?.textAnchor ?? 'middle');
31
+ // 'outside' is meant to be resolved per-point by the caller (see
32
+ // HoverMarker, which knows each point's signed x); a caller that passes it
33
+ // through unresolved falls back to 'start' rather than an invalid svelteplot value.
34
+ const textAnchor = $derived(style?.textAnchor === 'outside' ? 'start' : (style?.textAnchor ?? 'middle'));
32
35
  const lineAnchor = $derived(style?.lineAnchor ?? 'middle');
33
36
  const dx = $derived(style?.dx ?? 0);
34
37
  const dy = $derived(style?.dy ?? 0);
@@ -46,10 +49,14 @@
46
49
  fontStyle={fontStyleProp}
47
50
  {textAnchor}
48
51
  {lineAnchor}
52
+ lineHeight={style?.lineHeight}
53
+ rotate={style?.rotate}
49
54
  stroke={style?.stroke}
50
55
  strokeWidth={style?.strokeWidth}
51
56
  paintOrder={style?.paintOrder}
52
57
  {dx}
53
58
  {dy}
59
+ class={style?.class}
60
+ textClass={style?.textClass}
54
61
  />
55
62
  {/if}
@@ -1,6 +1,6 @@
1
- import type { MarkerProps } from '../../types/plots/markers';
2
- import type { AxisValue } from '../../types/plots/common';
3
- import type { FontStyle } from '../../types/plots/styles';
1
+ import type { MarkerProps } from '../types/markers/props';
2
+ import type { AxisValue } from '../types/plots/axis';
3
+ import type { FontStyle } from '../types/plots/styling';
4
4
  declare function $$render<TData = Record<string, unknown>>(): {
5
5
  props: MarkerProps<TData> & {
6
6
  x?: AxisValue | ((d: TData) => AxisValue);