@fundar/data-chart-telling 1.17.0 → 1.18.0
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/CHANGELOG.md +24 -0
- package/dist/configuration/config.svelte.js +3 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +2 -0
- package/dist/layout/geometry/GeometryLayout.svelte +188 -54
- package/dist/layout/hover/HoverDefaults.d.ts +52 -0
- package/dist/layout/hover/HoverDefaults.js +96 -0
- package/dist/layout/hover/HoverLayers.d.ts +35 -0
- package/dist/layout/hover/HoverLayers.js +122 -0
- package/dist/layout/hover/HoverSettings.d.ts +1 -3
- package/dist/layout/hover/HoverSettings.js +3 -2
- package/dist/layout/hover/HoverShapes.d.ts +44 -0
- package/dist/layout/hover/HoverShapes.js +103 -0
- package/dist/layout/hover/PlotHover.svelte.d.ts +18 -0
- package/dist/layout/hover/PlotHover.svelte.js +54 -1
- package/dist/layout/legend/LegendState.svelte.d.ts +2 -0
- package/dist/layout/legend/LegendState.svelte.js +5 -0
- package/dist/layout/plot/Core.svelte +2 -2
- package/dist/layout/plot/PublicPlotContext.d.ts +6 -0
- package/dist/layout/plot/PublicPlotContext.js +8 -0
- package/dist/layout/styles/ValueLabels.d.ts +12 -4
- package/dist/layout/styles/ValueLabels.js +22 -13
- package/dist/layout/tooltip/TooltipState.svelte.d.ts +8 -9
- package/dist/layout/tooltip/TooltipState.svelte.js +28 -29
- package/dist/markers/MarkerKinds.d.ts +5 -0
- package/dist/markers/MarkerKinds.js +1 -0
- package/dist/markers/band/BandMarker.svelte +74 -0
- package/dist/markers/band/BandMarker.svelte.d.ts +7 -0
- package/dist/markers/band/Bands.d.ts +16 -0
- package/dist/markers/band/Bands.js +48 -0
- package/dist/markers/bar/BarMarker.svelte +23 -66
- package/dist/markers/hover/HoverMarker.svelte +7 -265
- package/dist/markers/hover/HoverMarker.svelte.d.ts +4 -27
- package/dist/markers/utils/PointPixel.d.ts +37 -0
- package/dist/markers/utils/PointPixel.js +44 -0
- package/dist/phases/Pipeline.d.ts +7 -7
- package/dist/phases/Pipeline.js +12 -9
- package/dist/phases/context/ContextPhase.d.ts +22 -0
- package/dist/phases/context/ContextPhase.js +23 -0
- package/dist/phases/context/RowContext.d.ts +35 -0
- package/dist/phases/context/RowContext.js +95 -0
- package/dist/phases/data/DataPhase.d.ts +4 -4
- package/dist/phases/data/DataPhase.js +7 -7
- package/dist/phases/hover/HoverPhase.d.ts +6 -7
- package/dist/phases/hover/HoverPhase.js +28 -23
- package/dist/phases/mapping/MappingPhase.d.ts +4 -4
- package/dist/phases/mapping/MappingPhase.js +2 -2
- package/dist/phases/markers/MarkerStyles.d.ts +8 -0
- package/dist/phases/markers/MarkerStyles.js +15 -0
- package/dist/phases/markers/MarkersPhase.d.ts +4 -4
- package/dist/phases/markers/MarkersPhase.js +4 -4
- package/dist/phases/scales/ScalesPhase.d.ts +4 -4
- package/dist/phases/scales/ScalesPhase.js +4 -4
- package/dist/phases/sharedScales/SharedScalesPhase.d.ts +2 -2
- package/dist/phases/sharedScales/SharedScalesPhase.js +2 -2
- package/dist/phases/statistics/MarkerGrouping.d.ts +0 -9
- package/dist/phases/statistics/MarkerGrouping.js +0 -16
- package/dist/phases/styles/StylesPhase.d.ts +3 -3
- package/dist/phases/styles/StylesPhase.js +2 -2
- package/dist/phases/tooltip/TooltipPhase.d.ts +4 -4
- package/dist/phases/tooltip/TooltipPhase.js +10 -7
- package/dist/phases/transforms/TransformsPhase.d.ts +3 -3
- package/dist/phases/transforms/TransformsPhase.js +2 -2
- package/dist/plots/correlation/heatmap/defaults.d.ts +2 -2
- package/dist/plots/correlation/heatmap/defaults.js +21 -6
- package/dist/plots/correlation/heatmap/markers.d.ts +2 -2
- package/dist/plots/correlation/heatmap/markers.js +4 -3
- package/dist/plots/correlation/scatter/defaults.d.ts +2 -2
- package/dist/plots/correlation/scatter/defaults.js +9 -3
- package/dist/plots/correlation/scatter/markers.d.ts +2 -2
- package/dist/plots/correlation/scatter/markers.js +4 -3
- package/dist/plots/distribution/box/BoxSummaries.d.ts +17 -8
- package/dist/plots/distribution/box/BoxSummaries.js +90 -40
- package/dist/plots/distribution/box/defaults.d.ts +2 -2
- package/dist/plots/distribution/box/defaults.js +9 -6
- package/dist/plots/distribution/box/markers.d.ts +2 -2
- package/dist/plots/distribution/box/markers.js +3 -3
- package/dist/plots/distribution/density/defaults.d.ts +2 -2
- package/dist/plots/distribution/density/defaults.js +17 -1
- package/dist/plots/distribution/density/markers.d.ts +2 -2
- package/dist/plots/distribution/density/markers.js +4 -4
- package/dist/plots/distribution/histogram/defaults.d.ts +2 -2
- package/dist/plots/distribution/histogram/defaults.js +20 -25
- package/dist/plots/distribution/histogram/markers.d.ts +2 -2
- package/dist/plots/distribution/histogram/markers.js +5 -4
- package/dist/plots/distribution/pyramid/defaults.d.ts +2 -2
- package/dist/plots/distribution/pyramid/defaults.js +4 -1
- package/dist/plots/distribution/pyramid/markers.d.ts +2 -2
- package/dist/plots/distribution/pyramid/markers.js +4 -3
- package/dist/plots/distribution/violin/defaults.d.ts +2 -2
- package/dist/plots/distribution/violin/defaults.js +9 -7
- package/dist/plots/distribution/violin/markers.d.ts +2 -2
- package/dist/plots/distribution/violin/markers.js +3 -3
- package/dist/plots/evolution/area/defaults.d.ts +2 -2
- package/dist/plots/evolution/area/defaults.js +1 -1
- package/dist/plots/evolution/area/markers.d.ts +2 -2
- package/dist/plots/evolution/area/markers.js +4 -4
- package/dist/plots/evolution/line/defaults.d.ts +2 -2
- package/dist/plots/evolution/line/defaults.js +1 -1
- package/dist/plots/evolution/line/markers.d.ts +2 -2
- package/dist/plots/evolution/line/markers.js +3 -3
- package/dist/plots/evolution/slope/defaults.d.ts +2 -2
- package/dist/plots/evolution/slope/defaults.js +8 -4
- package/dist/plots/evolution/slope/markers.d.ts +2 -2
- package/dist/plots/evolution/slope/markers.js +3 -3
- package/dist/plots/evolution/sparkline/defaults.d.ts +2 -2
- package/dist/plots/evolution/sparkline/defaults.js +5 -2
- package/dist/plots/evolution/sparkline/markers.d.ts +2 -2
- package/dist/plots/evolution/sparkline/markers.js +3 -3
- package/dist/plots/map/geo/Core.svelte +3 -3
- package/dist/plots/map/geo/defaults.d.ts +2 -2
- package/dist/plots/map/geo/phases/data.d.ts +1 -1
- package/dist/plots/map/geo/phases/data.js +1 -1
- package/dist/plots/map/geo/phases/hover.d.ts +2 -2
- package/dist/plots/map/geo/phases/hover.js +3 -3
- package/dist/plots/map/geo/phases/markers.d.ts +2 -2
- package/dist/plots/map/geo/phases/markers.js +3 -3
- package/dist/plots/map/geo/phases/scales.d.ts +2 -2
- package/dist/plots/map/geo/phases/scales.js +2 -2
- package/dist/plots/part-of-a-whole/treemap/defaults.d.ts +2 -2
- package/dist/plots/part-of-a-whole/treemap/defaults.js +37 -21
- package/dist/plots/ranking/bar/defaults.d.ts +2 -2
- package/dist/plots/ranking/bar/defaults.js +13 -7
- package/dist/plots/ranking/bar/markers.d.ts +3 -3
- package/dist/plots/ranking/bar/markers.js +8 -7
- package/dist/plots/ranking/bullet/defaults.d.ts +2 -2
- package/dist/plots/ranking/bullet/defaults.js +4 -4
- package/dist/plots/ranking/bullet/markers.d.ts +3 -3
- package/dist/plots/ranking/bullet/markers.js +9 -9
- package/dist/plots/ranking/cleveland/ClevelandMarkers.d.ts +3 -3
- package/dist/plots/ranking/cleveland/ClevelandMarkers.js +11 -10
- package/dist/plots/ranking/cleveland/defaults.d.ts +2 -2
- package/dist/plots/ranking/cleveland/defaults.js +7 -4
- package/dist/plots/ranking/dumbbell/defaults.d.ts +2 -2
- package/dist/plots/ranking/forest/defaults.d.ts +2 -2
- package/dist/plots/ranking/lollipop/defaults.d.ts +2 -2
- package/dist/types/configuration/styling.d.ts +4 -0
- package/dist/types/layout/context.d.ts +21 -0
- package/dist/types/layout/context.js +1 -0
- package/dist/types/layout/hover.d.ts +3 -0
- package/dist/types/layout/styles/values.d.ts +8 -2
- package/dist/types/layout/tooltip.d.ts +5 -10
- package/dist/types/markers/axis/axisBasedMarkersConfig.d.ts +2 -1
- package/dist/types/markers/axis/band.d.ts +24 -0
- package/dist/types/markers/axis/band.js +1 -0
- package/dist/types/markers/axis/bar.d.ts +2 -2
- package/dist/types/markers/axis/hover.d.ts +60 -15
- package/dist/types/markers/channels/commonMarkerConfig.d.ts +6 -2
- package/dist/types/markers/common.d.ts +1 -0
- package/dist/types/markers/common.js +1 -0
- package/dist/types/phases/context/phase.d.ts +7 -0
- package/dist/types/phases/context/phase.js +1 -0
- package/dist/types/phases/data/phase.d.ts +2 -2
- package/dist/types/phases/hover/phase.d.ts +10 -7
- package/dist/types/phases/index.d.ts +2 -1
- package/dist/types/phases/kind.d.ts +11 -8
- package/dist/types/phases/mapping/phase.d.ts +2 -2
- package/dist/types/phases/markers/phase.d.ts +3 -3
- package/dist/types/phases/scales/phase.d.ts +4 -4
- package/dist/types/phases/stage.d.ts +3 -3
- package/dist/types/phases/state.d.ts +1 -1
- package/dist/types/phases/styles/phase.d.ts +3 -2
- package/dist/types/phases/transforms/phase.d.ts +2 -2
- package/dist/types/plots/constants.d.ts +3 -5
- package/dist/types/plots/correlation/heatmap/state.d.ts +2 -2
- package/dist/types/plots/correlation/scatter/state.d.ts +2 -2
- package/dist/types/plots/distribution/box/props.d.ts +23 -1
- package/dist/types/plots/distribution/box/state.d.ts +6 -17
- package/dist/types/plots/distribution/density/state.d.ts +2 -2
- package/dist/types/plots/distribution/histogram/props.d.ts +14 -1
- package/dist/types/plots/distribution/histogram/state.d.ts +2 -2
- package/dist/types/plots/distribution/pyramid/state.d.ts +2 -2
- package/dist/types/plots/distribution/violin/props.d.ts +2 -1
- package/dist/types/plots/distribution/violin/state.d.ts +2 -2
- package/dist/types/plots/evolution/area/state.d.ts +2 -2
- package/dist/types/plots/evolution/line/state.d.ts +2 -2
- package/dist/types/plots/evolution/slope/state.d.ts +2 -2
- package/dist/types/plots/evolution/sparkline/state.d.ts +2 -2
- package/dist/types/plots/map/geo/state.d.ts +1 -1
- package/dist/types/plots/part-of-a-whole/treemap/props.d.ts +14 -4
- package/dist/types/plots/part-of-a-whole/treemap/state.d.ts +2 -2
- package/dist/types/plots/props.d.ts +3 -0
- package/dist/types/plots/ranking/bar/state.d.ts +2 -2
- package/dist/types/plots/ranking/bullet/state.d.ts +2 -2
- package/dist/types/plots/ranking/cleveland/state.d.ts +2 -2
- package/dist/types/plots/ranking/dumbbell/state.d.ts +2 -2
- package/dist/types/plots/ranking/forest/state.d.ts +2 -2
- package/dist/types/plots/ranking/lollipop/state.d.ts +2 -2
- package/docs/markers.md +34 -2
- package/docs/plots/evolution/line.md +1 -1
- package/docs/plots/evolution/slope.md +1 -1
- package/docs/plots/part-of-a-whole/treemap.md +36 -2
- package/docs/plots/ranking/bar.md +1 -0
- package/docs/plots/ranking/cleveland.md +1 -1
- package/docs/state.md +33 -0
- package/docs/styles-and-segments.md +2 -2
- package/docs/theming.md +2 -1
- package/docs/tooltip-and-hover.md +81 -3
- package/examples/LineDates.svelte +1 -1
- package/examples/ResponsiveLine.svelte +3 -1
- package/examples/TreemapHover.svelte +25 -0
- package/examples/ValueLabels.svelte +1 -1
- package/llms-full.txt +8 -6
- package/llms.txt +2 -1
- package/package.json +1 -1
|
@@ -1,283 +1,25 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { usePlot } from '../../layout/plot/usePlot';
|
|
3
|
-
import
|
|
4
|
-
import TextMarker from '../text/TextMarker.svelte';
|
|
5
|
-
import RuleMarker from '../rule/RuleMarker.svelte';
|
|
6
|
-
import BarMarker from '../bar/BarMarker.svelte';
|
|
7
|
-
import { getConfiguration } from '../../configuration/config.svelte';
|
|
8
|
-
import { NumberFormat } from '../../utils/NumberFormat';
|
|
9
|
-
import { BandNudge } from '../utils/BandNudge';
|
|
10
|
-
import type { BandSlice } from '../../types/layout/hover';
|
|
11
|
-
import type { HoverMarkerConfig } from '../../types/markers/common';
|
|
3
|
+
import { PointPixel } from '../utils/PointPixel';
|
|
12
4
|
import type { HoverDisplayPoint } from '../../types/layout/hover';
|
|
13
5
|
|
|
14
6
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* any pixel math. Draws optional x/y crosshair rules plus a dot + value label
|
|
19
|
-
* for each matched point.
|
|
20
|
-
*
|
|
21
|
-
* Takes the raw `marker` config plus the couple of things it can't know on
|
|
22
|
-
* its own - `activePoint` (the specific matched point actually under the
|
|
23
|
-
* cursor, for the crosshair position; see `controller.svelte.ts`'s own doc
|
|
24
|
-
* comment) and whether the active hover strategy implies a crosshair on
|
|
25
|
-
* each axis by default - the same "component owns its own rendering"
|
|
26
|
-
* pattern `BarMarker`/`RuleMarker` follow. `activePoint`/
|
|
27
|
-
* `impliesCrosshairX`/`impliesCrosshairY` are all optional and default to
|
|
28
|
-
* "no crosshair".
|
|
29
|
-
*
|
|
30
|
-
* `areaX`/`areaY` are a separate, always-explicit opt-in (no implied
|
|
31
|
-
* default) for a thickened area highlight in place of the thin crosshair -
|
|
32
|
-
* see `HoverMarkerConfig`'s own doc comment. Rendered via `BarMarker`'s
|
|
33
|
-
* `role: 'highlight'`, so it renders nothing on an axis without a band
|
|
34
|
-
* scale (e.g. a line/scatter plot's continuous axis).
|
|
7
|
+
* Where the hover is, rendered *inside* a `<Plot>`: it reports the pixel position of the point under the pointer,
|
|
8
|
+
* through the host plot's scales, so a synced plot's tooltip can sit on it. It draws nothing itself - the
|
|
9
|
+
* `hover` marker's `back` and `front` markers are drawn by `GeometryLayout`, as every other marker.
|
|
35
10
|
*/
|
|
36
11
|
interface Props {
|
|
37
|
-
data?: HoverDisplayPoint[];
|
|
38
|
-
/** Narrowed to just the fields this component reads - `HoverMarkerConfig` also carries routing/scoping fields (`series`/`scope`/`strategy`/`sync`) it has no use for. */
|
|
39
|
-
marker: Pick<
|
|
40
|
-
HoverMarkerConfig,
|
|
41
|
-
| 'ruleX'
|
|
42
|
-
| 'ruleY'
|
|
43
|
-
| 'areaX'
|
|
44
|
-
| 'areaY'
|
|
45
|
-
| 'showLabels'
|
|
46
|
-
| 'format'
|
|
47
|
-
| 'rule'
|
|
48
|
-
| 'area'
|
|
49
|
-
| 'dot'
|
|
50
|
-
| 'text'
|
|
51
|
-
| 'decimals'
|
|
52
|
-
| 'prefix'
|
|
53
|
-
| 'suffix'
|
|
54
|
-
| 'signDisplay'
|
|
55
|
-
>;
|
|
56
12
|
activePoint?: HoverDisplayPoint | null;
|
|
57
|
-
|
|
58
|
-
impliesCrosshairY?: boolean;
|
|
59
|
-
/** Reports `activePoint`'s real pixel position within this facet's own box, or `null` when nothing's matched. */
|
|
13
|
+
/** Reports `activePoint`'s pixel position within this facet's own box, or `null` when nothing's matched. */
|
|
60
14
|
onActivePointPixel?: (pos: { x: number; y: number } | null) => void;
|
|
61
15
|
}
|
|
62
16
|
|
|
63
|
-
let {
|
|
64
|
-
data = [],
|
|
65
|
-
marker,
|
|
66
|
-
activePoint = null,
|
|
67
|
-
impliesCrosshairX = false,
|
|
68
|
-
impliesCrosshairY = false,
|
|
69
|
-
onActivePointPixel
|
|
70
|
-
}: Props = $props();
|
|
71
|
-
|
|
72
|
-
const cfg = $derived(getConfiguration());
|
|
17
|
+
let { activePoint = null, onActivePointPixel }: Props = $props();
|
|
73
18
|
const plot = usePlot().svelteplot;
|
|
74
19
|
|
|
75
|
-
|
|
76
|
-
function scaledPixel(
|
|
77
|
-
scale: { type?: string; fn?: (v: never) => unknown } | undefined,
|
|
78
|
-
value: unknown
|
|
79
|
-
): number | null {
|
|
80
|
-
if (!scale?.fn || value == null) return null;
|
|
81
|
-
const raw = Number(scale.fn(value as never));
|
|
82
|
-
if (!Number.isFinite(raw)) return null;
|
|
83
|
-
if (scale.type === 'band') {
|
|
84
|
-
const bandwidth = (scale.fn as { bandwidth?: () => number }).bandwidth?.() ?? 0;
|
|
85
|
-
return raw + bandwidth / 2;
|
|
86
|
-
}
|
|
87
|
-
return raw;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const nudgeOf = (point: { areaInset?: BandSlice } | null | undefined) =>
|
|
91
|
-
BandNudge.of(plot.scales, point?.areaInset);
|
|
92
|
-
|
|
93
|
-
const activePixel = $derived.by((): { x: number; y: number } | null => {
|
|
94
|
-
if (!activePoint) return null;
|
|
95
|
-
const x = scaledPixel(plot.scales.x, activePoint.x);
|
|
96
|
-
const y = scaledPixel(plot.scales.y, activePoint.y);
|
|
97
|
-
if (x == null || y == null) return null;
|
|
98
|
-
const nudge = nudgeOf(activePoint);
|
|
99
|
-
return { x: x + nudge.dx, y: y + nudge.dy };
|
|
100
|
-
});
|
|
20
|
+
const activePixel = $derived(activePoint ? PointPixel.of(plot.scales, activePoint) : null);
|
|
101
21
|
|
|
102
22
|
$effect(() => {
|
|
103
23
|
onActivePointPixel?.(activePixel);
|
|
104
24
|
});
|
|
105
|
-
|
|
106
|
-
const showCrosshairX = $derived(
|
|
107
|
-
marker.ruleX !== false && (impliesCrosshairX || marker.ruleX === true)
|
|
108
|
-
);
|
|
109
|
-
const showCrosshairY = $derived(
|
|
110
|
-
marker.ruleY !== false && (impliesCrosshairY || marker.ruleY === true)
|
|
111
|
-
);
|
|
112
|
-
const ruleX = $derived(showCrosshairX ? (activePoint?.x ?? null) : null);
|
|
113
|
-
const ruleY = $derived(showCrosshairY ? (activePoint?.y ?? null) : null);
|
|
114
|
-
// areaX/areaY have no implied default - always off unless explicitly set.
|
|
115
|
-
const areaX = $derived(marker.areaX === true ? (activePoint?.x ?? null) : null);
|
|
116
|
-
const areaY = $derived(marker.areaY === true ? (activePoint?.y ?? null) : null);
|
|
117
|
-
/**
|
|
118
|
-
* The part of the band an area covers: the active point's own slice (a grouped bar's series), or the whole
|
|
119
|
-
* band while the hover matches more than one point in that category, as its dots do.
|
|
120
|
-
*/
|
|
121
|
-
const areaInsetOf = (axis: 'x' | 'y'): BandSlice | undefined => {
|
|
122
|
-
const at = activePoint?.[axis];
|
|
123
|
-
const key = (value: unknown) => (value instanceof Date ? +value : value);
|
|
124
|
-
const shared = data.filter((point) => key(point[axis]) === key(at)).length > 1;
|
|
125
|
-
return shared ? undefined : activePoint?.areaInset;
|
|
126
|
-
};
|
|
127
|
-
const areaInsetX = $derived(areaX != null ? areaInsetOf('x') : undefined);
|
|
128
|
-
const areaInsetY = $derived(areaY != null ? areaInsetOf('y') : undefined);
|
|
129
|
-
const showLabels = $derived(marker.showLabels ?? true);
|
|
130
|
-
const format = $derived(marker.format);
|
|
131
|
-
/** The hovered value as the marker's display fields ask; `undefined` while nothing does, so the plot's own label stays. */
|
|
132
|
-
const numberOf = $derived(NumberFormat.maybe(NumberFormat.displayOf(marker), cfg));
|
|
133
|
-
const number = $derived(numberOf ?? NumberFormat.of(undefined, cfg));
|
|
134
|
-
const labelOf = (d: HoverDisplayPoint): string => {
|
|
135
|
-
if (format) return format(d, number);
|
|
136
|
-
const value = Number(d.label);
|
|
137
|
-
return numberOf && d.label !== undefined && d.label !== '' && Number.isFinite(value)
|
|
138
|
-
? numberOf(value)
|
|
139
|
-
: (d.label ?? '');
|
|
140
|
-
};
|
|
141
|
-
const ruleStyle = $derived(marker.rule);
|
|
142
|
-
const areaStyle = $derived({ ...cfg.hover.area, ...marker.area });
|
|
143
|
-
const dotStyle = $derived(marker.dot);
|
|
144
|
-
const fontStyle = $derived(marker.text);
|
|
145
|
-
|
|
146
|
-
/** Explicit style wins; otherwise fall back to the point's own resolved color. */
|
|
147
|
-
const resolveColor = (explicit: string | undefined, point: HoverDisplayPoint) =>
|
|
148
|
-
explicit ?? point.color;
|
|
149
|
-
|
|
150
|
-
// Horizontal gap between the dot's edge and an 'outside'-anchored label -
|
|
151
|
-
// scales with the dot's own resolved radius so the label clears it.
|
|
152
|
-
const dotRadius = $derived(dotStyle?.dotRadius ?? cfg.hover.dot.dotRadius ?? 5);
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Resolves `text.textAnchor: 'outside'` into a per-point anchor +
|
|
156
|
-
* offset, mirroring the value-label `'outside'` anchor's own left/right
|
|
157
|
-
* split (see pyramid/bar's own `ValueAnchor` handling): a positive `x`
|
|
158
|
-
* anchors `'start'` and nudges right, a negative `x` anchors `'end'` and
|
|
159
|
-
* nudges left - so a diverging mark's label sits on the side of the dot
|
|
160
|
-
* away from zero, on whichever side that dot itself landed. Only
|
|
161
|
-
* meaningful when the point's `x` is numeric (e.g. pyramid's magnitude
|
|
162
|
-
* axis); anything else falls through to the plain `text` style unchanged.
|
|
163
|
-
*
|
|
164
|
-
* Also overrides `cfg.hover.font`'s default `dy: -18` (meant for a label
|
|
165
|
-
* floating *above* the dot) back to vertically centered, and clears the
|
|
166
|
-
* dot's own radius horizontally instead of the flat default nudge, so the
|
|
167
|
-
* label doesn't collide with the dot it's labeling.
|
|
168
|
-
*/
|
|
169
|
-
function resolveFontStyle(point: HoverDisplayPoint) {
|
|
170
|
-
if (fontStyle?.textAnchor !== 'outside' || typeof point.x !== 'number') {
|
|
171
|
-
return { ...cfg.hover.font, ...fontStyle, fill: resolveColor(fontStyle?.fill, point) };
|
|
172
|
-
}
|
|
173
|
-
const outward = point.x >= 0;
|
|
174
|
-
const gap = dotRadius + 5;
|
|
175
|
-
return {
|
|
176
|
-
...cfg.hover.font,
|
|
177
|
-
...fontStyle,
|
|
178
|
-
fill: resolveColor(fontStyle?.fill, point),
|
|
179
|
-
textAnchor: outward ? ('start' as const) : ('end' as const),
|
|
180
|
-
lineAnchor: fontStyle?.lineAnchor ?? ('middle' as const),
|
|
181
|
-
dx: fontStyle?.dx ?? (outward ? gap : -gap),
|
|
182
|
-
dy: fontStyle?.dy ?? 0
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
25
|
</script>
|
|
186
|
-
|
|
187
|
-
{#if areaX != null}
|
|
188
|
-
<BarMarker
|
|
189
|
-
marker={{
|
|
190
|
-
type: 'bar',
|
|
191
|
-
role: 'highlight',
|
|
192
|
-
isHorizontal: false,
|
|
193
|
-
data: [],
|
|
194
|
-
category: areaX,
|
|
195
|
-
styles: {
|
|
196
|
-
...areaStyle,
|
|
197
|
-
insetLeft: areaInsetX?.start,
|
|
198
|
-
insetRight: areaInsetX?.end
|
|
199
|
-
}
|
|
200
|
-
}}
|
|
201
|
-
/>
|
|
202
|
-
{/if}
|
|
203
|
-
|
|
204
|
-
{#if areaY != null}
|
|
205
|
-
<BarMarker
|
|
206
|
-
marker={{
|
|
207
|
-
type: 'bar',
|
|
208
|
-
role: 'highlight',
|
|
209
|
-
isHorizontal: true,
|
|
210
|
-
data: [],
|
|
211
|
-
category: areaY,
|
|
212
|
-
styles: {
|
|
213
|
-
...areaStyle,
|
|
214
|
-
insetTop: areaInsetY?.start,
|
|
215
|
-
insetBottom: areaInsetY?.end
|
|
216
|
-
}
|
|
217
|
-
}}
|
|
218
|
-
/>
|
|
219
|
-
{/if}
|
|
220
|
-
|
|
221
|
-
{#if ruleX != null}
|
|
222
|
-
<g transform="translate({nudgeOf(activePoint).dx}, {nudgeOf(activePoint).dy})">
|
|
223
|
-
<RuleMarker
|
|
224
|
-
marker={{
|
|
225
|
-
type: 'rule',
|
|
226
|
-
axis: 'x',
|
|
227
|
-
value: ruleX,
|
|
228
|
-
styles: { ...cfg.hover.rule, ...ruleStyle }
|
|
229
|
-
}}
|
|
230
|
-
/>
|
|
231
|
-
</g>
|
|
232
|
-
{/if}
|
|
233
|
-
|
|
234
|
-
{#if ruleY != null}
|
|
235
|
-
<g transform="translate({nudgeOf(activePoint).dx}, {nudgeOf(activePoint).dy})">
|
|
236
|
-
<RuleMarker
|
|
237
|
-
marker={{
|
|
238
|
-
type: 'rule',
|
|
239
|
-
axis: 'y',
|
|
240
|
-
value: ruleY,
|
|
241
|
-
styles: { ...cfg.hover.rule, ...ruleStyle }
|
|
242
|
-
}}
|
|
243
|
-
/>
|
|
244
|
-
</g>
|
|
245
|
-
{/if}
|
|
246
|
-
|
|
247
|
-
{#if data.length > 0}
|
|
248
|
-
{#each data as point (`${point.series ?? ''}_${point.x}_${point.y}`)}
|
|
249
|
-
<!-- A grouped bar's point sits in its series' slice of the band (`areaInset`), not at the band's middle. -->
|
|
250
|
-
<g transform="translate({nudgeOf(point).dx}, {nudgeOf(point).dy})">
|
|
251
|
-
<DotMarker
|
|
252
|
-
marker={{
|
|
253
|
-
type: 'dot',
|
|
254
|
-
data: [point],
|
|
255
|
-
x: (d) => d.x,
|
|
256
|
-
y: (d) => d.y,
|
|
257
|
-
styles: {
|
|
258
|
-
...cfg.hover.dot,
|
|
259
|
-
...dotStyle,
|
|
260
|
-
dotFill: resolveColor(dotStyle?.dotFill, point),
|
|
261
|
-
dotStroke: resolveColor(dotStyle?.dotStroke, point)
|
|
262
|
-
}
|
|
263
|
-
}}
|
|
264
|
-
/>
|
|
265
|
-
</g>
|
|
266
|
-
{/each}
|
|
267
|
-
{#if showLabels}
|
|
268
|
-
{#each data as point (`${point.series ?? ''}_${point.x}_${point.y}`)}
|
|
269
|
-
<g transform="translate({nudgeOf(point).dx}, {nudgeOf(point).dy})">
|
|
270
|
-
<TextMarker
|
|
271
|
-
marker={{
|
|
272
|
-
type: 'text',
|
|
273
|
-
data: [point],
|
|
274
|
-
x: (d: HoverDisplayPoint) => d.x,
|
|
275
|
-
y: (d: HoverDisplayPoint) => d.y,
|
|
276
|
-
text: labelOf,
|
|
277
|
-
styles: resolveFontStyle(point)
|
|
278
|
-
}}
|
|
279
|
-
/>
|
|
280
|
-
</g>
|
|
281
|
-
{/each}
|
|
282
|
-
{/if}
|
|
283
|
-
{/if}
|
|
@@ -1,35 +1,12 @@
|
|
|
1
|
-
import type { HoverMarkerConfig } from '../../types/markers/common';
|
|
2
1
|
import type { HoverDisplayPoint } from '../../types/layout/hover';
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* any pixel math. Draws optional x/y crosshair rules plus a dot + value label
|
|
8
|
-
* for each matched point.
|
|
9
|
-
*
|
|
10
|
-
* Takes the raw `marker` config plus the couple of things it can't know on
|
|
11
|
-
* its own - `activePoint` (the specific matched point actually under the
|
|
12
|
-
* cursor, for the crosshair position; see `controller.svelte.ts`'s own doc
|
|
13
|
-
* comment) and whether the active hover strategy implies a crosshair on
|
|
14
|
-
* each axis by default - the same "component owns its own rendering"
|
|
15
|
-
* pattern `BarMarker`/`RuleMarker` follow. `activePoint`/
|
|
16
|
-
* `impliesCrosshairX`/`impliesCrosshairY` are all optional and default to
|
|
17
|
-
* "no crosshair".
|
|
18
|
-
*
|
|
19
|
-
* `areaX`/`areaY` are a separate, always-explicit opt-in (no implied
|
|
20
|
-
* default) for a thickened area highlight in place of the thin crosshair -
|
|
21
|
-
* see `HoverMarkerConfig`'s own doc comment. Rendered via `BarMarker`'s
|
|
22
|
-
* `role: 'highlight'`, so it renders nothing on an axis without a band
|
|
23
|
-
* scale (e.g. a line/scatter plot's continuous axis).
|
|
3
|
+
* Where the hover is, rendered *inside* a `<Plot>`: it reports the pixel position of the point under the pointer,
|
|
4
|
+
* through the host plot's scales, so a synced plot's tooltip can sit on it. It draws nothing itself - the
|
|
5
|
+
* `hover` marker's `back` and `front` markers are drawn by `GeometryLayout`, as every other marker.
|
|
24
6
|
*/
|
|
25
7
|
interface Props {
|
|
26
|
-
data?: HoverDisplayPoint[];
|
|
27
|
-
/** Narrowed to just the fields this component reads - `HoverMarkerConfig` also carries routing/scoping fields (`series`/`scope`/`strategy`/`sync`) it has no use for. */
|
|
28
|
-
marker: Pick<HoverMarkerConfig, 'ruleX' | 'ruleY' | 'areaX' | 'areaY' | 'showLabels' | 'format' | 'rule' | 'area' | 'dot' | 'text' | 'decimals' | 'prefix' | 'suffix' | 'signDisplay'>;
|
|
29
8
|
activePoint?: HoverDisplayPoint | null;
|
|
30
|
-
|
|
31
|
-
impliesCrosshairY?: boolean;
|
|
32
|
-
/** Reports `activePoint`'s real pixel position within this facet's own box, or `null` when nothing's matched. */
|
|
9
|
+
/** Reports `activePoint`'s pixel position within this facet's own box, or `null` when nothing's matched. */
|
|
33
10
|
onActivePointPixel?: (pos: {
|
|
34
11
|
x: number;
|
|
35
12
|
y: number;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { BandSlice } from '../../types/layout/hover';
|
|
2
|
+
type Scale = {
|
|
3
|
+
type?: string;
|
|
4
|
+
fn?: (value: never) => unknown;
|
|
5
|
+
} | undefined;
|
|
6
|
+
/** Where a point is drawn, in pixels of its plot: its `x` and `y` through the plot's scales. */
|
|
7
|
+
export declare class PointPixel {
|
|
8
|
+
#private;
|
|
9
|
+
/**
|
|
10
|
+
* `point`'s pixel position - the middle of its band on a band scale, of its slice when it has one (a grouped
|
|
11
|
+
* bar's series) - or `null` when a scale cannot place it.
|
|
12
|
+
*/
|
|
13
|
+
static of(scales: {
|
|
14
|
+
x?: Scale;
|
|
15
|
+
y?: Scale;
|
|
16
|
+
}, point: {
|
|
17
|
+
x: unknown;
|
|
18
|
+
y: unknown;
|
|
19
|
+
areaInset?: BandSlice;
|
|
20
|
+
}): {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
} | null;
|
|
24
|
+
/** The index of the one of `points` nearest `cursor`, or `-1` when none can be placed. */
|
|
25
|
+
static nearest(scales: {
|
|
26
|
+
x?: Scale;
|
|
27
|
+
y?: Scale;
|
|
28
|
+
}, points: readonly {
|
|
29
|
+
x: unknown;
|
|
30
|
+
y: unknown;
|
|
31
|
+
areaInset?: BandSlice;
|
|
32
|
+
}[], cursor: {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
}): number;
|
|
36
|
+
}
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { BandNudge } from './BandNudge';
|
|
2
|
+
/** Where a point is drawn, in pixels of its plot: its `x` and `y` through the plot's scales. */
|
|
3
|
+
export class PointPixel {
|
|
4
|
+
/**
|
|
5
|
+
* `point`'s pixel position - the middle of its band on a band scale, of its slice when it has one (a grouped
|
|
6
|
+
* bar's series) - or `null` when a scale cannot place it.
|
|
7
|
+
*/
|
|
8
|
+
static of(scales, point) {
|
|
9
|
+
const x = PointPixel.#along(scales.x, point.x);
|
|
10
|
+
const y = PointPixel.#along(scales.y, point.y);
|
|
11
|
+
if (x == null || y == null)
|
|
12
|
+
return null;
|
|
13
|
+
const nudge = BandNudge.of(scales, point.areaInset);
|
|
14
|
+
return { x: x + nudge.dx, y: y + nudge.dy };
|
|
15
|
+
}
|
|
16
|
+
/** The index of the one of `points` nearest `cursor`, or `-1` when none can be placed. */
|
|
17
|
+
static nearest(scales, points, cursor) {
|
|
18
|
+
let best = -1;
|
|
19
|
+
let bestDistance = Infinity;
|
|
20
|
+
points.forEach((point, index) => {
|
|
21
|
+
const pixel = PointPixel.of(scales, point);
|
|
22
|
+
if (!pixel)
|
|
23
|
+
return;
|
|
24
|
+
const distance = Math.hypot(pixel.x - cursor.x, pixel.y - cursor.y);
|
|
25
|
+
if (distance < bestDistance) {
|
|
26
|
+
best = index;
|
|
27
|
+
bestDistance = distance;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
return best;
|
|
31
|
+
}
|
|
32
|
+
/** `value`'s pixel along one scale, in the middle of its band on a band scale. */
|
|
33
|
+
static #along(scale, value) {
|
|
34
|
+
if (!scale?.fn || value == null)
|
|
35
|
+
return null;
|
|
36
|
+
const raw = Number(scale.fn(value));
|
|
37
|
+
if (!Number.isFinite(raw))
|
|
38
|
+
return null;
|
|
39
|
+
if (scale.type !== 'band')
|
|
40
|
+
return raw;
|
|
41
|
+
const bandwidth = scale.fn.bandwidth?.() ?? 0;
|
|
42
|
+
return raw + bandwidth / 2;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AxisPlotState,
|
|
1
|
+
import type { AxisPlotState, AxisPlotEnv, AxisPlotKind, AxisPlotDefaults, DataRunState, HoverStageOptions, PhaseStage, PlotPhase } from '../types/phases';
|
|
2
2
|
import type { PlotStylesConfig } from '../types/configuration/styling';
|
|
3
3
|
import type { FacetConfig } from '../types/layout/facet';
|
|
4
4
|
import type { LegendState } from '../layout/legend/LegendState.svelte';
|
|
@@ -55,7 +55,7 @@ export type PipelineOptions<TState> = {
|
|
|
55
55
|
* Stages declared out of phase order still run in it - with a warning, once,
|
|
56
56
|
* since the declared order would otherwise read as the one that runs.
|
|
57
57
|
*/
|
|
58
|
-
export declare class Pipeline<TState,
|
|
58
|
+
export declare class Pipeline<TState, TEnv = unknown> {
|
|
59
59
|
#private;
|
|
60
60
|
/**
|
|
61
61
|
* The phases that work on the plot's rows - everything before its scales,
|
|
@@ -65,25 +65,25 @@ export declare class Pipeline<TState, TCtx = unknown> {
|
|
|
65
65
|
static readonly DATA_PHASES: readonly PlotPhase[];
|
|
66
66
|
/** Every {@link PlotPhase}, in the order they run: the data phases, then each panel's own. */
|
|
67
67
|
static readonly ORDER: readonly PlotPhase[];
|
|
68
|
-
readonly stages: readonly PhaseStage<TState,
|
|
69
|
-
constructor(stages: PhaseStage<TState,
|
|
68
|
+
readonly stages: readonly PhaseStage<TState, TEnv>[];
|
|
69
|
+
constructor(stages: PhaseStage<TState, TEnv>[], options?: PipelineOptions<TState>);
|
|
70
70
|
/**
|
|
71
71
|
* An axis-based plot kind's pipeline: the shared stages, each set up with
|
|
72
72
|
* the kind's own options - its marks and value labels among them - started
|
|
73
73
|
* from a core's props. Built once per kind object.
|
|
74
74
|
*/
|
|
75
|
-
static ofAxisKind<TState extends AxisPlotState<any, any, any, any>,
|
|
75
|
+
static ofAxisKind<TState extends AxisPlotState<any, any, any, any>, TEnv extends AxisPlotEnv>(kind: AxisPlotKind<TState, TEnv> & Pick<AxisPlotDefaults<any, any>, 'timeline'>): Pipeline<TState, TEnv>;
|
|
76
76
|
/** A plot's pipeline from what its kind gives: its axis-kind defaults, or a pipeline of its own. */
|
|
77
77
|
static of(defaults: AxisPlotDefaults<any, any> | Pipeline<any, any>): Pipeline<any, any>;
|
|
78
78
|
/** The stage of `phase`, if the pipeline has one. */
|
|
79
|
-
stage(phase: PlotPhase): PhaseStage<TState,
|
|
79
|
+
stage(phase: PlotPhase): PhaseStage<TState, TEnv> | undefined;
|
|
80
80
|
/**
|
|
81
81
|
* A core's run: every stage over the state its `input` (an axis core's
|
|
82
82
|
* props) seeds - or, given `panelState` (one facet panel's part of
|
|
83
83
|
* {@link runData}), only the stages after the data phases, over that state
|
|
84
84
|
* with the input's own props (its box, margins and scales) on top.
|
|
85
85
|
*/
|
|
86
|
-
run: (input: unknown,
|
|
86
|
+
run: (input: unknown, env: TEnv, panelState?: object) => TState;
|
|
87
87
|
/** Whether the pipeline has data phases to run once over every facet panel ({@link runData}). */
|
|
88
88
|
get facets(): boolean;
|
|
89
89
|
/**
|
package/dist/phases/Pipeline.js
CHANGED
|
@@ -2,6 +2,7 @@ import { FacetPhase } from './facet/FacetPhase';
|
|
|
2
2
|
import { DataPhase } from './data/DataPhase';
|
|
3
3
|
import { MappingPhase } from './mapping/MappingPhase';
|
|
4
4
|
import { TransformsPhase } from './transforms/TransformsPhase';
|
|
5
|
+
import { ContextPhase } from './context/ContextPhase';
|
|
5
6
|
import { StatisticsPhase } from './statistics/StatisticsPhase';
|
|
6
7
|
import { ScalesPhase } from './scales/ScalesPhase';
|
|
7
8
|
import { SharedScalesPhase } from './sharedScales/SharedScalesPhase';
|
|
@@ -33,6 +34,7 @@ export class Pipeline {
|
|
|
33
34
|
'data',
|
|
34
35
|
'mapping',
|
|
35
36
|
'transforms',
|
|
37
|
+
'context',
|
|
36
38
|
'statistics',
|
|
37
39
|
'sharedScales'
|
|
38
40
|
];
|
|
@@ -79,6 +81,7 @@ export class Pipeline {
|
|
|
79
81
|
new DataPhase(kind.data),
|
|
80
82
|
new MappingPhase(kind.mapping),
|
|
81
83
|
new TransformsPhase(kind.transforms, kind.data?.segments?.connect),
|
|
84
|
+
new ContextPhase(kind.context),
|
|
82
85
|
new StatisticsPhase(kind.statistics),
|
|
83
86
|
new SharedScalesPhase(kind.sharedScales, scales),
|
|
84
87
|
scales,
|
|
@@ -112,12 +115,12 @@ export class Pipeline {
|
|
|
112
115
|
* {@link runData}), only the stages after the data phases, over that state
|
|
113
116
|
* with the input's own props (its box, margins and scales) on top.
|
|
114
117
|
*/
|
|
115
|
-
run = (input,
|
|
118
|
+
run = (input, env, panelState) => {
|
|
116
119
|
const seed = this.#seed(input);
|
|
117
120
|
if (!panelState)
|
|
118
|
-
return Pipeline.#through(this.stages, seed,
|
|
121
|
+
return Pipeline.#through(this.stages, seed, env);
|
|
119
122
|
this.#panel ??= this.stages.filter((stage) => !Pipeline.DATA_PHASES.includes(stage.phase));
|
|
120
|
-
return Pipeline.#through(this.#panel, { ...panelState, ...seed },
|
|
123
|
+
return Pipeline.#through(this.#panel, { ...panelState, ...seed }, env);
|
|
121
124
|
};
|
|
122
125
|
/** Whether the pipeline has data phases to run once over every facet panel ({@link runData}). */
|
|
123
126
|
get facets() {
|
|
@@ -131,7 +134,7 @@ export class Pipeline {
|
|
|
131
134
|
runData(props, rows, facet, legendInteraction, cfg) {
|
|
132
135
|
this.#data ??= this.stages.filter((stage) => Pipeline.DATA_PHASES.includes(stage.phase));
|
|
133
136
|
const seed = { ...this.#seed(props, rows), facet };
|
|
134
|
-
return Pipeline.#through(this.#data, seed, Pipeline.#
|
|
137
|
+
return Pipeline.#through(this.#data, seed, Pipeline.#envFor(cfg, legendInteraction));
|
|
135
138
|
}
|
|
136
139
|
/**
|
|
137
140
|
* The plot's hover strategy when neither its `tooltip` nor a hover marker
|
|
@@ -143,7 +146,7 @@ export class Pipeline {
|
|
|
143
146
|
if (fixed !== undefined)
|
|
144
147
|
return fixed;
|
|
145
148
|
const upToData = this.stages.filter((s) => s.phase === 'data');
|
|
146
|
-
const state = Pipeline.#through(upToData, this.#seed(props), Pipeline.#
|
|
149
|
+
const state = Pipeline.#through(upToData, this.#seed(props), Pipeline.#envFor(cfg, null));
|
|
147
150
|
return HoverSettings.strategyOf(state.axes);
|
|
148
151
|
}
|
|
149
152
|
/** The plot's default `timeline.filterMode`. */
|
|
@@ -162,7 +165,7 @@ export class Pipeline {
|
|
|
162
165
|
return undefined;
|
|
163
166
|
const upToMapping = this.stages.filter((s) => s.phase === 'data' || s.phase === 'mapping');
|
|
164
167
|
// Without a legend: the channels read the same whatever it turns off.
|
|
165
|
-
const state = Pipeline.#through(upToMapping, this.#seed(props), Pipeline.#
|
|
168
|
+
const state = Pipeline.#through(upToMapping, this.#seed(props), Pipeline.#envFor(cfg, null));
|
|
166
169
|
const channel = (ref) => {
|
|
167
170
|
const name = ref === 'value' || ref === 'category' ? state.axes?.[ref] : ref;
|
|
168
171
|
if (name === undefined)
|
|
@@ -186,11 +189,11 @@ export class Pipeline {
|
|
|
186
189
|
return this.#options.seed ? this.#options.seed(input, rows) : input;
|
|
187
190
|
}
|
|
188
191
|
/** `stages` over `state`, in order. */
|
|
189
|
-
static #through(stages, state,
|
|
190
|
-
return stages.reduce((acc, stage) => stage.run(acc,
|
|
192
|
+
static #through(stages, state, env) {
|
|
193
|
+
return stages.reduce((acc, stage) => stage.run(acc, env), state);
|
|
191
194
|
}
|
|
192
195
|
/** The context a plot-level run reads, outside any core: the configuration and the legend. */
|
|
193
|
-
static #
|
|
196
|
+
static #envFor(cfg, legendInteraction) {
|
|
194
197
|
const interaction = new PlotInteraction(() => cfg, { legend: legendInteraction });
|
|
195
198
|
return { cfg, interaction };
|
|
196
199
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ContextStageOptions } from '../../types/phases/context/phase';
|
|
2
|
+
import type { PhaseStage, ProcessedData } from '../../types/phases';
|
|
3
|
+
import type { PlotAxes } from '../data/PlotAxes';
|
|
4
|
+
import type { Series } from '../../types/plots/data/common';
|
|
5
|
+
/** What the "context" phase reads. */
|
|
6
|
+
type ContextStageState = {
|
|
7
|
+
processedData?: ProcessedData;
|
|
8
|
+
resolvedSeries?: Series<any>[];
|
|
9
|
+
axes?: PlotAxes;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The "context" phase every axis-based plot shares: right after the transforms, it works out once what the plot
|
|
13
|
+
* knows of each processed row - the shared {@link RowContext}, with what the kind adds - and keeps it on the row,
|
|
14
|
+
* for the tooltip, the hover, the value labels and the author's own markers to read.
|
|
15
|
+
*/
|
|
16
|
+
export declare class ContextPhase<TState extends ContextStageState, TEnv = unknown> implements PhaseStage<TState, TEnv> {
|
|
17
|
+
#private;
|
|
18
|
+
readonly phase: "context";
|
|
19
|
+
constructor(kind?: ContextStageOptions<TState, TEnv>);
|
|
20
|
+
run: (state: TState, env: TEnv) => TState;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { RowContext } from './RowContext';
|
|
2
|
+
/**
|
|
3
|
+
* The "context" phase every axis-based plot shares: right after the transforms, it works out once what the plot
|
|
4
|
+
* knows of each processed row - the shared {@link RowContext}, with what the kind adds - and keeps it on the row,
|
|
5
|
+
* for the tooltip, the hover, the value labels and the author's own markers to read.
|
|
6
|
+
*/
|
|
7
|
+
export class ContextPhase {
|
|
8
|
+
phase = 'context';
|
|
9
|
+
#kind;
|
|
10
|
+
constructor(kind) {
|
|
11
|
+
this.#kind = kind;
|
|
12
|
+
}
|
|
13
|
+
run = (state, env) => {
|
|
14
|
+
const rows = state.processedData?.data;
|
|
15
|
+
if (!rows?.length)
|
|
16
|
+
return state;
|
|
17
|
+
const shared = RowContext.shared(state);
|
|
18
|
+
const own = this.#kind?.(state, env);
|
|
19
|
+
for (const row of rows)
|
|
20
|
+
RowContext.tag(row, { ...shared(row), ...own?.(row) });
|
|
21
|
+
return state;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { PlotAxes } from '../data/PlotAxes';
|
|
2
|
+
import type { PlotRowContext } from '../../types/layout/context';
|
|
3
|
+
import type { Series } from '../../types/plots/data/common';
|
|
4
|
+
import type { ProcessedData, ProcessedRow } from '../../types/phases';
|
|
5
|
+
/** What the shared context is worked out from. */
|
|
6
|
+
type ContextSource = {
|
|
7
|
+
processedData?: ProcessedData;
|
|
8
|
+
resolvedSeries?: Series<any>[];
|
|
9
|
+
axes?: PlotAxes;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The context of the rows a plot draws ({@link PlotRowContext}): worked out once per row by the "context" phase,
|
|
13
|
+
* kept on the row itself, and read back from it - or from any copy of it - with {@link of}.
|
|
14
|
+
*/
|
|
15
|
+
export declare class RowContext {
|
|
16
|
+
#private;
|
|
17
|
+
/**
|
|
18
|
+
* What the plot knows of `row`: its series, where it sits, the value it shows and whatever its kind works out
|
|
19
|
+
* for it, with the row itself. A row no plot drew reads as its series alone.
|
|
20
|
+
*/
|
|
21
|
+
static of<TContext extends PlotRowContext = PlotRowContext>(row: unknown): TContext;
|
|
22
|
+
/** Keeps `context` on `row`, for {@link of} to read back. */
|
|
23
|
+
static tag(row: ProcessedRow, context: PlotRowContext): void;
|
|
24
|
+
/** Whether `row` carries a context already. */
|
|
25
|
+
static has(row: unknown): boolean;
|
|
26
|
+
/** A reader of the rows of `state`: the context each carries, else the shared one, for a row no phase tagged. */
|
|
27
|
+
static reader(state: ContextSource): (row: ProcessedRow) => PlotRowContext;
|
|
28
|
+
/**
|
|
29
|
+
* The context every plot gives a processed row: its series; its `x` and `y` - each its series' own value
|
|
30
|
+
* (a stacked row's own, not the running total), else where the transforms put it (a density's estimate, a
|
|
31
|
+
* bin's centre); and its value on the plot's value axis, when that is a number.
|
|
32
|
+
*/
|
|
33
|
+
static shared(state: ContextSource): (row: ProcessedRow) => PlotRowContext;
|
|
34
|
+
}
|
|
35
|
+
export {};
|