@fundar/data-chart-telling 0.0.25 → 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.
- package/dist/charts/bar/Chart.svelte +2 -0
- package/dist/charts/heatmap/Chart.svelte +3 -1
- package/dist/charts/heatmap/Chart.svelte.d.ts +1 -2
- package/dist/charts/line/Chart.svelte +2 -0
- package/dist/charts/pyramid/Chart.svelte +3 -1
- package/dist/charts/pyramid/Chart.svelte.d.ts +1 -2
- package/dist/charts/scatter/Chart.svelte +95 -0
- package/dist/charts/scatter/Chart.svelte.d.ts +28 -0
- package/dist/configuration/config.svelte.js +2 -0
- package/dist/configuration/themes/index.d.ts +48 -0
- package/dist/index.d.ts +20 -14
- package/dist/index.js +9 -3
- package/dist/layout/coordinates/CoordinatesLayout.svelte +95 -0
- package/dist/layout/{plot/RuleLayout.svelte.d.ts → coordinates/CoordinatesLayout.svelte.d.ts} +13 -16
- package/dist/layout/coordinates/margins.d.ts +16 -0
- package/dist/layout/coordinates/margins.js +11 -0
- package/dist/{plots/geo → layout/coordinates}/projections/argentina.d.ts +1 -1
- package/dist/layout/{geo → coordinates}/resolveProjection.d.ts +1 -1
- package/dist/layout/coordinates/resolveProjection.js +24 -0
- package/dist/{plots/geo → layout/coordinates}/zoom.svelte.d.ts +1 -1
- package/dist/layout/geometry/GeometryLayout.svelte +231 -0
- package/dist/layout/geometry/GeometryLayout.svelte.d.ts +62 -0
- package/dist/layout/plot/BasePlotLayout.svelte +142 -155
- package/dist/layout/plot/BasePlotLayout.svelte.d.ts +61 -51
- package/dist/layout/plot/margins.d.ts +0 -16
- package/dist/layout/plot/margins.js +0 -11
- package/dist/layout/{plot/AxisLayout.svelte → scales/ScalesLayout.svelte} +23 -16
- package/dist/layout/{plot/AxisLayout.svelte.d.ts → scales/ScalesLayout.svelte.d.ts} +4 -5
- package/dist/layout/scales/buildScale.d.ts +8 -0
- package/dist/layout/scales/buildScale.js +43 -0
- package/dist/layout/styles/StylesLayout.svelte +75 -0
- package/dist/layout/styles/StylesLayout.svelte.d.ts +15 -0
- package/dist/{plots/geo → layout/styles}/TileLayer.svelte +2 -2
- package/dist/{plots/geo → layout/styles}/TileLayer.svelte.d.ts +1 -1
- package/dist/layout/tooltip/Tooltip.svelte +16 -20
- package/dist/layout/tooltip/Tooltip.svelte.d.ts +5 -0
- package/dist/layout/tooltip/TooltipLayout.svelte +7 -1
- package/dist/layout/tooltip/TooltipLayout.svelte.d.ts +4 -0
- package/dist/layout/tooltip/controller.svelte.d.ts +4 -5
- package/dist/layout/tooltip/controller.svelte.js +28 -16
- package/dist/layout/tooltip/resolveTooltipPlacement.d.ts +24 -0
- package/dist/layout/tooltip/resolveTooltipPlacement.js +49 -0
- package/dist/layout/tooltip/utils.d.ts +27 -19
- package/dist/layout/tooltip/utils.js +16 -15
- package/dist/markers/BarMarker.svelte +92 -0
- package/dist/markers/BarMarker.svelte.d.ts +7 -0
- package/dist/markers/CellMarker.svelte +21 -0
- package/dist/markers/CellMarker.svelte.d.ts +7 -0
- package/dist/markers/ContourMarker.svelte +28 -0
- package/dist/markers/ContourMarker.svelte.d.ts +7 -0
- package/dist/markers/DeltaMarker.svelte +32 -39
- package/dist/markers/DeltaMarker.svelte.d.ts +4 -5
- package/dist/markers/DotMarker.svelte +35 -6
- package/dist/markers/DotMarker.svelte.d.ts +11 -2
- package/dist/markers/GeoFeaturesMarker.svelte +19 -0
- package/dist/markers/GeoFeaturesMarker.svelte.d.ts +7 -0
- package/dist/markers/GeoHoverMarker.svelte +25 -0
- package/dist/markers/GeoHoverMarker.svelte.d.ts +9 -0
- package/dist/markers/GraticuleMarker.svelte +18 -0
- package/dist/markers/GraticuleMarker.svelte.d.ts +7 -0
- package/dist/markers/HoverMarker.svelte +32 -33
- package/dist/markers/HoverMarker.svelte.d.ts +15 -9
- package/dist/markers/LineMarker.svelte +19 -0
- package/dist/markers/LineMarker.svelte.d.ts +7 -0
- package/dist/markers/LinkMarker.svelte +42 -0
- package/dist/markers/LinkMarker.svelte.d.ts +7 -0
- package/dist/markers/RuleMarker.svelte +24 -0
- package/dist/markers/RuleMarker.svelte.d.ts +10 -0
- package/dist/markers/SphereMarker.svelte +15 -0
- package/dist/markers/SphereMarker.svelte.d.ts +7 -0
- package/dist/markers/TextMarker.svelte +1 -1
- package/dist/markers/TextMarker.svelte.d.ts +1 -1
- package/dist/markers/VectorMarker.svelte +20 -0
- package/dist/markers/VectorMarker.svelte.d.ts +7 -0
- package/dist/markers/inset/InsetMarker.svelte +38 -45
- package/dist/markers/inset/InsetMarker.svelte.d.ts +6 -2
- package/dist/plots/bar/Plot.svelte +54 -84
- package/dist/plots/bar/Plot.svelte.d.ts +4 -5
- package/dist/plots/bar/ValueLabels.svelte +1 -1
- package/dist/plots/bar/ValueLabels.svelte.d.ts +1 -1
- package/dist/plots/bar/buildBarMarkers.d.ts +30 -0
- package/dist/plots/bar/buildBarMarkers.js +100 -0
- package/dist/plots/bar/layout.svelte.d.ts +1 -2
- package/dist/plots/bar/layout.svelte.js +1 -1
- package/dist/plots/geo/Plot.svelte +113 -387
- package/dist/plots/geo/Plot.svelte.d.ts +5 -4
- package/dist/plots/geo/buildGeoMarkers.d.ts +33 -0
- package/dist/plots/geo/buildGeoMarkers.js +47 -0
- package/dist/plots/heatmap/Plot.svelte +24 -49
- package/dist/plots/heatmap/Plot.svelte.d.ts +4 -5
- package/dist/plots/heatmap/ValueLabels.svelte +2 -2
- package/dist/plots/heatmap/ValueLabels.svelte.d.ts +2 -2
- package/dist/plots/heatmap/buildCellMarkers.d.ts +21 -0
- package/dist/plots/heatmap/buildCellMarkers.js +25 -0
- package/dist/plots/line/Plot.svelte +68 -141
- package/dist/plots/line/Plot.svelte.d.ts +4 -4
- package/dist/plots/line/ValueLabels.svelte +29 -52
- package/dist/plots/line/ValueLabels.svelte.d.ts +6 -1
- package/dist/plots/line/buildLineMarkers.d.ts +15 -0
- package/dist/plots/line/buildLineMarkers.js +57 -0
- package/dist/plots/pyramid/Plot.svelte +41 -117
- package/dist/plots/pyramid/Plot.svelte.d.ts +4 -5
- package/dist/plots/pyramid/ValueLabels.svelte +1 -1
- package/dist/plots/pyramid/ValueLabels.svelte.d.ts +1 -1
- package/dist/plots/pyramid/buildPyramidBarMarkers.d.ts +19 -0
- package/dist/plots/pyramid/buildPyramidBarMarkers.js +32 -0
- package/dist/plots/scatter/Plot.svelte +206 -0
- package/dist/plots/{bar/BarSegments.svelte.d.ts → scatter/Plot.svelte.d.ts} +9 -20
- package/dist/plots/scatter/ValueLabels.svelte +106 -0
- package/dist/{layout/plot/GridLayout.svelte.d.ts → plots/scatter/ValueLabels.svelte.d.ts} +11 -5
- package/dist/plots/scatter/buildScatterMarkers.d.ts +11 -0
- package/dist/plots/scatter/buildScatterMarkers.js +37 -0
- package/dist/plots/scatter/resolveRadiusPaddedDomain.d.ts +10 -0
- package/dist/plots/scatter/resolveRadiusPaddedDomain.js +23 -0
- package/dist/plots/utils/declutter.d.ts +55 -37
- package/dist/plots/utils/declutter.js +79 -64
- package/dist/plots/utils/geoAccessors.d.ts +1 -1
- package/dist/plots/utils/geoAccessors.js +1 -1
- package/dist/plots/utils/segments.d.ts +17 -10
- package/dist/plots/utils/segments.js +26 -16
- package/dist/types/charts/common.d.ts +1 -1
- package/dist/types/charts/props.d.ts +5 -6
- package/dist/types/configuration/styling.d.ts +7 -2
- package/dist/types/{plots/scales/geo.d.ts → layout/coordinates.d.ts} +33 -1
- package/dist/types/layout/geometry.d.ts +17 -0
- package/dist/types/{plots/axis.d.ts → layout/scales.d.ts} +11 -0
- package/dist/types/layout/styles.d.ts +126 -0
- package/dist/types/layout/tooltip.d.ts +20 -1
- package/dist/types/markers/all.d.ts +11 -0
- package/dist/types/markers/base.d.ts +45 -0
- package/dist/types/markers/base.js +1 -0
- package/dist/types/markers/common.d.ts +64 -16
- package/dist/types/markers/geo.d.ts +75 -100
- package/dist/types/markers/line.d.ts +34 -0
- package/dist/types/markers/line.js +1 -0
- package/dist/types/markers/props.d.ts +1 -1
- package/dist/types/plots/data/common.d.ts +1 -1
- package/dist/types/plots/data/scatter.d.ts +13 -0
- package/dist/types/plots/data/scatter.js +1 -0
- package/dist/types/plots/props.d.ts +46 -74
- package/dist/types/plots/segments/common.d.ts +6 -12
- package/dist/types/plots/segments/config.d.ts +1 -1
- package/dist/types/plots/styling.d.ts +10 -0
- package/dist/{plots/utils → utils}/delta.d.ts +2 -2
- package/dist/{plots/utils → utils}/delta.js +1 -1
- package/dist/utils/edgeAwareAnchor.d.ts +37 -0
- package/dist/utils/edgeAwareAnchor.js +65 -0
- package/dist/utils/grouping.d.ts +1 -1
- package/dist/utils/interpolate.d.ts +1 -1
- package/package.json +3 -1
- package/dist/layout/geo/GeoLayout.svelte +0 -51
- package/dist/layout/geo/GeoLayout.svelte.d.ts +0 -24
- package/dist/layout/geo/resolveProjection.js +0 -48
- package/dist/layout/plot/GridLayout.svelte +0 -30
- package/dist/layout/plot/RuleLayout.svelte +0 -111
- package/dist/plots/bar/BarSegments.svelte +0 -69
- package/dist/types/plots/delta.d.ts +0 -19
- package/dist/types/plots/styles/common.d.ts +0 -52
- package/dist/types/plots/styles/geo.d.ts +0 -42
- /package/dist/{plots/geo → layout/coordinates}/projections/argentina.js +0 -0
- /package/dist/{plots/geo → layout/coordinates}/projections/fit.d.ts +0 -0
- /package/dist/{plots/geo → layout/coordinates}/projections/fit.js +0 -0
- /package/dist/{plots/geo → layout/coordinates}/rotate.svelte.d.ts +0 -0
- /package/dist/{plots/geo → layout/coordinates}/rotate.svelte.js +0 -0
- /package/dist/{plots/geo → layout/coordinates}/zoom.svelte.js +0 -0
- /package/dist/types/{plots/axis.js → layout/coordinates.js} +0 -0
- /package/dist/types/{plots/delta.js → layout/geometry.js} +0 -0
- /package/dist/types/{plots/scales/geo.js → layout/scales.js} +0 -0
- /package/dist/types/{plots/styles/common.js → layout/styles.js} +0 -0
- /package/dist/types/{plots/styles/geo.js → markers/all.js} +0 -0
|
@@ -1,31 +1,14 @@
|
|
|
1
1
|
<script lang="ts" generics="TData extends Record<string, unknown>">
|
|
2
|
-
import { getContext } from 'svelte';
|
|
3
|
-
import { get } from 'svelte/store';
|
|
4
2
|
import { SvelteMap } from 'svelte/reactivity';
|
|
5
|
-
import type { Writable } from 'svelte/store';
|
|
6
3
|
import { Text, usePlot } from 'svelteplot';
|
|
7
4
|
import { resolveAccessor } from '../utils/accessors';
|
|
8
5
|
import { getConfiguration } from '../../configuration/config.svelte';
|
|
9
6
|
import { paletteColor } from '../../utils/color';
|
|
10
7
|
import { declutter1D, laneAssignment } from '../utils/declutter';
|
|
11
8
|
import type { Series } from '../../types/plots/data/common';
|
|
12
|
-
import type { ValuesStyle } from '../../types/
|
|
9
|
+
import type { ValuesStyle } from '../../types/layout/styles';
|
|
13
10
|
import type { ValueAnchor } from '../../types/plots/constants';
|
|
14
11
|
|
|
15
|
-
// svelteplot's own type for this context isn't part of its public export
|
|
16
|
-
// surface, but the context itself (keyed by this fixed string, set by its
|
|
17
|
-
// `<Plot>`) is — every axis mark already reads/writes it this way to
|
|
18
|
-
// auto-size margins around what it actually renders. Mirroring that here
|
|
19
|
-
// is what lets `margins.right: 'auto'` (the default) grow to fit these
|
|
20
|
-
// labels on its own, instead of the caller having to guess a fixed
|
|
21
|
-
// `margins.right` up front.
|
|
22
|
-
type AutoMarginStores = {
|
|
23
|
-
autoMarginTop: Writable<Map<string, number>>;
|
|
24
|
-
autoMarginLeft: Writable<Map<string, number>>;
|
|
25
|
-
autoMarginRight: Writable<Map<string, number>>;
|
|
26
|
-
autoMarginBottom: Writable<Map<string, number>>;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
12
|
/**
|
|
30
13
|
* End-point value labels for `LinePlot` — one label per series at its last
|
|
31
14
|
* data point, with automatic collision resolution whenever several series
|
|
@@ -37,9 +20,12 @@
|
|
|
37
20
|
let {
|
|
38
21
|
lastPoints,
|
|
39
22
|
values,
|
|
23
|
+
onOverflow,
|
|
40
24
|
}: {
|
|
41
25
|
lastPoints: { series: Series<TData>; lastRow: TData; seriesIndex: number }[];
|
|
42
26
|
values: ValuesStyle | undefined;
|
|
27
|
+
/** Reports how far the rendered labels overflow the plot's own content box, in pixels, on each side (0 = no overflow). */
|
|
28
|
+
onOverflow?: (overflow: { left: number; right: number }) => void;
|
|
43
29
|
} = $props();
|
|
44
30
|
|
|
45
31
|
const cfg = $derived(getConfiguration());
|
|
@@ -76,54 +62,43 @@
|
|
|
76
62
|
valAnchor === 'start' ? ('start' as const) : valAnchor === 'end' ? ('end' as const) : ('middle' as const),
|
|
77
63
|
);
|
|
78
64
|
const defaultLA = $derived(valAnchor === 'outside' ? ('bottom' as const) : ('middle' as const));
|
|
65
|
+
const fontSize = $derived(typeof font?.fontSize === 'number' ? font.fontSize : 12);
|
|
79
66
|
|
|
80
67
|
const plot = usePlot();
|
|
81
68
|
|
|
82
|
-
// ──
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
69
|
+
// ── Overflow measurement ─────────────────────────────────────────────────
|
|
70
|
+
// Measures how far the rendered labels extend past the plot's own content
|
|
71
|
+
// box and reports it to the caller (`Plot.svelte`), which grows its own
|
|
72
|
+
// local margin state to fit — real `getBBox()` measurement, same as the
|
|
73
|
+
// approach this used to register directly into svelteplot's *shared*
|
|
74
|
+
// margin-auto-sizing context. Reporting through a plain callback instead
|
|
75
|
+
// keeps this entirely local to one `<Plot>` instance: nothing here is
|
|
76
|
+
// shared across instances, which is what made the old approach spike
|
|
77
|
+
// CPU/memory into unresponsiveness when many instances mounted/unmounted
|
|
78
|
+
// together (e.g. a multi-story docs page → single story).
|
|
90
79
|
let groupEl: SVGGElement | undefined = $state();
|
|
91
80
|
|
|
92
81
|
$effect(() => {
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
// this the effect would only ever measure once.
|
|
82
|
+
// getBBox() below is a plain DOM read, not itself tracked — depend on
|
|
83
|
+
// whatever actually changes the rendered label layout so this re-runs.
|
|
96
84
|
void lastPoints;
|
|
97
85
|
void values;
|
|
98
86
|
const el = groupEl;
|
|
99
|
-
if (!
|
|
87
|
+
if (!el || !onOverflow) return;
|
|
100
88
|
const xRange = plot.scales.x?.fn?.range?.()?.map(Number);
|
|
101
89
|
if (!xRange) return;
|
|
102
90
|
const plotLeft = Math.min(xRange[0], xRange[1]);
|
|
103
91
|
const plotRight = Math.max(xRange[0], xRange[1]);
|
|
104
|
-
// getBBox() is in the <svg>'s own user-space coordinates — the same
|
|
105
|
-
// pixel space `plot.scales.*.fn()` already produces everywhere else in
|
|
106
|
-
// this file, so no viewport/DOM conversion is needed here.
|
|
107
92
|
let box: { x: number; width: number };
|
|
108
93
|
try {
|
|
109
94
|
box = el.getBBox();
|
|
110
95
|
} catch {
|
|
111
96
|
return;
|
|
112
97
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (rightMargins.get(marginId) !== overflowRight) rightMargins.set(marginId, overflowRight);
|
|
118
|
-
if (leftMargins.get(marginId) !== overflowLeft) leftMargins.set(marginId, overflowLeft);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
$effect(() => {
|
|
122
|
-
return () => {
|
|
123
|
-
if (!autoMargins) return;
|
|
124
|
-
get(autoMargins.autoMarginRight).delete(marginId);
|
|
125
|
-
get(autoMargins.autoMarginLeft).delete(marginId);
|
|
126
|
-
};
|
|
98
|
+
onOverflow({
|
|
99
|
+
left: Math.ceil(Math.max(0, plotLeft - box.x)),
|
|
100
|
+
right: Math.ceil(Math.max(0, box.x + box.width - plotRight)),
|
|
101
|
+
});
|
|
127
102
|
});
|
|
128
103
|
|
|
129
104
|
/**
|
|
@@ -151,7 +126,6 @@
|
|
|
151
126
|
if (lastPoints.length < 2) return map;
|
|
152
127
|
const yScale = plot.scales.y?.fn;
|
|
153
128
|
if (!yScale) return map;
|
|
154
|
-
const fontSize = font?.fontSize ?? 12;
|
|
155
129
|
const minGap = Math.max(16, fontSize * 1.4);
|
|
156
130
|
const items = lastPoints.map(({ series: s, lastRow }) => ({
|
|
157
131
|
name: s.name,
|
|
@@ -192,6 +166,9 @@
|
|
|
192
166
|
{@const labelDx = offset?.inGroup
|
|
193
167
|
? dir * (elbowGap + (numLanes - 1) * laneStep + labelGap)
|
|
194
168
|
: baseDx}
|
|
169
|
+
{@const text = fmtVal(Number(yFn(lastRow)), s.name)}
|
|
170
|
+
{@const preferredTA = resolvedTextAnchor ?? defaultTA}
|
|
171
|
+
{@const preferredLA = font?.lineAnchor ?? defaultLA}
|
|
195
172
|
{#if offset?.inGroup && plot.scales.x?.fn && plot.scales.y?.fn}
|
|
196
173
|
{@const px = Number(plot.scales.x.fn(xFn(lastRow)))}
|
|
197
174
|
{@const py = Number(plot.scales.y.fn(Number(yFn(lastRow))))}
|
|
@@ -212,17 +189,17 @@
|
|
|
212
189
|
data={[lastRow]}
|
|
213
190
|
x={xFn}
|
|
214
191
|
y={yFn}
|
|
215
|
-
text={() =>
|
|
192
|
+
text={() => text}
|
|
216
193
|
dx={labelDx}
|
|
217
194
|
dy={(font?.dy ?? defaultDy) + extraDy}
|
|
218
|
-
textAnchor={
|
|
219
|
-
lineAnchor={
|
|
195
|
+
textAnchor={preferredTA}
|
|
196
|
+
lineAnchor={preferredLA}
|
|
220
197
|
lineHeight={font?.lineHeight}
|
|
221
198
|
rotate={font?.rotate}
|
|
222
199
|
class={font?.class}
|
|
223
200
|
textClass={font?.textClass}
|
|
224
201
|
fill={font?.fill ?? seriesColor}
|
|
225
|
-
|
|
202
|
+
{fontSize}
|
|
226
203
|
fontWeight={font?.fontWeight}
|
|
227
204
|
fontStyle={font?.fontStyle}
|
|
228
205
|
stroke={font?.stroke}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Series } from '../../types/plots/data/common';
|
|
2
|
-
import type { ValuesStyle } from '../../types/
|
|
2
|
+
import type { ValuesStyle } from '../../types/layout/styles';
|
|
3
3
|
declare function $$render<TData extends Record<string, unknown>>(): {
|
|
4
4
|
props: {
|
|
5
5
|
lastPoints: {
|
|
@@ -8,6 +8,11 @@ declare function $$render<TData extends Record<string, unknown>>(): {
|
|
|
8
8
|
seriesIndex: number;
|
|
9
9
|
}[];
|
|
10
10
|
values: ValuesStyle | undefined;
|
|
11
|
+
/** Reports how far the rendered labels overflow the plot's own content box, in pixels, on each side (0 = no overflow). */
|
|
12
|
+
onOverflow?: (overflow: {
|
|
13
|
+
left: number;
|
|
14
|
+
right: number;
|
|
15
|
+
}) => void;
|
|
11
16
|
};
|
|
12
17
|
exports: {};
|
|
13
18
|
bindings: "";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Series } from '../../types/plots/data/common';
|
|
2
|
+
import type { LineSegmentStyle } from '../../types/plots/segments/common';
|
|
3
|
+
import type { VisualGroup } from '../../types/plots/segments/config';
|
|
4
|
+
import type { StrokeStyle, DotStyle } from '../../types/plots/styling';
|
|
5
|
+
import type { LineMarkerConfig, DotMarkerConfig } from '../../types/markers/common';
|
|
6
|
+
/**
|
|
7
|
+
* Every `'line'` (+ `'dot'`, where a segment's style asks for them) marker
|
|
8
|
+
* for one LinePlot render: one `'line'` marker per visual segment, plus a
|
|
9
|
+
* `'dot'` marker right after it when that segment sets `style.dots`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildLineMarkers<TData extends Record<string, unknown>>(args: {
|
|
12
|
+
groupedSeries: VisualGroup<Series<TData>, LineSegmentStyle>[];
|
|
13
|
+
disabledStrokeFor: (seriesName: string) => StrokeStyle | undefined;
|
|
14
|
+
disabledDotsFor: (seriesName: string) => DotStyle | undefined;
|
|
15
|
+
}): (LineMarkerConfig | DotMarkerConfig)[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { resolveAccessor } from '../utils/accessors';
|
|
2
|
+
import { paletteColor } from '../../utils/color';
|
|
3
|
+
import { getConfiguration } from '../../configuration/config.svelte';
|
|
4
|
+
/**
|
|
5
|
+
* Every `'line'` (+ `'dot'`, where a segment's style asks for them) marker
|
|
6
|
+
* for one LinePlot render: one `'line'` marker per visual segment, plus a
|
|
7
|
+
* `'dot'` marker right after it when that segment sets `style.dots`.
|
|
8
|
+
*/
|
|
9
|
+
export function buildLineMarkers(args) {
|
|
10
|
+
const { groupedSeries, disabledStrokeFor, disabledDotsFor } = args;
|
|
11
|
+
const cfg = getConfiguration();
|
|
12
|
+
const markers = [];
|
|
13
|
+
groupedSeries.forEach((group, seriesIndex) => {
|
|
14
|
+
const defaultColor = paletteColor(seriesIndex, cfg.palette);
|
|
15
|
+
const disabledStroke = disabledStrokeFor(group.series.name);
|
|
16
|
+
const disabledDots = disabledDotsFor(group.series.name);
|
|
17
|
+
const xFn = resolveAccessor(group.series.x);
|
|
18
|
+
const yFn = resolveAccessor(group.series.y);
|
|
19
|
+
for (const seg of group.visualSegments) {
|
|
20
|
+
const strokeStyle = seg.style.stroke;
|
|
21
|
+
const dotStyle = seg.style.dots;
|
|
22
|
+
markers.push({
|
|
23
|
+
type: 'line',
|
|
24
|
+
data: seg.data,
|
|
25
|
+
x: xFn,
|
|
26
|
+
y: yFn,
|
|
27
|
+
style: {
|
|
28
|
+
stroke: disabledStroke?.stroke ?? strokeStyle?.stroke ?? defaultColor,
|
|
29
|
+
strokeWidth: disabledStroke?.strokeWidth ?? strokeStyle?.strokeWidth ?? cfg.line.strokeWidth,
|
|
30
|
+
strokeOpacity: disabledStroke?.strokeOpacity ?? strokeStyle?.strokeOpacity ?? 1,
|
|
31
|
+
strokeDasharray: disabledStroke?.strokeDasharray ?? strokeStyle?.strokeDasharray,
|
|
32
|
+
strokeLinejoin: disabledStroke?.strokeLinejoin ?? strokeStyle?.strokeLinejoin,
|
|
33
|
+
strokeLinecap: disabledStroke?.strokeLinecap ?? strokeStyle?.strokeLinecap,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
if (!dotStyle)
|
|
37
|
+
continue;
|
|
38
|
+
markers.push({
|
|
39
|
+
type: 'dot',
|
|
40
|
+
data: seg.data,
|
|
41
|
+
x: xFn,
|
|
42
|
+
y: yFn,
|
|
43
|
+
style: {
|
|
44
|
+
dotRadius: disabledDots?.dotRadius ?? dotStyle.dotRadius ?? cfg.line.dotRadius,
|
|
45
|
+
dotFill: disabledDots?.dotFill ?? dotStyle.dotFill ?? strokeStyle?.stroke ?? defaultColor,
|
|
46
|
+
dotSymbol: disabledDots?.dotSymbol ?? dotStyle.dotSymbol ?? 'circle',
|
|
47
|
+
dotStroke: disabledDots?.dotStroke ?? dotStyle.dotStroke ?? strokeStyle?.stroke ?? defaultColor,
|
|
48
|
+
dotStrokeWidth: disabledDots?.dotStrokeWidth ?? dotStyle.dotStrokeWidth ?? 1,
|
|
49
|
+
dotFillOpacity: disabledDots?.dotFillOpacity ?? dotStyle.dotFillOpacity ?? strokeStyle?.strokeOpacity ?? 1,
|
|
50
|
+
dotStrokeOpacity: disabledDots?.dotStrokeOpacity ?? dotStyle.dotStrokeOpacity ?? strokeStyle?.strokeOpacity ?? 1,
|
|
51
|
+
dotStrokeDasharray: disabledDots?.dotStrokeDasharray ?? dotStyle.dotStrokeDasharray,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return markers;
|
|
57
|
+
}
|
|
@@ -1,28 +1,22 @@
|
|
|
1
1
|
<script lang="ts" generics="TData extends Record<string, unknown>">
|
|
2
|
-
import { SvelteMap } from 'svelte/reactivity';
|
|
3
|
-
import { BarX } from 'svelteplot';
|
|
4
2
|
import { resolveAccessor } from '../utils/accessors';
|
|
5
3
|
import { getConfiguration } from '../../configuration/config.svelte';
|
|
6
4
|
import { paletteColor } from '../../utils/color';
|
|
7
5
|
import { buildSeries } from '../../utils/grouping';
|
|
8
6
|
import { getLegendInteraction } from '../../layout/legend/interaction.svelte';
|
|
9
7
|
import { disabledFillOverride } from '../utils/legendDisabled';
|
|
10
|
-
import {
|
|
8
|
+
import { buildPyramidBarMarkers } from './buildPyramidBarMarkers';
|
|
9
|
+
import { buildGroupedSeries, validateSegments, buildScopedMarkerGroups } from '../utils/segments';
|
|
11
10
|
import BasePlotLayout from '../../layout/plot/BasePlotLayout.svelte';
|
|
12
|
-
import AxisLayout from '../../layout/plot/AxisLayout.svelte';
|
|
13
|
-
import GridLayout from '../../layout/plot/GridLayout.svelte';
|
|
14
|
-
import RuleLayout from '../../layout/plot/RuleLayout.svelte';
|
|
15
|
-
import HoverMarker from '../../markers/HoverMarker.svelte';
|
|
16
11
|
import ValueLabels from './ValueLabels.svelte';
|
|
17
12
|
import { hasHoverMarker, resolveHoverStrategy, resolveHoverSync } from '../../layout/tooltip/utils';
|
|
18
13
|
import type { BasePlotProps } from '../../types/plots/props';
|
|
19
|
-
import type { AxisValue } from '../../types/
|
|
14
|
+
import type { AxisValue, AxisBasedScalesConfig } from '../../types/layout/scales';
|
|
20
15
|
import type { Series, SeriesProps, DataProps } from '../../types/plots/data/common';
|
|
21
|
-
import type {
|
|
22
|
-
import type { BasePlotStyles } from '../../types/plots/styles/common';
|
|
16
|
+
import type { BasePlotStylesConfig } from '../../types/layout/styles';
|
|
23
17
|
import type { ValueAnchor } from '../../types/plots/constants';
|
|
24
18
|
import type { BarSegmentStyle } from '../../types/plots/segments/common';
|
|
25
|
-
import type {
|
|
19
|
+
import type { AxisBasedMarkersConfig } from '../../types/markers/common';
|
|
26
20
|
|
|
27
21
|
/**
|
|
28
22
|
* A population-pyramid-style diverging bar chart: each series is mirrored
|
|
@@ -43,9 +37,9 @@
|
|
|
43
37
|
type Props = BasePlotProps<
|
|
44
38
|
SeriesProps<TData> | DataProps<TData>,
|
|
45
39
|
TData,
|
|
46
|
-
|
|
40
|
+
BasePlotStylesConfig,
|
|
47
41
|
BarSegmentStyle,
|
|
48
|
-
|
|
42
|
+
AxisBasedMarkersConfig,
|
|
49
43
|
AxisBasedScalesConfig<TData>
|
|
50
44
|
>;
|
|
51
45
|
|
|
@@ -166,17 +160,6 @@
|
|
|
166
160
|
return vals.length ? Math.max(...vals) : 1;
|
|
167
161
|
});
|
|
168
162
|
|
|
169
|
-
const seriesMarkers = $derived.by(() => {
|
|
170
|
-
const map = new SvelteMap<string, Marker[]>();
|
|
171
|
-
for (const marker of markers) {
|
|
172
|
-
if ((marker.type === 'component' || marker.type === 'hover') && marker.series) {
|
|
173
|
-
if (!map.has(marker.series)) map.set(marker.series, []);
|
|
174
|
-
map.get(marker.series)!.push(marker);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return map;
|
|
178
|
-
});
|
|
179
|
-
|
|
180
163
|
// The single, fully-resolved scales object BasePlotLayout reads for
|
|
181
164
|
// everything (ticks, grid, sort, and the real svelteplot scale) — avoids
|
|
182
165
|
// keeping a second, separately-defaulted copy of `scales.x`/`scales.y`
|
|
@@ -192,111 +175,52 @@
|
|
|
192
175
|
y: { type: 'band', padding: 0.2, domain: yDomain, ...(scales.y ?? {}) },
|
|
193
176
|
});
|
|
194
177
|
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
178
|
+
const hover = $derived({
|
|
179
|
+
active: tooltip != null || hasHoverMarker(markers),
|
|
180
|
+
points: hoverPoints,
|
|
181
|
+
label: labelFor,
|
|
182
|
+
strategy: resolveHoverStrategy(tooltip?.strategy, markers, 'y'),
|
|
183
|
+
sync: resolveHoverSync(tooltip?.sync, markers),
|
|
184
|
+
tooltip,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const pyramidMarkers = $derived(
|
|
188
|
+
buildPyramidBarMarkers({
|
|
189
|
+
groupedSeries,
|
|
190
|
+
valueFor: signedValue,
|
|
191
|
+
colorFor: (s) => (s.side === 'left' ? leftColor : rightColor),
|
|
192
|
+
disabledFillFor: (name) => disabledFillOverride(legendInteraction?.disabledSeries.get(name)),
|
|
193
|
+
}),
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const scopedGroups = $derived(
|
|
197
|
+
buildScopedMarkerGroups<TData, InternalSeries, BarSegmentStyle>({
|
|
198
|
+
groupedSeries,
|
|
199
|
+
segments,
|
|
200
|
+
colorFor: (series) => (series.side === 'left' ? leftColor : rightColor),
|
|
201
|
+
segmentColorFor: (style, defaultColor) => style?.fill ?? defaultColor,
|
|
202
|
+
}),
|
|
203
|
+
);
|
|
198
204
|
</script>
|
|
199
205
|
|
|
200
206
|
<BasePlotLayout
|
|
201
207
|
{width}
|
|
202
208
|
{height}
|
|
209
|
+
{styles}
|
|
203
210
|
data={flat}
|
|
204
211
|
{getX}
|
|
205
212
|
{getY}
|
|
206
|
-
{
|
|
207
|
-
label={labelFor}
|
|
208
|
-
{tooltip}
|
|
209
|
-
{hoverActive}
|
|
210
|
-
{hoverStrategy}
|
|
211
|
-
{hoverSync}
|
|
213
|
+
{hover}
|
|
212
214
|
scales={resolvedScales}
|
|
213
215
|
{margins}
|
|
214
216
|
xTickRotate={resolvedScales?.x?.tickRotate}
|
|
217
|
+
markers={[...pyramidMarkers, ...markers]}
|
|
218
|
+
seriesData={resolvedSeries}
|
|
219
|
+
{scopedGroups}
|
|
215
220
|
>
|
|
216
|
-
{#
|
|
217
|
-
<AxisLayout data={flat} {getX} {getY} {width} {height} {numericMargins} scales={resolvedScales} />
|
|
218
|
-
<GridLayout scales={resolvedScales} />
|
|
219
|
-
<RuleLayout {markers} seriesData={resolvedSeries} {matchedPoints} {ruleX} {ruleY} {impliesRuleX} {impliesRuleY}>
|
|
221
|
+
{#if showVals}
|
|
220
222
|
{#each groupedSeries as group (group.series.name)}
|
|
221
|
-
{
|
|
222
|
-
{@const getCategory = resolveAccessor(group.series.x)}
|
|
223
|
-
{@const disabledFill = disabledFillOverride(legendInteraction?.disabledSeries.get(group.series.name))}
|
|
224
|
-
{#each group.visualSegments as seg, i (`${group.series.name}-${i}`)}
|
|
225
|
-
<BarX
|
|
226
|
-
data={seg.data}
|
|
227
|
-
x1={0}
|
|
228
|
-
x2={(d: TData) => signedValue(group.series, d)}
|
|
229
|
-
y={getCategory}
|
|
230
|
-
fill={disabledFill?.fill ?? seg.style.fill ?? defaultColor}
|
|
231
|
-
fillOpacity={disabledFill?.fillOpacity ?? seg.style.fillOpacity ?? 1}
|
|
232
|
-
/>
|
|
233
|
-
{/each}
|
|
234
|
-
|
|
235
|
-
{#if showVals}
|
|
236
|
-
<ValueLabels {group} {valAnchor} {fmtVal} values={styles.values} axisColor={cfg.axis.color} />
|
|
237
|
-
{/if}
|
|
238
|
-
|
|
239
|
-
{#each seriesMarkers.get(group.series.name) ?? [] as marker, i (`series-${group.series.name}-${marker.type}-${i}`)}
|
|
240
|
-
{#if marker.type === 'component'}
|
|
241
|
-
{#if marker.scope === 'segment'}
|
|
242
|
-
{@const segs = resolveSegmentsForSeries(segments, group.series.name)}
|
|
243
|
-
{@const segmentDataArrays = getDataForSegments(group.series.data, getCategory, segs)}
|
|
244
|
-
{#each segmentDataArrays as segmentData, segIdx (`${group.series.name}-marker-segment-${segIdx}`)}
|
|
245
|
-
{#if segmentData.length > 0}
|
|
246
|
-
{@const segmentColor = segs[segIdx]?.style?.fill ?? defaultColor}
|
|
247
|
-
<marker.component
|
|
248
|
-
{...marker.props}
|
|
249
|
-
series={group.series.name}
|
|
250
|
-
data={segmentData}
|
|
251
|
-
seriesColor={segmentColor}
|
|
252
|
-
/>
|
|
253
|
-
{/if}
|
|
254
|
-
{/each}
|
|
255
|
-
{:else}
|
|
256
|
-
{@const segs = resolveSegmentsForSeries(segments, group.series.name)}
|
|
257
|
-
{@const seriesColor = segs[0]?.style?.fill ?? defaultColor}
|
|
258
|
-
<marker.component
|
|
259
|
-
{...marker.props}
|
|
260
|
-
series={group.series.name}
|
|
261
|
-
data={group.series.data}
|
|
262
|
-
seriesColor={seriesColor}
|
|
263
|
-
/>
|
|
264
|
-
{/if}
|
|
265
|
-
{:else if marker.type === 'hover'}
|
|
266
|
-
{@const seriesPoints = matchedPoints.filter((p) => p.series === group.series.name)}
|
|
267
|
-
{#if marker.scope === 'segment'}
|
|
268
|
-
{@const segs = resolveSegmentsForSeries(segments, group.series.name)}
|
|
269
|
-
{#each segs as seg, segIdx (`${group.series.name}-hover-segment-${segIdx}`)}
|
|
270
|
-
{@const segPoints = seriesPoints.filter((p) => matchesSegmentX(p.x, seg))}
|
|
271
|
-
{#if segPoints.length > 0}
|
|
272
|
-
<HoverMarker
|
|
273
|
-
data={segPoints}
|
|
274
|
-
ruleX={marker.ruleX !== false && (impliesRuleX || marker.ruleX === true) ? ruleX : null}
|
|
275
|
-
ruleY={marker.ruleY !== false && (impliesRuleY || marker.ruleY === true) ? ruleY : null}
|
|
276
|
-
showLabels={marker.showLabels}
|
|
277
|
-
ruleStyle={marker.ruleStyle}
|
|
278
|
-
dotStyle={marker.dotStyle}
|
|
279
|
-
fontStyle={marker.fontStyle}
|
|
280
|
-
seriesColor={seg.style?.fill ?? defaultColor}
|
|
281
|
-
/>
|
|
282
|
-
{/if}
|
|
283
|
-
{/each}
|
|
284
|
-
{:else}
|
|
285
|
-
{@const segs = resolveSegmentsForSeries(segments, group.series.name)}
|
|
286
|
-
<HoverMarker
|
|
287
|
-
data={seriesPoints}
|
|
288
|
-
ruleX={marker.ruleX !== false && (impliesRuleX || marker.ruleX === true) ? ruleX : null}
|
|
289
|
-
ruleY={marker.ruleY !== false && (impliesRuleY || marker.ruleY === true) ? ruleY : null}
|
|
290
|
-
showLabels={marker.showLabels}
|
|
291
|
-
ruleStyle={marker.ruleStyle}
|
|
292
|
-
dotStyle={marker.dotStyle}
|
|
293
|
-
fontStyle={marker.fontStyle}
|
|
294
|
-
seriesColor={segs[0]?.style?.fill ?? defaultColor}
|
|
295
|
-
/>
|
|
296
|
-
{/if}
|
|
297
|
-
{/if}
|
|
298
|
-
{/each}
|
|
223
|
+
<ValueLabels {group} {valAnchor} {fmtVal} values={styles.values} axisColor={cfg.axis.color} />
|
|
299
224
|
{/each}
|
|
300
|
-
|
|
301
|
-
{/snippet}
|
|
225
|
+
{/if}
|
|
302
226
|
</BasePlotLayout>
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { BasePlotProps } from '../../types/plots/props';
|
|
2
|
+
import type { AxisBasedScalesConfig } from '../../types/layout/scales';
|
|
2
3
|
import type { SeriesProps, DataProps } from '../../types/plots/data/common';
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
import type { BarSegmentStyle } from '../../types/plots/segments/common';
|
|
6
|
-
import type { Marker } from '../../types/markers/common';
|
|
4
|
+
import type { BasePlotStylesConfig } from '../../types/layout/styles';
|
|
5
|
+
import type { AxisBasedMarkersConfig } from '../../types/markers/common';
|
|
7
6
|
declare function $$render<TData extends Record<string, unknown>>(): {
|
|
8
|
-
props: BasePlotProps<SeriesProps<TData> | DataProps<TData>, TData,
|
|
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/
|
|
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/
|
|
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';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Series } from '../../types/plots/data/common';
|
|
2
|
+
import type { BarSegmentStyle } from '../../types/plots/segments/common';
|
|
3
|
+
import type { VisualGroup } from '../../types/plots/segments/config';
|
|
4
|
+
import type { BarMarkerConfig } from '../../types/markers/common';
|
|
5
|
+
/**
|
|
6
|
+
* Every `'bar'` marker for one PyramidPlot render — reuses `BarMarkerConfig`
|
|
7
|
+
* directly, always horizontal (category on y, signed magnitude on x) and
|
|
8
|
+
* with no insets. Emits no `role: 'hitArea'` markers, since PyramidPlot's
|
|
9
|
+
* hover matches nearest points rather than hit-testing category bands.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildPyramidBarMarkers<TData extends Record<string, unknown>, S extends Series<TData>>(args: {
|
|
12
|
+
groupedSeries: VisualGroup<S, BarSegmentStyle>[];
|
|
13
|
+
valueFor: (series: S, d: TData) => number;
|
|
14
|
+
colorFor: (series: S) => string;
|
|
15
|
+
disabledFillFor: (seriesName: string) => {
|
|
16
|
+
fill?: string;
|
|
17
|
+
fillOpacity?: number;
|
|
18
|
+
} | undefined;
|
|
19
|
+
}): BarMarkerConfig[];
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { resolveAccessor } from '../utils/accessors';
|
|
2
|
+
/**
|
|
3
|
+
* Every `'bar'` marker for one PyramidPlot render — reuses `BarMarkerConfig`
|
|
4
|
+
* directly, always horizontal (category on y, signed magnitude on x) and
|
|
5
|
+
* with no insets. Emits no `role: 'hitArea'` markers, since PyramidPlot's
|
|
6
|
+
* hover matches nearest points rather than hit-testing category bands.
|
|
7
|
+
*/
|
|
8
|
+
export function buildPyramidBarMarkers(args) {
|
|
9
|
+
const { groupedSeries, valueFor, colorFor, disabledFillFor } = args;
|
|
10
|
+
const markers = [];
|
|
11
|
+
for (const group of groupedSeries) {
|
|
12
|
+
const getCategory = resolveAccessor(group.series.x);
|
|
13
|
+
const defaultColor = colorFor(group.series);
|
|
14
|
+
const disabledFill = disabledFillFor(group.series.name);
|
|
15
|
+
for (const seg of group.visualSegments) {
|
|
16
|
+
markers.push({
|
|
17
|
+
type: 'bar',
|
|
18
|
+
role: 'segment',
|
|
19
|
+
isHorizontal: true,
|
|
20
|
+
data: seg.data,
|
|
21
|
+
y: getCategory,
|
|
22
|
+
x1: 0,
|
|
23
|
+
x2: (d) => valueFor(group.series, d),
|
|
24
|
+
style: {
|
|
25
|
+
fill: disabledFill?.fill ?? seg.style.fill ?? defaultColor,
|
|
26
|
+
fillOpacity: disabledFill?.fillOpacity ?? seg.style.fillOpacity ?? 1,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return markers;
|
|
32
|
+
}
|