@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
@@ -1,32 +1,54 @@
1
1
  <script lang="ts">
2
+ import { untrack } from 'svelte';
2
3
  import type { ContinuousLegendSection } from '../../types/charts/legend';
3
4
  import { makeColorScale } from '../../utils/color';
4
5
  import { getConfiguration } from '../../configuration/config.svelte';
6
+ import { getLegendInteraction, createLegendInteraction } from './interaction.svelte';
5
7
 
6
8
  let { section }: { section: ContinuousLegendSection } = $props();
7
9
 
8
10
  const cfg = $derived(getConfiguration());
9
11
  const gradientId = `dct-grad-${Math.random().toString(36).slice(2)}`;
10
12
  const barWidth = $derived(section.width ?? 170);
13
+
14
+ const fmt = $derived(section.format ?? ((v: number) => String(v)));
15
+
16
+ const tickPositions = $derived(
17
+ section.interactive
18
+ ? []
19
+ : (section.ticks ?? []).map((v) => ({
20
+ v,
21
+ t: (v - section.min) / (section.max - section.min || 1),
22
+ label: fmt(v),
23
+ })),
24
+ );
25
+
26
+ // ── Drag-to-narrow-margins ────────────────────────────────────────────────
27
+ const interaction = getLegendInteraction() ?? createLegendInteraction();
28
+
29
+ // Seeded once from the section's own bounds (deliberately untracked — this
30
+ // component doesn't follow later `section.min`/`max` prop changes).
31
+ let lo = $state(untrack(() => section.min));
32
+ let hi = $state(untrack(() => section.max));
33
+
34
+ function toT(v: number): number {
35
+ return (v - section.min) / (section.max - section.min || 1);
36
+ }
37
+ const loT = $derived(toT(lo));
38
+ const hiT = $derived(toT(hi));
39
+
40
+ const colorDomain = $derived.by((): [number, number] =>
41
+ section.interactive && (section.mode ?? 'filter') === 'rescale' ? [lo, hi] : [section.min, section.max],
42
+ );
11
43
  const colorScale = $derived(
12
44
  makeColorScale(
13
- section.min,
14
- section.max,
45
+ colorDomain[0],
46
+ colorDomain[1],
15
47
  section.colorMin ?? cfg.continuous.min,
16
48
  section.colorMax ?? cfg.continuous.max,
17
49
  ),
18
50
  );
19
51
 
20
- const fmt = $derived(section.format ?? ((v: number) => String(v)));
21
-
22
- const tickPositions = $derived(
23
- (section.ticks ?? []).map((v) => ({
24
- v,
25
- t: (v - section.min) / (section.max - section.min || 1),
26
- label: fmt(v),
27
- })),
28
- );
29
-
30
52
  const stops = $derived(
31
53
  Array.from({ length: 20 }, (_, i) => {
32
54
  const t = i / 19;
@@ -34,6 +56,56 @@
34
56
  return { offset: t * 100, color: colorScale(v) };
35
57
  }),
36
58
  );
59
+
60
+ $effect(() => {
61
+ if (!section.interactive) return;
62
+ interaction.continuousRange =
63
+ lo <= section.min && hi >= section.max
64
+ ? null
65
+ : {
66
+ min: lo,
67
+ max: hi,
68
+ mode: section.mode ?? 'filter',
69
+ disabledStyle: section.disabledStyle ?? { opacity: cfg.legend.disabledOpacity },
70
+ };
71
+ });
72
+
73
+ function startDrag(which: 'lo' | 'hi') {
74
+ return (event: PointerEvent) => {
75
+ if (!section.interactive) return;
76
+ event.preventDefault();
77
+ const target = event.currentTarget as SVGElement;
78
+ target.setPointerCapture(event.pointerId);
79
+ const startX = event.clientX;
80
+ const startValue = which === 'lo' ? lo : hi;
81
+ const valuePerPixel = (section.max - section.min) / barWidth;
82
+
83
+ function onMove(e: PointerEvent): void {
84
+ const next = startValue + (e.clientX - startX) * valuePerPixel;
85
+ if (which === 'lo') {
86
+ lo = Math.max(section.min, Math.min(next, hi));
87
+ } else {
88
+ hi = Math.min(section.max, Math.max(next, lo));
89
+ }
90
+ }
91
+ function onUp(e: PointerEvent): void {
92
+ target.releasePointerCapture(e.pointerId);
93
+ target.removeEventListener('pointermove', onMove);
94
+ target.removeEventListener('pointerup', onUp);
95
+ target.removeEventListener('pointercancel', onUp);
96
+ }
97
+ target.addEventListener('pointermove', onMove);
98
+ target.addEventListener('pointerup', onUp);
99
+ target.addEventListener('pointercancel', onUp);
100
+ };
101
+ }
102
+
103
+ function resetHandle(which: 'lo' | 'hi') {
104
+ return () => {
105
+ if (which === 'lo') lo = section.min;
106
+ else hi = section.max;
107
+ };
108
+ }
37
109
  </script>
