@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.
- package/README.md +121 -17
- package/dist/charts/{Chart.svelte → BaseChart.svelte} +2 -0
- package/dist/charts/{Chart.svelte.d.ts → BaseChart.svelte.d.ts} +3 -3
- package/dist/charts/bar/Chart.svelte +5 -5
- package/dist/charts/bar/Chart.svelte.d.ts +1 -2
- package/dist/charts/heatmap/Chart.svelte +8 -7
- package/dist/charts/heatmap/Chart.svelte.d.ts +2 -3
- package/dist/charts/line/Chart.svelte +5 -5
- package/dist/charts/line/Chart.svelte.d.ts +1 -2
- package/dist/charts/pyramid/Chart.svelte +7 -6
- package/dist/charts/pyramid/Chart.svelte.d.ts +2 -3
- package/dist/configuration/config.svelte.js +2 -1
- package/dist/configuration/themes/index.d.ts +60 -40
- package/dist/index.d.ts +27 -13
- package/dist/index.js +12 -7
- package/dist/layout/facet/FacetLayout.svelte +1 -1
- package/dist/layout/facet/FacetLayout.svelte.d.ts +1 -1
- package/dist/layout/geo/GeoLayout.svelte +47 -0
- package/dist/layout/geo/GeoLayout.svelte.d.ts +22 -0
- package/dist/layout/geo/resolveProjection.d.ts +10 -0
- package/dist/layout/geo/resolveProjection.js +48 -0
- package/dist/layout/legend/ContinuousSection.svelte +158 -17
- package/dist/layout/legend/DiscreteSection.svelte +56 -4
- package/dist/layout/legend/interaction.svelte.d.ts +7 -0
- package/dist/layout/legend/interaction.svelte.js +24 -0
- package/dist/layout/plot/AxisLayout.svelte +95 -0
- package/dist/layout/plot/AxisLayout.svelte.d.ts +39 -0
- package/dist/layout/plot/BasePlotLayout.svelte +302 -0
- package/dist/layout/plot/BasePlotLayout.svelte.d.ts +95 -0
- package/dist/layout/plot/GridLayout.svelte +30 -0
- package/dist/layout/plot/GridLayout.svelte.d.ts +27 -0
- package/dist/layout/plot/RuleLayout.svelte +111 -0
- package/dist/layout/plot/RuleLayout.svelte.d.ts +40 -0
- package/dist/layout/plot/margins.d.ts +34 -0
- package/dist/layout/plot/margins.js +26 -0
- package/dist/layout/timeline/TimelineLayout.svelte +1 -1
- package/dist/layout/timeline/TimelineLayout.svelte.d.ts +1 -1
- package/dist/layout/tooltip/Tooltip.svelte +15 -0
- package/dist/layout/tooltip/Tooltip.svelte.d.ts +7 -0
- package/dist/layout/tooltip/TooltipLayout.svelte +45 -0
- package/dist/layout/tooltip/TooltipLayout.svelte.d.ts +41 -0
- package/dist/layout/tooltip/controller.svelte.d.ts +4 -3
- package/dist/layout/tooltip/controller.svelte.js +5 -6
- package/dist/layout/tooltip/hover.svelte.d.ts +1 -1
- package/dist/layout/tooltip/hover.svelte.js +1 -1
- package/dist/layout/tooltip/utils.d.ts +2 -2
- package/dist/markers/ContourClipMarker.svelte +77 -0
- package/dist/markers/ContourClipMarker.svelte.d.ts +10 -0
- package/dist/{plots/markers → markers}/DeltaMarker.svelte +4 -4
- package/dist/{plots/markers → markers}/DeltaMarker.svelte.d.ts +3 -3
- package/dist/{plots/markers → markers}/DotMarker.svelte +3 -3
- package/dist/{plots/markers → markers}/DotMarker.svelte.d.ts +3 -3
- package/dist/markers/HoverMarker.svelte +136 -0
- package/dist/{plots/markers → markers}/HoverMarker.svelte.d.ts +4 -4
- package/dist/{plots/markers → markers}/PeakMarker.svelte +2 -2
- package/dist/{plots/markers → markers}/PeakMarker.svelte.d.ts +2 -2
- package/dist/{plots/markers → markers}/TextMarker.svelte +11 -4
- package/dist/{plots/markers → markers}/TextMarker.svelte.d.ts +3 -3
- package/dist/markers/inset/InsetMarker.svelte +170 -0
- package/dist/markers/inset/InsetMarker.svelte.d.ts +51 -0
- package/dist/markers/inset/insetLayout.d.ts +25 -0
- package/dist/markers/inset/insetLayout.js +39 -0
- package/dist/markers/inset/insetProjection.d.ts +8 -0
- package/dist/markers/inset/insetProjection.js +39 -0
- package/dist/plots/bar/BarSegments.svelte +69 -0
- package/dist/plots/bar/BarSegments.svelte.d.ts +40 -0
- package/dist/plots/bar/Plot.svelte +140 -74
- package/dist/plots/bar/Plot.svelte.d.ts +7 -3
- package/dist/plots/bar/ValueLabels.svelte +89 -0
- package/dist/plots/bar/ValueLabels.svelte.d.ts +39 -0
- package/dist/plots/bar/hoverPoints.d.ts +15 -0
- package/dist/plots/bar/hoverPoints.js +35 -0
- package/dist/plots/bar/layout.svelte.d.ts +37 -0
- package/dist/plots/bar/layout.svelte.js +132 -0
- package/dist/plots/geo/Plot.svelte +577 -0
- package/dist/plots/geo/Plot.svelte.d.ts +29 -0
- package/dist/plots/geo/TileLayer.svelte +77 -0
- package/dist/plots/geo/TileLayer.svelte.d.ts +11 -0
- package/dist/plots/geo/projections/argentina.d.ts +8 -0
- package/dist/plots/geo/projections/argentina.js +35 -0
- package/dist/plots/geo/projections/fit.d.ts +8 -0
- package/dist/plots/geo/projections/fit.js +14 -0
- package/dist/plots/geo/rotate.svelte.d.ts +26 -0
- package/dist/plots/geo/rotate.svelte.js +79 -0
- package/dist/plots/geo/zoom.svelte.d.ts +32 -0
- package/dist/plots/geo/zoom.svelte.js +50 -0
- package/dist/plots/heatmap/Plot.svelte +171 -58
- package/dist/plots/heatmap/Plot.svelte.d.ts +7 -3
- package/dist/plots/line/Plot.svelte +67 -30
- package/dist/plots/line/Plot.svelte.d.ts +7 -3
- package/dist/plots/pyramid/Plot.svelte +88 -35
- package/dist/plots/pyramid/Plot.svelte.d.ts +7 -3
- package/dist/plots/utils/delta.d.ts +1 -1
- package/dist/plots/utils/geoAccessors.d.ts +64 -0
- package/dist/plots/utils/geoAccessors.js +188 -0
- package/dist/plots/utils/geoSegments.d.ts +14 -0
- package/dist/plots/utils/geoSegments.js +17 -0
- package/dist/plots/utils/legendDisabled.d.ts +15 -0
- package/dist/plots/utils/legendDisabled.js +41 -0
- package/dist/plots/utils/segments.d.ts +14 -5
- package/dist/plots/utils/segments.js +25 -13
- package/dist/plots/utils/tiles.d.ts +89 -0
- package/dist/plots/utils/tiles.js +159 -0
- package/dist/plots/utils/topojson.d.ts +9 -0
- package/dist/plots/utils/topojson.js +30 -0
- package/dist/types/charts/common.d.ts +2 -1
- package/dist/types/charts/legend.d.ts +32 -1
- package/dist/types/charts/props.d.ts +9 -6
- package/dist/types/configuration/styling.d.ts +13 -5
- package/dist/types/layout/legend.d.ts +26 -0
- package/dist/types/layout/tooltip.d.ts +14 -21
- package/dist/types/{plots/markers.d.ts → markers/common.d.ts} +2 -18
- package/dist/types/markers/geo.d.ts +237 -0
- package/dist/types/markers/props.d.ts +11 -0
- package/dist/types/markers/props.js +1 -0
- package/dist/types/plots/axis.d.ts +55 -0
- package/dist/types/plots/axis.js +1 -0
- package/dist/types/plots/constants.d.ts +30 -0
- package/dist/types/plots/constants.js +1 -0
- package/dist/types/plots/data/common.d.ts +40 -0
- package/dist/types/plots/data/common.js +1 -0
- package/dist/types/plots/data/geo.d.ts +74 -0
- package/dist/types/plots/data/geo.js +1 -0
- package/dist/types/plots/delta.d.ts +2 -2
- package/dist/types/plots/props.d.ts +105 -16
- package/dist/types/plots/scales/geo.d.ts +44 -0
- package/dist/types/plots/scales/geo.js +1 -0
- package/dist/types/plots/segments/common.d.ts +25 -0
- package/dist/types/plots/segments/common.js +1 -0
- package/dist/types/plots/segments/config.d.ts +51 -0
- package/dist/types/plots/segments/config.js +1 -0
- package/dist/types/plots/styles/common.d.ts +37 -0
- package/dist/types/plots/styles/common.js +1 -0
- package/dist/types/plots/styles/geo.d.ts +42 -0
- package/dist/types/plots/styles/geo.js +1 -0
- package/dist/types/plots/styling.d.ts +45 -0
- package/dist/types/plots/styling.js +1 -0
- package/dist/utils/grouping.d.ts +2 -1
- package/dist/utils/interpolate.d.ts +2 -1
- package/package.json +27 -13
- package/dist/layout/plot/PlotLayout.svelte +0 -294
- package/dist/layout/plot/PlotLayout.svelte.d.ts +0 -70
- package/dist/plots/markers/HoverMarker.svelte +0 -98
- package/dist/types/plots/common.d.ts +0 -97
- package/dist/types/plots/styles.d.ts +0 -187
- /package/dist/types/{plots/common.js → layout/legend.js} +0 -0
- /package/dist/types/{plots/markers.js → markers/common.js} +0 -0
- /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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
<span class="dct-continuous__tick" style:left="{
|
|
65
|
-
{
|
|
66
|
-
|
|
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
|
|
5
|
-
import type {
|
|
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
|
|
29
|
-
|
|
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;
|