@fundar/data-chart-telling 0.0.25 → 0.0.27
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/BaseChart.svelte +18 -10
- package/dist/charts/BaseChart.svelte.d.ts +4 -2
- package/dist/charts/bar/Chart.svelte +2 -0
- package/dist/charts/heatmap/Chart.svelte +5 -3
- package/dist/charts/heatmap/Chart.svelte.d.ts +1 -2
- package/dist/charts/line/Chart.svelte +2 -0
- package/dist/charts/pyramid/Chart.svelte +5 -3
- 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 +28 -0
- package/dist/configuration/themes/dark.js +4 -0
- package/dist/configuration/themes/index.d.ts +112 -0
- package/dist/index.d.ts +26 -16
- package/dist/index.js +11 -3
- package/dist/layout/coordinates/CoordinatesLayout.svelte +93 -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/facet/FacetIndicator.svelte +31 -0
- package/dist/layout/facet/FacetIndicator.svelte.d.ts +8 -0
- package/dist/layout/facet/FacetLayout.svelte +249 -24
- package/dist/layout/facet/FacetLayout.svelte.d.ts +7 -1
- package/dist/layout/geometry/GeometryLayout.svelte +231 -0
- package/dist/layout/geometry/GeometryLayout.svelte.d.ts +62 -0
- package/dist/layout/legend/ContinuousSection.svelte +1 -1
- package/dist/layout/legend/ContinuousSection.svelte.d.ts +1 -1
- package/dist/layout/legend/DiscreteSection.svelte +1 -1
- package/dist/layout/legend/DiscreteSection.svelte.d.ts +1 -1
- package/dist/layout/legend/LegendLayout.svelte +1 -1
- package/dist/layout/legend/LegendLayout.svelte.d.ts +1 -1
- package/dist/layout/legend/interaction.svelte.d.ts +9 -1
- package/dist/layout/legend/interaction.svelte.js +9 -1
- 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/timeline/Timeline.svelte +1 -1
- package/dist/layout/timeline/Timeline.svelte.d.ts +1 -1
- package/dist/layout/timeline/TimelineLayout.svelte +1 -1
- package/dist/layout/timeline/TimelineLayout.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 +42 -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 +34 -35
- 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 +96 -87
- package/dist/plots/bar/Plot.svelte.d.ts +4 -5
- package/dist/plots/bar/ValueLabels.svelte +70 -24
- package/dist/plots/bar/ValueLabels.svelte.d.ts +11 -2
- 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 +27 -52
- package/dist/plots/heatmap/Plot.svelte.d.ts +4 -5
- package/dist/plots/heatmap/ValueLabels.svelte +5 -5
- package/dist/plots/heatmap/ValueLabels.svelte.d.ts +3 -3
- package/dist/plots/heatmap/buildCellMarkers.d.ts +21 -0
- package/dist/plots/heatmap/buildCellMarkers.js +25 -0
- package/dist/plots/line/Plot.svelte +73 -141
- package/dist/plots/line/Plot.svelte.d.ts +4 -4
- package/dist/plots/line/ValueLabels.svelte +84 -85
- package/dist/plots/line/ValueLabels.svelte.d.ts +10 -1
- package/dist/plots/line/buildLineMarkers.d.ts +15 -0
- package/dist/plots/line/buildLineMarkers.js +57 -0
- package/dist/plots/pyramid/Plot.svelte +80 -121
- package/dist/plots/pyramid/Plot.svelte.d.ts +4 -5
- package/dist/plots/pyramid/ValueLabels.svelte +71 -25
- package/dist/plots/pyramid/ValueLabels.svelte.d.ts +11 -2
- package/dist/plots/pyramid/buildPyramidBarMarkers.d.ts +19 -0
- package/dist/plots/pyramid/buildPyramidBarMarkers.js +32 -0
- package/dist/plots/scatter/Plot.svelte +242 -0
- package/dist/{layout/plot/GridLayout.svelte.d.ts → plots/scatter/Plot.svelte.d.ts} +9 -7
- package/dist/plots/scatter/ValueLabels.svelte +149 -0
- package/dist/plots/{bar/BarSegments.svelte.d.ts → scatter/ValueLabels.svelte.d.ts} +17 -15
- 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/labelOverflow.svelte.d.ts +51 -0
- package/dist/plots/utils/labelOverflow.svelte.js +116 -0
- package/dist/plots/utils/legendDisabled.d.ts +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 +3 -50
- package/dist/types/charts/props.d.ts +8 -8
- package/dist/types/configuration/styling.d.ts +26 -2
- package/dist/types/{plots/scales/geo.d.ts → layout/coordinates.d.ts} +33 -1
- package/dist/types/layout/facet.d.ts +41 -0
- package/dist/types/layout/geometry.d.ts +17 -0
- package/dist/types/layout/legend.d.ts +65 -1
- package/dist/types/{plots/axis.d.ts → layout/scales.d.ts} +11 -0
- package/dist/types/layout/styles.d.ts +115 -0
- package/dist/types/layout/timeline.d.ts +32 -0
- package/dist/types/layout/tooltip.d.ts +20 -1
- package/dist/types/markers/all.d.ts +11 -0
- package/dist/types/markers/all.js +1 -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 +2 -2
- 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/charts/legend.d.ts +0 -60
- 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/{charts/legend.js → layout/coordinates.js} +0 -0
- /package/dist/types/{plots/axis.js → layout/facet.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 → layout/timeline.js} +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Series, Accessor } from '../../types/plots/data/common';
|
|
2
|
+
import type { ScatterSegmentStyle } from '../../types/plots/segments/common';
|
|
3
|
+
import type { VisualGroup } from '../../types/plots/segments/config';
|
|
4
|
+
import type { DotStyle } from '../../types/plots/styling';
|
|
5
|
+
import type { DotMarkerConfig } from '../../types/markers/common';
|
|
6
|
+
/** Every `'dot'` marker for one ScatterPlot render: one per visual segment. */
|
|
7
|
+
export declare function buildScatterMarkers<TData extends Record<string, unknown>>(args: {
|
|
8
|
+
groupedSeries: VisualGroup<Series<TData>, ScatterSegmentStyle>[];
|
|
9
|
+
r?: Accessor<TData, number> | number;
|
|
10
|
+
disabledDotsFor: (seriesName: string) => DotStyle | undefined;
|
|
11
|
+
}): DotMarkerConfig[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { resolveAccessor } from '../utils/accessors';
|
|
2
|
+
import { paletteColor } from '../../utils/color';
|
|
3
|
+
import { getConfiguration } from '../../configuration/config.svelte';
|
|
4
|
+
/** Every `'dot'` marker for one ScatterPlot render: one per visual segment. */
|
|
5
|
+
export function buildScatterMarkers(args) {
|
|
6
|
+
const { groupedSeries, r, disabledDotsFor } = args;
|
|
7
|
+
const cfg = getConfiguration();
|
|
8
|
+
const rFn = r === undefined || typeof r === 'number' ? r : resolveAccessor(r);
|
|
9
|
+
const markers = [];
|
|
10
|
+
groupedSeries.forEach((group, seriesIndex) => {
|
|
11
|
+
const defaultColor = paletteColor(seriesIndex, cfg.palette);
|
|
12
|
+
const disabledDots = disabledDotsFor(group.series.name);
|
|
13
|
+
const xFn = resolveAccessor(group.series.x);
|
|
14
|
+
const yFn = resolveAccessor(group.series.y);
|
|
15
|
+
for (const seg of group.visualSegments) {
|
|
16
|
+
const dotStyle = seg.style;
|
|
17
|
+
markers.push({
|
|
18
|
+
type: 'dot',
|
|
19
|
+
data: seg.data,
|
|
20
|
+
x: xFn,
|
|
21
|
+
y: yFn,
|
|
22
|
+
r: rFn,
|
|
23
|
+
style: {
|
|
24
|
+
dotRadius: disabledDots?.dotRadius ?? dotStyle.dotRadius ?? cfg.scatter.dotRadius,
|
|
25
|
+
dotFill: disabledDots?.dotFill ?? dotStyle.dotFill ?? defaultColor,
|
|
26
|
+
dotSymbol: disabledDots?.dotSymbol ?? dotStyle.dotSymbol,
|
|
27
|
+
dotStroke: disabledDots?.dotStroke ?? dotStyle.dotStroke,
|
|
28
|
+
dotStrokeWidth: disabledDots?.dotStrokeWidth ?? dotStyle.dotStrokeWidth,
|
|
29
|
+
dotFillOpacity: disabledDots?.dotFillOpacity ?? dotStyle.dotFillOpacity,
|
|
30
|
+
dotStrokeOpacity: disabledDots?.dotStrokeOpacity ?? dotStyle.dotStrokeOpacity,
|
|
31
|
+
dotStrokeDasharray: disabledDots?.dotStrokeDasharray ?? dotStyle.dotStrokeDasharray,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return markers;
|
|
37
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extends a `[min, max]` data domain so the largest rendered dot radius
|
|
3
|
+
* doesn't get clipped by the plot's own frame — svelteplot's own `'auto'`
|
|
4
|
+
* margin only measures axis chrome (tick labels), never mark geometry, so a
|
|
5
|
+
* point sitting at the domain edge with a non-trivial radius otherwise
|
|
6
|
+
* overflows past the frame. `innerSizePx` is an estimate (the real pixel
|
|
7
|
+
* range isn't known until the scale itself is built from this domain), so
|
|
8
|
+
* this errs generous rather than exact.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveRadiusPaddedDomain(values: number[], maxRadiusPx: number, innerSizePx: number): [number, number] | undefined;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extends a `[min, max]` data domain so the largest rendered dot radius
|
|
3
|
+
* doesn't get clipped by the plot's own frame — svelteplot's own `'auto'`
|
|
4
|
+
* margin only measures axis chrome (tick labels), never mark geometry, so a
|
|
5
|
+
* point sitting at the domain edge with a non-trivial radius otherwise
|
|
6
|
+
* overflows past the frame. `innerSizePx` is an estimate (the real pixel
|
|
7
|
+
* range isn't known until the scale itself is built from this domain), so
|
|
8
|
+
* this errs generous rather than exact.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveRadiusPaddedDomain(values, maxRadiusPx, innerSizePx) {
|
|
11
|
+
const finite = values.filter((v) => Number.isFinite(v));
|
|
12
|
+
if (finite.length === 0 || innerSizePx <= 0 || maxRadiusPx <= 0)
|
|
13
|
+
return undefined;
|
|
14
|
+
const min = Math.min(...finite);
|
|
15
|
+
const max = Math.max(...finite);
|
|
16
|
+
if (min === max) {
|
|
17
|
+
// A degenerate single-value domain has no data-per-pixel ratio to pad by — fall back to a fixed unit either side.
|
|
18
|
+
return [min - 1, max + 1];
|
|
19
|
+
}
|
|
20
|
+
const dataPerPixel = (max - min) / innerSizePx;
|
|
21
|
+
const pad = maxRadiusPx * dataPerPixel;
|
|
22
|
+
return [min - pad, max + pad];
|
|
23
|
+
}
|
|
@@ -45,55 +45,73 @@ export type Declutter1DResult<T> = {
|
|
|
45
45
|
* that side, so a plain 50/50 blend would still ask the up-pass and
|
|
46
46
|
* down-pass to pull the block symmetrically toward *both* edges before
|
|
47
47
|
* `fitRunsWithinBounds` shifts/shrinks it back — the up-pass reaching for
|
|
48
|
-
* room that was never really there
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* shelf pinned against one wall would.
|
|
48
|
+
* room that was never really there, needlessly widening how far members end
|
|
49
|
+
* up travelling from their original positions (`laneAssignment`'s own lane
|
|
50
|
+
* count grows with exactly that travel distance — see its doc comment) — so
|
|
51
|
+
* a run near an edge leans toward whichever pass already grows away from it
|
|
52
|
+
* instead — the block still expands to clear `minGap` everywhere, just
|
|
53
|
+
* mostly in the one direction that was actually available, the way a person
|
|
54
|
+
* sliding books apart on a shelf pinned against one wall would.
|
|
56
55
|
*/
|
|
57
56
|
export declare function declutter1D<T extends {
|
|
58
57
|
pos: number;
|
|
59
58
|
}>(items: T[], minGap: number, bounds?: readonly [number, number]): Declutter1DResult<T>;
|
|
60
59
|
/**
|
|
61
60
|
* Groups `declutter1D`'s own `connected` flags into runs (maximal stretches
|
|
62
|
-
* of consecutive connected pairs), then
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* *travel spans* (`[min(original, final), max(original, final)]`, the pixel
|
|
68
|
-
* range a leader line's vertical segment actually sweeps through) don't
|
|
69
|
-
* overlap. A pair that would collide is split into two lanes of its own
|
|
70
|
-
* instead of one shared one, so correctness never depends on the pairing
|
|
71
|
-
* being right — only on this check.
|
|
61
|
+
* of consecutive connected pairs), then gives every run member its own,
|
|
62
|
+
* unique lane — no two members ever share one, deliberately (see below) —
|
|
63
|
+
* ordered so that a leader line drawn through lane `rank` never crosses, or
|
|
64
|
+
* even draws visually flush against, one drawn through any other lane of
|
|
65
|
+
* the same run.
|
|
72
66
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
67
|
+
* Every member's leader line is the same three-segment elbow: a horizontal
|
|
68
|
+
* stub from the data point out to its lane, a vertical run up/down that
|
|
69
|
+
* lane to the label's height, then a final horizontal run in to the shared
|
|
70
|
+
* label column. Two such elbows, `i` (in the tighter of the two lanes) and
|
|
71
|
+
* `j` (further out), are safe from crossing *specifically* when neither of
|
|
72
|
+
* these can happen:
|
|
73
|
+
* - `j`'s own stub (at `j`'s original height) reaches out past `i`'s lane
|
|
74
|
+
* while `i`'s vertical run passes through that height — `j`'s stub would
|
|
75
|
+
* cut across `i`'s vertical segment.
|
|
76
|
+
* - `i`'s final run (at `i`'s label height) reaches past `i`'s own lane out
|
|
77
|
+
* to the shared column, and in doing so crosses `j`'s lane while `j`'s
|
|
78
|
+
* vertical run passes through *that* height — `i`'s own final segment
|
|
79
|
+
* would cut across `j`'s vertical segment.
|
|
81
80
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
81
|
+
* Whether `i` can safely take the tighter lane depends on both members'
|
|
82
|
+
* *actual* pixel geometry — not simply on which one moved further — so each
|
|
83
|
+
* pair is checked directly per {@link saferNearer} rather than inferred from
|
|
84
|
+
* a single sort key. A pair with a clean, provable answer either way (the
|
|
85
|
+
* overwhelmingly common case for `declutter1D`'s own output) is ordered by
|
|
86
|
+
* that answer; a pair with no clean answer (both directions equally
|
|
87
|
+
* "unsafe," or, symmetrically, both fine) falls back to the same *travel
|
|
88
|
+
* distance* heuristic used before this per-pair check existed — furthest
|
|
89
|
+
* `|final - original|` first — which is what decides ties and keeps the
|
|
90
|
+
* "furthest traveller gets the tightest lane, so it starts bending
|
|
91
|
+
* immediately rather than reaching across everyone else's leader lines
|
|
92
|
+
* first" intent for every pair the geometry itself doesn't already settle.
|
|
93
|
+
*
|
|
94
|
+
* No sharing, ever, is the other half of this: two members can only have
|
|
95
|
+
* non-overlapping travel spans (and so pass the old, now-removed,
|
|
96
|
+
* share-a-lane check) by sitting far apart along the axis — but "far apart"
|
|
97
|
+
* is exactly the case with the most room to spare, so it never needed the
|
|
98
|
+
* lane it would save by sharing, while any pair close enough to actually be
|
|
99
|
+
* short on room has spans close enough together that sharing draws their
|
|
100
|
+
* two leader lines only a couple pixels apart — visually indistinguishable
|
|
101
|
+
* from one continuous line touching the next label down. A run of `n`
|
|
102
|
+
* members uses exactly `n` lanes; letting the lane count grow is the price
|
|
103
|
+
* of a fan that never has two leader lines touching *or* crossing, and
|
|
104
|
+
* `ValueLabels`' own auto-margin measurement (see its doc comment) already
|
|
105
|
+
* grows `margins.right` to fit however many lanes that turns out to be, so
|
|
106
|
+
* nothing has to be guessed up front.
|
|
90
107
|
*
|
|
91
108
|
* `rank` is the lane index (0-indexed) to drive a leader line's routing:
|
|
92
109
|
* `rank` 0 bends right off the data point (a short first stub); higher
|
|
93
110
|
* ranks travel further before bending (right up to the shared label
|
|
94
|
-
* column). `lanesUsed` is the run's own total lane count
|
|
95
|
-
* member
|
|
96
|
-
* to know whether a given member belongs to a
|
|
111
|
+
* column). `lanesUsed` is the run's own total lane count — always equal to
|
|
112
|
+
* `runSize` — shared by every member; `runSize` is the run's member count,
|
|
113
|
+
* for callers that just need to know whether a given member belongs to a
|
|
114
|
+
* real run at all.
|
|
97
115
|
*/
|
|
98
116
|
export declare function laneAssignment(originals: number[], finals: number[], connected: boolean[]): {
|
|
99
117
|
rank: number;
|
|
@@ -34,14 +34,13 @@
|
|
|
34
34
|
* that side, so a plain 50/50 blend would still ask the up-pass and
|
|
35
35
|
* down-pass to pull the block symmetrically toward *both* edges before
|
|
36
36
|
* `fitRunsWithinBounds` shifts/shrinks it back — the up-pass reaching for
|
|
37
|
-
* room that was never really there
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* shelf pinned against one wall would.
|
|
37
|
+
* room that was never really there, needlessly widening how far members end
|
|
38
|
+
* up travelling from their original positions (`laneAssignment`'s own lane
|
|
39
|
+
* count grows with exactly that travel distance — see its doc comment) — so
|
|
40
|
+
* a run near an edge leans toward whichever pass already grows away from it
|
|
41
|
+
* instead — the block still expands to clear `minGap` everywhere, just
|
|
42
|
+
* mostly in the one direction that was actually available, the way a person
|
|
43
|
+
* sliding books apart on a shelf pinned against one wall would.
|
|
45
44
|
*/
|
|
46
45
|
export function declutter1D(items, minGap, bounds) {
|
|
47
46
|
const sorted = [...items].sort((a, b) => a.pos - b.pos);
|
|
@@ -137,41 +136,60 @@ function fitRunsWithinBounds(positions, connected, [lo, hi]) {
|
|
|
137
136
|
}
|
|
138
137
|
/**
|
|
139
138
|
* Groups `declutter1D`'s own `connected` flags into runs (maximal stretches
|
|
140
|
-
* of consecutive connected pairs), then
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* *travel spans* (`[min(original, final), max(original, final)]`, the pixel
|
|
146
|
-
* range a leader line's vertical segment actually sweeps through) don't
|
|
147
|
-
* overlap. A pair that would collide is split into two lanes of its own
|
|
148
|
-
* instead of one shared one, so correctness never depends on the pairing
|
|
149
|
-
* being right — only on this check.
|
|
139
|
+
* of consecutive connected pairs), then gives every run member its own,
|
|
140
|
+
* unique lane — no two members ever share one, deliberately (see below) —
|
|
141
|
+
* ordered so that a leader line drawn through lane `rank` never crosses, or
|
|
142
|
+
* even draws visually flush against, one drawn through any other lane of
|
|
143
|
+
* the same run.
|
|
150
144
|
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
145
|
+
* Every member's leader line is the same three-segment elbow: a horizontal
|
|
146
|
+
* stub from the data point out to its lane, a vertical run up/down that
|
|
147
|
+
* lane to the label's height, then a final horizontal run in to the shared
|
|
148
|
+
* label column. Two such elbows, `i` (in the tighter of the two lanes) and
|
|
149
|
+
* `j` (further out), are safe from crossing *specifically* when neither of
|
|
150
|
+
* these can happen:
|
|
151
|
+
* - `j`'s own stub (at `j`'s original height) reaches out past `i`'s lane
|
|
152
|
+
* while `i`'s vertical run passes through that height — `j`'s stub would
|
|
153
|
+
* cut across `i`'s vertical segment.
|
|
154
|
+
* - `i`'s final run (at `i`'s label height) reaches past `i`'s own lane out
|
|
155
|
+
* to the shared column, and in doing so crosses `j`'s lane while `j`'s
|
|
156
|
+
* vertical run passes through *that* height — `i`'s own final segment
|
|
157
|
+
* would cut across `j`'s vertical segment.
|
|
159
158
|
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
159
|
+
* Whether `i` can safely take the tighter lane depends on both members'
|
|
160
|
+
* *actual* pixel geometry — not simply on which one moved further — so each
|
|
161
|
+
* pair is checked directly per {@link saferNearer} rather than inferred from
|
|
162
|
+
* a single sort key. A pair with a clean, provable answer either way (the
|
|
163
|
+
* overwhelmingly common case for `declutter1D`'s own output) is ordered by
|
|
164
|
+
* that answer; a pair with no clean answer (both directions equally
|
|
165
|
+
* "unsafe," or, symmetrically, both fine) falls back to the same *travel
|
|
166
|
+
* distance* heuristic used before this per-pair check existed — furthest
|
|
167
|
+
* `|final - original|` first — which is what decides ties and keeps the
|
|
168
|
+
* "furthest traveller gets the tightest lane, so it starts bending
|
|
169
|
+
* immediately rather than reaching across everyone else's leader lines
|
|
170
|
+
* first" intent for every pair the geometry itself doesn't already settle.
|
|
171
|
+
*
|
|
172
|
+
* No sharing, ever, is the other half of this: two members can only have
|
|
173
|
+
* non-overlapping travel spans (and so pass the old, now-removed,
|
|
174
|
+
* share-a-lane check) by sitting far apart along the axis — but "far apart"
|
|
175
|
+
* is exactly the case with the most room to spare, so it never needed the
|
|
176
|
+
* lane it would save by sharing, while any pair close enough to actually be
|
|
177
|
+
* short on room has spans close enough together that sharing draws their
|
|
178
|
+
* two leader lines only a couple pixels apart — visually indistinguishable
|
|
179
|
+
* from one continuous line touching the next label down. A run of `n`
|
|
180
|
+
* members uses exactly `n` lanes; letting the lane count grow is the price
|
|
181
|
+
* of a fan that never has two leader lines touching *or* crossing, and
|
|
182
|
+
* `ValueLabels`' own auto-margin measurement (see its doc comment) already
|
|
183
|
+
* grows `margins.right` to fit however many lanes that turns out to be, so
|
|
184
|
+
* nothing has to be guessed up front.
|
|
168
185
|
*
|
|
169
186
|
* `rank` is the lane index (0-indexed) to drive a leader line's routing:
|
|
170
187
|
* `rank` 0 bends right off the data point (a short first stub); higher
|
|
171
188
|
* ranks travel further before bending (right up to the shared label
|
|
172
|
-
* column). `lanesUsed` is the run's own total lane count
|
|
173
|
-
* member
|
|
174
|
-
* to know whether a given member belongs to a
|
|
189
|
+
* column). `lanesUsed` is the run's own total lane count — always equal to
|
|
190
|
+
* `runSize` — shared by every member; `runSize` is the run's member count,
|
|
191
|
+
* for callers that just need to know whether a given member belongs to a
|
|
192
|
+
* real run at all.
|
|
175
193
|
*/
|
|
176
194
|
export function laneAssignment(originals, finals, connected) {
|
|
177
195
|
const n = connected.length + 1;
|
|
@@ -181,7 +199,15 @@ export function laneAssignment(originals, finals, connected) {
|
|
|
181
199
|
const b = finals[k];
|
|
182
200
|
return a < b ? [a, b] : [b, a];
|
|
183
201
|
};
|
|
184
|
-
const
|
|
202
|
+
const distance = (k) => Math.abs(finals[k] - originals[k]);
|
|
203
|
+
/** Whether lane-`i`-nearer-than-`j` is provably crossing-free — see this function's doc comment. */
|
|
204
|
+
function saferNearer(i, j) {
|
|
205
|
+
const [loI, hiI] = span(i);
|
|
206
|
+
const [loJ, hiJ] = span(j);
|
|
207
|
+
const jStubCutsI = originals[j] >= loI && originals[j] <= hiI;
|
|
208
|
+
const iFinalCutsJ = finals[i] >= loJ && finals[i] <= hiJ;
|
|
209
|
+
return !jStubCutsI && !iFinalCutsJ;
|
|
210
|
+
}
|
|
185
211
|
let i = 0;
|
|
186
212
|
while (i < n) {
|
|
187
213
|
let j = i;
|
|
@@ -189,32 +215,21 @@ export function laneAssignment(originals, finals, connected) {
|
|
|
189
215
|
j++;
|
|
190
216
|
const runSize = j - i + 1;
|
|
191
217
|
if (runSize > 1) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
rankOf[lo] = nextLane;
|
|
208
|
-
rankOf[hi] = nextLane + 1;
|
|
209
|
-
nextLane += 2;
|
|
210
|
-
}
|
|
211
|
-
lo += 1;
|
|
212
|
-
hi -= 1;
|
|
213
|
-
}
|
|
214
|
-
const lanesUsed = nextLane;
|
|
215
|
-
for (let k = 0; k < runSize; k++) {
|
|
216
|
-
out[i + k] = { rank: rankOf[k], lanesUsed, runSize };
|
|
217
|
-
}
|
|
218
|
+
// Members of this run, ordered so each is provably safe to place
|
|
219
|
+
// nearer than the next whenever that's decidable, furthest-travelled
|
|
220
|
+
// first otherwise (ties keep their original — i.e. top-to-bottom —
|
|
221
|
+
// order, since `sort` is stable). Position in this order *is* the
|
|
222
|
+
// rank: no sharing.
|
|
223
|
+
const order = Array.from({ length: runSize }, (_, k) => i + k).sort((a, b) => {
|
|
224
|
+
const aNearer = saferNearer(a, b);
|
|
225
|
+
const bNearer = saferNearer(b, a);
|
|
226
|
+
if (aNearer !== bNearer)
|
|
227
|
+
return aNearer ? -1 : 1;
|
|
228
|
+
return distance(b) - distance(a);
|
|
229
|
+
});
|
|
230
|
+
order.forEach((k, rank) => {
|
|
231
|
+
out[k] = { rank, lanesUsed: runSize, runSize };
|
|
232
|
+
});
|
|
218
233
|
}
|
|
219
234
|
i = j + 1;
|
|
220
235
|
}
|
|
@@ -36,7 +36,7 @@ export declare function geometryPartCentroids(geometry: GeoJSON.Geometry): [numb
|
|
|
36
36
|
* based on a per-part predicate, reassembling a geometry of the same type
|
|
37
37
|
* from whatever survives — `null` if nothing does. Used to let a single
|
|
38
38
|
* feature's geometry be split between the main map and an `inset` marker
|
|
39
|
-
* (see `GeoDataProps.excludeParts`/`
|
|
39
|
+
* (see `GeoDataProps.excludeParts`/`GeoInsetMarkerConfig.featurePartsFilter`)
|
|
40
40
|
* without preprocessing the source data: e.g. a province whose `MultiPolygon`
|
|
41
41
|
* spans both a mainland part and a far-flung part can have the main map
|
|
42
42
|
* exclude the latter while an inset shows only it.
|
|
@@ -102,7 +102,7 @@ export function geometryPartCentroids(geometry) {
|
|
|
102
102
|
* based on a per-part predicate, reassembling a geometry of the same type
|
|
103
103
|
* from whatever survives — `null` if nothing does. Used to let a single
|
|
104
104
|
* feature's geometry be split between the main map and an `inset` marker
|
|
105
|
-
* (see `GeoDataProps.excludeParts`/`
|
|
105
|
+
* (see `GeoDataProps.excludeParts`/`GeoInsetMarkerConfig.featurePartsFilter`)
|
|
106
106
|
* without preprocessing the source data: e.g. a province whose `MultiPolygon`
|
|
107
107
|
* spans both a mainland part and a far-flung part can have the main map
|
|
108
108
|
* exclude the latter while an inset shows only it.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { MarginConfig } from '../../types/plots/props';
|
|
2
|
+
/** How far a value label's rendered box extends past the plot's own content box, in pixels, on each side (0 = no overflow). */
|
|
3
|
+
export type MarginOverflow = {
|
|
4
|
+
left: number;
|
|
5
|
+
right: number;
|
|
6
|
+
top: number;
|
|
7
|
+
bottom: number;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Watches an SVG group's rendered bounding box against the plot's own
|
|
11
|
+
* content box (`bounds`) and reports how far it overflows on each side.
|
|
12
|
+
* Polled across successive paints until two consecutive readings agree,
|
|
13
|
+
* rather than trusted after a single frame: the group's own resolved
|
|
14
|
+
* geometry (driven by svelteplot's separate reactive graph) and svelteplot's
|
|
15
|
+
* own axis auto-margins can take a variable number of frames to settle —
|
|
16
|
+
* more on a page with many other components competing for layout (e.g. a
|
|
17
|
+
* docs page) than in an isolated story. Measuring too early can catch
|
|
18
|
+
* content still at its pre-layout default position; capped so a layout that
|
|
19
|
+
* genuinely never stabilizes still reports its last reading instead of
|
|
20
|
+
* polling forever. Also waits for web fonts to finish loading, since glyph
|
|
21
|
+
* metrics (and therefore `getBBox()`) can still reflect a fallback font's
|
|
22
|
+
* narrower widths until the real font swaps in.
|
|
23
|
+
*/
|
|
24
|
+
export declare function watchLabelOverflow(args: {
|
|
25
|
+
el: () => SVGGElement | undefined;
|
|
26
|
+
bounds: () => {
|
|
27
|
+
left: number;
|
|
28
|
+
right: number;
|
|
29
|
+
top: number;
|
|
30
|
+
bottom: number;
|
|
31
|
+
} | undefined;
|
|
32
|
+
onOverflow: (overflow: MarginOverflow) => void;
|
|
33
|
+
}): void;
|
|
34
|
+
/**
|
|
35
|
+
* Tracks value-label overflow reported by (possibly several, e.g. one per
|
|
36
|
+
* series) {@link watchLabelOverflow} calls and grows a local margin override
|
|
37
|
+
* to fit it — monotonically, since shrinking it back would re-expose the
|
|
38
|
+
* overflow that caused the growth in the first place, oscillating between
|
|
39
|
+
* "shrink margin → overflow reappears → grow margin → …". Each side is only
|
|
40
|
+
* ever set when the caller's own `margins` prop hasn't already pinned it, so
|
|
41
|
+
* an explicit caller override always wins over this auto-grown one.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createLabelMarginTracker(margins: () => MarginConfig | undefined): {
|
|
44
|
+
report: (overflow: MarginOverflow) => void;
|
|
45
|
+
readonly resolvedMargins: Partial<{
|
|
46
|
+
top: number | "auto";
|
|
47
|
+
right: number | "auto";
|
|
48
|
+
bottom: number | "auto";
|
|
49
|
+
left: number | "auto";
|
|
50
|
+
}> | undefined;
|
|
51
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const NO_OVERFLOW = { left: 0, right: 0, top: 0, bottom: 0 };
|
|
2
|
+
/**
|
|
3
|
+
* Watches an SVG group's rendered bounding box against the plot's own
|
|
4
|
+
* content box (`bounds`) and reports how far it overflows on each side.
|
|
5
|
+
* Polled across successive paints until two consecutive readings agree,
|
|
6
|
+
* rather than trusted after a single frame: the group's own resolved
|
|
7
|
+
* geometry (driven by svelteplot's separate reactive graph) and svelteplot's
|
|
8
|
+
* own axis auto-margins can take a variable number of frames to settle —
|
|
9
|
+
* more on a page with many other components competing for layout (e.g. a
|
|
10
|
+
* docs page) than in an isolated story. Measuring too early can catch
|
|
11
|
+
* content still at its pre-layout default position; capped so a layout that
|
|
12
|
+
* genuinely never stabilizes still reports its last reading instead of
|
|
13
|
+
* polling forever. Also waits for web fonts to finish loading, since glyph
|
|
14
|
+
* metrics (and therefore `getBBox()`) can still reflect a fallback font's
|
|
15
|
+
* narrower widths until the real font swaps in.
|
|
16
|
+
*/
|
|
17
|
+
export function watchLabelOverflow(args) {
|
|
18
|
+
$effect(() => {
|
|
19
|
+
const el = args.el();
|
|
20
|
+
const bounds = args.bounds();
|
|
21
|
+
const reportOverflow = args.onOverflow;
|
|
22
|
+
if (!el || !bounds)
|
|
23
|
+
return;
|
|
24
|
+
const MAX_ATTEMPTS = 30;
|
|
25
|
+
let cancelled = false;
|
|
26
|
+
let frame;
|
|
27
|
+
let attempt = 0;
|
|
28
|
+
let lastReading;
|
|
29
|
+
function measure() {
|
|
30
|
+
if (cancelled)
|
|
31
|
+
return;
|
|
32
|
+
let box;
|
|
33
|
+
try {
|
|
34
|
+
box = el.getBBox();
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const overflow = {
|
|
40
|
+
left: Math.ceil(Math.max(0, bounds.left - box.x)),
|
|
41
|
+
right: Math.ceil(Math.max(0, box.x + box.width - bounds.right)),
|
|
42
|
+
top: Math.ceil(Math.max(0, bounds.top - box.y)),
|
|
43
|
+
bottom: Math.ceil(Math.max(0, box.y + box.height - bounds.bottom)),
|
|
44
|
+
};
|
|
45
|
+
attempt += 1;
|
|
46
|
+
const stable = lastReading != null &&
|
|
47
|
+
lastReading.left === overflow.left &&
|
|
48
|
+
lastReading.right === overflow.right &&
|
|
49
|
+
lastReading.top === overflow.top &&
|
|
50
|
+
lastReading.bottom === overflow.bottom;
|
|
51
|
+
lastReading = overflow;
|
|
52
|
+
if (stable || attempt >= MAX_ATTEMPTS) {
|
|
53
|
+
reportOverflow(overflow);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
frame = requestAnimationFrame(measure);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const fontsReady = document.fonts?.ready ?? Promise.resolve();
|
|
60
|
+
fontsReady.then(() => {
|
|
61
|
+
if (cancelled)
|
|
62
|
+
return;
|
|
63
|
+
frame = requestAnimationFrame(measure);
|
|
64
|
+
});
|
|
65
|
+
return () => {
|
|
66
|
+
cancelled = true;
|
|
67
|
+
if (frame !== undefined)
|
|
68
|
+
cancelAnimationFrame(frame);
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Tracks value-label overflow reported by (possibly several, e.g. one per
|
|
74
|
+
* series) {@link watchLabelOverflow} calls and grows a local margin override
|
|
75
|
+
* to fit it — monotonically, since shrinking it back would re-expose the
|
|
76
|
+
* overflow that caused the growth in the first place, oscillating between
|
|
77
|
+
* "shrink margin → overflow reappears → grow margin → …". Each side is only
|
|
78
|
+
* ever set when the caller's own `margins` prop hasn't already pinned it, so
|
|
79
|
+
* an explicit caller override always wins over this auto-grown one.
|
|
80
|
+
*/
|
|
81
|
+
export function createLabelMarginTracker(margins) {
|
|
82
|
+
let measured = $state(NO_OVERFLOW);
|
|
83
|
+
function report(overflow) {
|
|
84
|
+
const next = {
|
|
85
|
+
left: Math.max(measured.left, overflow.left),
|
|
86
|
+
right: Math.max(measured.right, overflow.right),
|
|
87
|
+
top: Math.max(measured.top, overflow.top),
|
|
88
|
+
bottom: Math.max(measured.bottom, overflow.bottom),
|
|
89
|
+
};
|
|
90
|
+
if (next.left !== measured.left ||
|
|
91
|
+
next.right !== measured.right ||
|
|
92
|
+
next.top !== measured.top ||
|
|
93
|
+
next.bottom !== measured.bottom) {
|
|
94
|
+
measured = next;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const resolvedMargins = $derived.by(() => {
|
|
98
|
+
const base = margins();
|
|
99
|
+
if (measured.left <= 0 && measured.right <= 0 && measured.top <= 0 && measured.bottom <= 0)
|
|
100
|
+
return base;
|
|
101
|
+
const out = { ...base };
|
|
102
|
+
if (measured.right > 0 && base?.right === undefined)
|
|
103
|
+
out.right = measured.right;
|
|
104
|
+
if (measured.left > 0 && base?.left === undefined)
|
|
105
|
+
out.left = measured.left;
|
|
106
|
+
if (measured.top > 0 && base?.top === undefined)
|
|
107
|
+
out.top = measured.top;
|
|
108
|
+
if (measured.bottom > 0 && base?.bottom === undefined)
|
|
109
|
+
out.bottom = measured.bottom;
|
|
110
|
+
return out;
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
report,
|
|
114
|
+
get resolvedMargins() { return resolvedMargins; },
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LegendDisabledStyle } from '../../types/
|
|
1
|
+
import type { LegendDisabledStyle } from '../../types/layout/legend';
|
|
2
2
|
import type { StrokeStyle, DotStyle } from '../../types/plots/styling';
|
|
3
3
|
/**
|
|
4
4
|
* The three per-mark override layers a legend "disabled" state can apply,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { AxisValue } from '../../types/
|
|
1
|
+
import type { AxisValue } from '../../types/layout/scales';
|
|
2
2
|
import type { Series } from '../../types/plots/data/common';
|
|
3
3
|
import type { LineSegmentStyle } from '../../types/plots/segments/common';
|
|
4
4
|
import type { Segment, VisualGroup } from '../../types/plots/segments/config';
|
|
5
5
|
import type { SegmentsConfig } from '../../types/plots/props';
|
|
6
|
+
import type { ScopedMarkerGroup } from '../../types/layout/geometry';
|
|
6
7
|
/**
|
|
7
8
|
* Returns the flat {@link Segment} list for one series.
|
|
8
9
|
* Series-specific segments are added before `'default'` segments so that
|
|
@@ -11,15 +12,6 @@ import type { SegmentsConfig } from '../../types/plots/props';
|
|
|
11
12
|
export declare function resolveSegmentsForSeries<TStyle>(segments: SegmentsConfig<TStyle>, seriesName: string): Segment<TStyle>[];
|
|
12
13
|
/** Returns true when `xVal` falls inside any of a segment's x ranges. */
|
|
13
14
|
export declare function matchesSegmentX<TStyle>(xVal: AxisValue, seg: Segment<TStyle>): boolean;
|
|
14
|
-
/**
|
|
15
|
-
* For each segment in `segs`, returns the subset of `data` rows whose x value
|
|
16
|
-
* (from `xAccessor`) falls inside any of the segment's x ranges. Rows are
|
|
17
|
-
* not deduplicated across segments — a row that matches multiple segments
|
|
18
|
-
* appears in each resulting array.
|
|
19
|
-
*
|
|
20
|
-
* Used by component and hover markers with the `'segment'` strategy.
|
|
21
|
-
*/
|
|
22
|
-
export declare function getDataForSegments<TData, TStyle>(data: TData[], xAccessor: (d: TData) => AxisValue, segs: Segment<TStyle>[]): TData[][];
|
|
23
15
|
/**
|
|
24
16
|
* Warns about obvious configuration mistakes: multiple catch-all segments
|
|
25
17
|
* (no `areas`) in the same series list. Since catch-alls are evaluated last
|
|
@@ -38,3 +30,18 @@ export declare function validateSegments<TStyle>(segments: SegmentsConfig<TStyle
|
|
|
38
30
|
* aren't duplicated as an extra, separately-styled mark.
|
|
39
31
|
*/
|
|
40
32
|
export declare function buildGroupedSeries<S extends Series<any>, TStyle = LineSegmentStyle>(seriesList: S[], segments: SegmentsConfig<TStyle>, connect?: boolean): VisualGroup<S, TStyle>[];
|
|
33
|
+
/**
|
|
34
|
+
* Builds the `ScopedMarkerGroup[]` a `Plot.svelte` hands to `GeometryLayout`
|
|
35
|
+
* for rendering `series`-scoped `hover`/`component` markers — one group per
|
|
36
|
+
* series, carrying its resolved segments' match predicates and colors.
|
|
37
|
+
* `colorFor` resolves a series' own default color; `segmentColorFor` reads
|
|
38
|
+
* whichever field counts as "color" out of that plot kind's segment style
|
|
39
|
+
* shape (e.g. `fill` vs `stroke.stroke`), since that's the only part of this
|
|
40
|
+
* that isn't shared across plot kinds.
|
|
41
|
+
*/
|
|
42
|
+
export declare function buildScopedMarkerGroups<TData extends Record<string, unknown>, S extends Series<any>, TStyle>(args: {
|
|
43
|
+
groupedSeries: VisualGroup<S, TStyle>[];
|
|
44
|
+
segments: SegmentsConfig<TStyle>;
|
|
45
|
+
colorFor: (series: S, seriesIndex: number) => string;
|
|
46
|
+
segmentColorFor: (style: TStyle | undefined, defaultColor: string) => string;
|
|
47
|
+
}): ScopedMarkerGroup<TData>[];
|