38
110
 
39
111
  <div class="dct-continuous" style:width="{barWidth}px">
@@ -56,14 +128,56 @@
56
128
  stroke-width="1"
57
129
  />
58
130
  {/each}
131
+
132
+ {#if section.interactive}
133
+ {#if loT > 0}
134
+ <rect x="0" y="0" width={loT * barWidth} height="12" rx="2" fill="currentColor" fill-opacity="0.35" />
135
+ {/if}
136
+ {#if hiT < 1}
137
+ <rect x={hiT * barWidth} y="0" width={barWidth - hiT * barWidth} height="12" rx="2" fill="currentColor" fill-opacity="0.35" />
138
+ {/if}
139
+ <circle
140
+ class="dct-continuous__handle"
141
+ cx={loT * barWidth}
142
+ cy="6"
143
+ r="6"
144
+ onpointerdown={startDrag('lo')}
145
+ ondblclick={resetHandle('lo')}
146
+ role="slider"
147
+ aria-label="Margen inferior"
148
+ aria-valuemin={section.min}
149
+ aria-valuemax={hi}
150
+ aria-valuenow={lo}
151
+ tabindex="0"
152
+ />
153
+ <circle
154
+ class="dct-continuous__handle"
155
+ cx={hiT * barWidth}
156
+ cy="6"
157
+ r="6"
158
+ onpointerdown={startDrag('hi')}
159
+ ondblclick={resetHandle('hi')}
160
+ role="slider"
161
+ aria-label="Margen superior"
162
+ aria-valuemin={lo}
163
+ aria-valuemax={section.max}
164
+ aria-valuenow={hi}
165
+ tabindex="0"
166
+ />
167
+ {/if}
59
168
  </svg>
60
169
 
61
170
  <div class="dct-continuous__labels" style:width="{barWidth}px">
62
- <span class="dct-continuous__edge dct-continuous__edge--start">{fmt(section.min)}</span>
63
- {#each tickPositions as tick (tick.v)}
64
- <span class="dct-continuous__tick" style:left="{tick.t * barWidth}px">{tick.label}</span>
65
- {/each}
66
- <span class="dct-continuous__edge dct-continuous__edge--end">{fmt(section.max)}</span>
171
+ {#if section.interactive}
172
+ <span class="dct-continuous__tick dct-continuous__tick--handle-lo" style:left="{loT * barWidth}px">{fmt(lo)}</span>
173
+ <span class="dct-continuous__tick dct-continuous__tick--handle-hi" style:left="{hiT * barWidth}px">{fmt(hi)}</span>
174
+ {:else}
175
+ <span class="dct-continuous__edge dct-continuous__edge--start">{fmt(section.min)}</span>
176
+ {#each tickPositions as tick (tick.v)}
177
+ <span class="dct-continuous__tick" style:left="{tick.t * barWidth}px">{tick.label}</span>
178
+ {/each}
179
+ <span class="dct-continuous__edge dct-continuous__edge--end">{fmt(section.max)}</span>
180
+ {/if}
67
181
  </div>
68
182
  </div>
69
183
 
@@ -92,4 +206,31 @@
92
206
  position: absolute;
93
207
  transform: translateX(-50%);
94
208
  }
209
+ /*
210
+ * Anchored outward (not centered, unlike a plain tick) so the label always
211
+ * grows into the bar instead of over its own edge — a centered label at
212
+ * lo=min or hi=max would overflow past `.dct-chart`'s `overflow: hidden`
213
+ * and get clipped, exactly like the old fixed min/max edge labels never did.
214
+ */
215
+ .dct-continuous__tick--handle-lo,
216
+ .dct-continuous__tick--handle-hi {
217
+ font-weight: 600;
218
+ }
219
+ .dct-continuous__tick--handle-lo {
220
+ transform: none;
221
+ }
222
+ .dct-continuous__tick--handle-hi {
223
+ transform: translateX(-100%);
224
+ }
225
+ .dct-continuous__handle {
226
+ fill: currentColor;
227
+ stroke: var(--dct-bg, #fff);
228
+ stroke-width: 1.5;
229
+ cursor: ew-resize;
230
+ touch-action: none;
231
+ }
232
+ .dct-continuous__handle:focus-visible {
233
+ outline: 2px solid currentColor;
234
+ outline-offset: 2px;
235
+ }
95
236
  </style>
@@ -1,13 +1,42 @@
1
1
  <script lang="ts">
2
+ import { SvelteSet } from 'svelte/reactivity';
2
3
  import { getConfiguration } from '../../configuration/config.svelte';
3
4
  import { paletteColor } from '../../utils/color';
4
- import type { DiscreteLegendSection } from '../../types/charts/legend';
5
- import type { LineStyle } from '../../types/plots/styles';
5
+ import { getLegendInteraction, createLegendInteraction } from './interaction.svelte';
6
+ import type { DiscreteLegendSection, LegendItem } from '../../types/charts/legend';
7
+ import type { LineStyle } from '../../types/plots/constants';
6
8
 
7
9
  let { section }: { section: DiscreteLegendSection } = $props();
8
10
 
9
11
  const cfg = $derived(getConfiguration());
10
12
  const columns = $derived(section.columns ?? 1);
13
+ const interaction = getLegendInteraction() ?? createLegendInteraction();
14
+
15
+ const seeded = new SvelteSet<string>();
16
+ $effect(() => {
17
+ for (const item of section.items) {
18
+ if (seeded.has(item.name)) continue;
19
+ seeded.add(item.name);
20
+ if (item.disabled) {
21
+ interaction.disabledSeries.set(item.name, section.disabledStyle ?? { opacity: cfg.legend.disabledOpacity });
22
+ }
23
+ }
24
+ });
25
+
26
+ function toggle(item: LegendItem): void {
27
+ if (!section.interactive) return;
28
+ if (interaction.disabledSeries.has(item.name)) {
29
+ interaction.disabledSeries.delete(item.name);
30
+ } else {
31
+ interaction.disabledSeries.set(item.name, section.disabledStyle ?? { opacity: cfg.legend.disabledOpacity });
32
+ }
33
+ }
34
+
35
+ function onKeydown(event: KeyboardEvent, item: LegendItem): void {
36
+ if (event.key !== 'Enter' && event.key !== ' ') return;
37
+ event.preventDefault();
38
+ toggle(item);
39
+ }
11
40
 
12
41
  function dashArray(style?: LineStyle): string | undefined {
13
42
  switch (style) {
@@ -25,8 +54,23 @@
25
54
 
26
55
  <div class="dct-discrete" style:grid-template-columns="repeat({columns}, max-content)">
27
56
  {#each section.items as item, i (item.name)}
28
- {@const color = item.color ?? paletteColor(i, cfg.palette)}
29
- <div class="dct-discrete__item" style:opacity={item.opacity ?? 1}>
57
+ {@const disabledStyle = interaction.disabledSeries.get(item.name)}
58
+ {@const color = disabledStyle?.fill ?? disabledStyle?.stroke?.stroke ?? item.color ?? paletteColor(i, cfg.palette)}
59
+ {@const itemOpacity = disabledStyle ? (disabledStyle.opacity ?? cfg.legend.disabledOpacity) : (item.opacity ?? 1)}
60
+ <div
61
+ class="dct-discrete__item"
62
+ class:dct-discrete__item--interactive={section.interactive}
63
+ style:opacity={itemOpacity}
64
+ {...section.interactive
65
+ ? {
66
+ role: 'button',
67
+ tabindex: 0,
68
+ 'aria-pressed': disabledStyle != null,
69
+ onclick: () => toggle(item),
70
+ onkeydown: (e: KeyboardEvent) => onKeydown(e, item),
71
+ }
72
+ : {}}
73
+ >
30
74
  <svg class="dct-discrete__swatch" viewBox="0 0 15 10">
31
75
  {#if item.symbol === 'circle'}
32
76
  <circle cx="5" cy="5" r="4" fill={color} />
@@ -70,6 +114,14 @@
70
114
  align-items: center;
71
115
  gap: 0.35rem;
72
116
  min-width: 0;
117
+ transition: opacity 0.15s ease;
118
+ }
119
+ .dct-discrete__item--interactive {
120
+ cursor: pointer;
121
+ }
122
+ .dct-discrete__item--interactive:focus-visible {
123
+ outline: 2px solid currentColor;
124
+ outline-offset: 2px;
73
125
  }
74
126
  .dct-discrete__swatch {
75
127
  width: var(--dct-legend-swatch, 1.25rem);
@@ -0,0 +1,7 @@
1
+ import type { LegendInteractionStore } from '../../types/layout/legend';
2
+ /** Create + register the shared legend-interaction store. Call during component init. */
3
+ export declare function provideLegendInteraction(): LegendInteractionStore;
4
+ /** Read the shared legend-interaction store, or `null` when there is no provider. */
5
+ export declare function getLegendInteraction(): LegendInteractionStore | null;
6
+ /** A private legend-interaction store for a legend/plot used outside of a {@link BaseChart}. */
7
+ export declare function createLegendInteraction(): LegendInteractionStore;
@@ -0,0 +1,24 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ import { SvelteMap } from 'svelte/reactivity';
3
+ const LEGEND_INTERACTION_KEY = Symbol('dct-legend-interaction');
4
+ function blank() {
5
+ return {
6
+ disabledSeries: new SvelteMap(),
7
+ continuousRange: null,
8
+ };
9
+ }
10
+ /** Create + register the shared legend-interaction store. Call during component init. */
11
+ export function provideLegendInteraction() {
12
+ const store = $state(blank());
13
+ setContext(LEGEND_INTERACTION_KEY, store);
14
+ return store;
15
+ }
16
+ /** Read the shared legend-interaction store, or `null` when there is no provider. */
17
+ export function getLegendInteraction() {
18
+ return getContext(LEGEND_INTERACTION_KEY) ?? null;
19
+ }
20
+ /** A private legend-interaction store for a legend/plot used outside of a {@link BaseChart}. */
21
+ export function createLegendInteraction() {
22
+ const store = $state(blank());
23
+ return store;
24
+ }
@@ -0,0 +1,95 @@
1
+ <script lang="ts" generics="TData extends Record<string, unknown>">
2
+ import { AxisX, AxisY } from 'svelteplot';
3
+ import { getConfiguration } from '../../configuration/config.svelte';
4
+ import type { AxisValue } from '../../types/plots/axis';
5
+ import type { AxisBasedScalesConfig } from '../../types/plots/props';
6
+
7
+ /**
8
+ * Axis ticks/titles for the plot kinds that have an x/y axis pair
9
+ * (line/bar/heatmap/pyramid) — reads the scale/tick config off `scales`,
10
+ * against the plot's own resolved margins. Not used by `GeoPlot`, whose
11
+ * position comes from a `d3-geo` projection instead (see `GeoLayout`).
12
+ * `AxisX`/`AxisY` themselves inherit their actual scale from svelteplot's
13
+ * `<Plot>` context — this only supplies their cosmetic/tick-count props.
14
+ */
15
+ let {
16
+ data,
17
+ getX,
18
+ getY,
19
+ width,
20
+ height,
21
+ numericMargins,
22
+ scales,
23
+ }: {
24
+ data: TData[];
25
+ getX: (d: TData) => AxisValue;
26
+ getY: (d: TData) => AxisValue;
27
+ width: number;
28
+ height: number;
29
+ numericMargins: { top: number; right: number; bottom: number; left: number };
30
+ scales?: AxisBasedScalesConfig<TData>;
31
+ } = $props();
32
+
33
+ const cfg = $derived(getConfiguration());
34
+
35
+ const xValues = $derived(data.map(getX));
36
+ const yValues = $derived(data.map(getY));
37
+
38
+ /**
39
+ * Caps the requested tick count to the axis's own number of distinct
40
+ * values, but never above what the pixel width would naturally request —
41
+ * so a narrow domain (e.g. 5 whole years) never gets subdivided into
42
+ * fractional ticks (2020, 2020.5, 2021, …) just to fill the space. Skipped
43
+ * whenever the caller already controls tick density directly.
44
+ */
45
+ function autoTickCount(scale: AxisBasedScalesConfig<TData>['x'], values: AxisValue[], span: number, spacing: number, min: number): number | undefined {
46
+ if (scale?.ticks != null || scale?.tickSpacing != null || scale?.interval != null) return undefined;
47
+ const uniqueCount = new Set(values.map(String)).size;
48
+ const natural = Math.max(min, Math.round(span / spacing));
49
+ return uniqueCount >= 2 && uniqueCount < natural ? uniqueCount : undefined;
50
+ }
51
+
52
+ const xTickCount = $derived(
53
+ autoTickCount(scales?.x, xValues, Math.max(0, width - numericMargins.left - numericMargins.right), 80, 3),
54
+ );
55
+ const yTickCount = $derived(
56
+ autoTickCount(scales?.y, yValues, Math.max(0, height - numericMargins.top - numericMargins.bottom), 50, 2),
57
+ );
58
+
59
+ /** Vertical Y-axis title placement, when `scales.y.labelOrientation === 'vertical'`. */
60
+ const verticalYLabel = $derived.by(() => {
61
+ if (!scales?.y?.label || scales.y.labelOrientation !== 'vertical') return null;
62
+ const position = scales.y.labelPosition ?? 'middle';
63
+ const x = 14;
64
+ const top = numericMargins.top;
65
+ const bottom = height - numericMargins.bottom;
66
+ const y = position === 'top' ? top : (top + bottom) / 2;
67
+ return { text: scales.y.label, x, y, anchor: position === 'top' ? ('end' as const) : ('middle' as const) };
68
+ });
69
+ </script>
70
+
71
+ <AxisX
72
+ tickCount={xTickCount}
73
+ tickSize={scales?.x?.tickSize ?? cfg.axis.xTickSize}
74
+ tickPadding={scales?.x?.tickPadding ?? cfg.axis.xTickPadding}
75
+ tickFontSize={cfg.axis.fontSize}
76
+ titleFontSize={scales?.x?.titleFontSize ?? cfg.axis.titleFontSize}
77
+ />
78
+ <AxisY
79
+ tickCount={yTickCount}
80
+ tickSize={scales?.y?.tickSize ?? cfg.axis.yTickSize}
81
+ tickPadding={scales?.y?.tickPadding ?? cfg.axis.yTickPadding}
82
+ tickFontSize={cfg.axis.fontSize}
83
+ titleFontSize={scales?.y?.titleFontSize ?? cfg.axis.titleFontSize}
84
+ />
85
+ {#if verticalYLabel}
86
+ <text
87
+ x={verticalYLabel.x}
88
+ y={verticalYLabel.y}
89
+ transform="rotate(-90, {verticalYLabel.x}, {verticalYLabel.y})"
90
+ text-anchor={verticalYLabel.anchor}
91
+ font-size={scales?.y?.titleFontSize ?? cfg.axis.titleFontSize}
92
+ fill={cfg.axis.color}
93
+ opacity="0.8"
94
+ >{verticalYLabel.text}</text>
95
+ {/if}
@@ -0,0 +1,39 @@
1
+ import type { AxisValue } from '../../types/plots/axis';
2
+ import type { AxisBasedScalesConfig } from '../../types/plots/props';
3
+ declare function $$render<TData extends Record<string, unknown>>(): {
4
+ props: {
5
+ data: TData[];
6
+ getX: (d: TData) => AxisValue;
7
+ getY: (d: TData) => AxisValue;
8
+ width: number;
9
+ height: number;
10
+ numericMargins: {
11
+ top: number;
12
+ right: number;
13
+ bottom: number;
14
+ left: number;
15
+ };
16
+ scales?: AxisBasedScalesConfig<TData>;
17
+ };
18
+ exports: {};
19
+ bindings: "";
20
+ slots: {};
21
+ events: {};
22
+ };
23
+ declare class __sveltets_Render<TData extends Record<string, unknown>> {
24
+ props(): ReturnType<typeof $$render<TData>>['props'];
25
+ events(): ReturnType<typeof $$render<TData>>['events'];
26
+ slots(): ReturnType<typeof $$render<TData>>['slots'];
27
+ bindings(): "";
28
+ exports(): {};
29
+ }
30
+ interface $$IsomorphicComponent {
31
+ new <TData extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TData>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TData>['props']>, ReturnType<__sveltets_Render<TData>['events']>, ReturnType<__sveltets_Render<TData>['slots']>> & {
32
+ $$bindings?: ReturnType<__sveltets_Render<TData>['bindings']>;
33
+ } & ReturnType<__sveltets_Render<TData>['exports']>;
34
+ <TData extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<TData>['props']> & {}): ReturnType<__sveltets_Render<TData>['exports']>;
35
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
36
+ }
37
+ declare const AxisLayout: $$IsomorphicComponent;
38
+ type AxisLayout<TData extends Record<string, unknown>> = InstanceType<typeof AxisLayout<TData>>;
39
+ export default AxisLayout;