@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,15 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AxisPlotEnv, AxisPlotState, PhaseStage } from '../../types/phases';
|
|
2
2
|
type AnyAxisState = AxisPlotState<any, any, any, any>;
|
|
3
3
|
/**
|
|
4
4
|
* The "tooltip" phase every axis-based plot shares: how a hovered row reads,
|
|
5
5
|
* as `tooltipFormat` - the same default on every plot, {@link TooltipState.default} -
|
|
6
6
|
* and the order its rows read in, as `tooltipSort`: highest value first, by
|
|
7
|
-
* each row's
|
|
7
|
+
* the value each row's context gives it (a stacked row's own, not its running total). The value reads as the tooltip's
|
|
8
8
|
* `decimals`, `prefix`, `suffix` and `signDisplay` ask. It
|
|
9
9
|
* runs after the scales, which say which axis holds categories.
|
|
10
10
|
*/
|
|
11
|
-
export declare class TooltipPhase<TState extends AnyAxisState> implements PhaseStage<TState,
|
|
11
|
+
export declare class TooltipPhase<TState extends AnyAxisState> implements PhaseStage<TState, AxisPlotEnv> {
|
|
12
12
|
readonly phase: "tooltip";
|
|
13
|
-
run: (state: TState,
|
|
13
|
+
run: (state: TState, env: AxisPlotEnv) => TState;
|
|
14
14
|
}
|
|
15
15
|
export {};
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { TooltipState } from '../../layout/tooltip/TooltipState.svelte';
|
|
2
|
-
import {
|
|
2
|
+
import { RowContext } from '../context/RowContext';
|
|
3
3
|
import { NumberFormat } from '../../utils/NumberFormat';
|
|
4
4
|
/**
|
|
5
5
|
* The "tooltip" phase every axis-based plot shares: how a hovered row reads,
|
|
6
6
|
* as `tooltipFormat` - the same default on every plot, {@link TooltipState.default} -
|
|
7
7
|
* and the order its rows read in, as `tooltipSort`: highest value first, by
|
|
8
|
-
* each row's
|
|
8
|
+
* the value each row's context gives it (a stacked row's own, not its running total). The value reads as the tooltip's
|
|
9
9
|
* `decimals`, `prefix`, `suffix` and `signDisplay` ask. It
|
|
10
10
|
* runs after the scales, which say which axis holds categories.
|
|
11
11
|
*/
|
|
12
12
|
export class TooltipPhase {
|
|
13
13
|
phase = 'tooltip';
|
|
14
|
-
run = (state,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
run = (state, env) => {
|
|
15
|
+
const contextOf = RowContext.reader(state);
|
|
16
|
+
return {
|
|
17
|
+
...state,
|
|
18
|
+
tooltipFormat: TooltipState.default(state, NumberFormat.maybe(NumberFormat.displayOf(state.tooltip), env.cfg)),
|
|
19
|
+
tooltipSort: TooltipState.descending((row) => contextOf(row).value)
|
|
20
|
+
};
|
|
21
|
+
};
|
|
19
22
|
}
|
|
@@ -18,10 +18,10 @@ export declare const GAP_KIND = "__gapKind";
|
|
|
18
18
|
* `styles.gaps` fills is read at its interpolated value, so it takes its place
|
|
19
19
|
* in a stack the way a real value would, and keeps its gap kind.
|
|
20
20
|
*/
|
|
21
|
-
export declare class TransformsPhase<TState extends TransformsStageState,
|
|
21
|
+
export declare class TransformsPhase<TState extends TransformsStageState, TEnv = unknown> implements PhaseStage<TState, TEnv> {
|
|
22
22
|
#private;
|
|
23
23
|
readonly phase: "transforms";
|
|
24
24
|
/** `connect`: whether a series' visual segments bridge into each other, as the kind's "data" phase splits them. */
|
|
25
|
-
constructor(options?: TransformsStageOptions<TState,
|
|
26
|
-
run: (state: TState,
|
|
25
|
+
constructor(options?: TransformsStageOptions<TState, TEnv>, connect?: boolean);
|
|
26
|
+
run: (state: TState, env: TEnv) => TState;
|
|
27
27
|
}
|
|
@@ -37,7 +37,7 @@ export class TransformsPhase {
|
|
|
37
37
|
this.#options = options;
|
|
38
38
|
this.#connect = connect;
|
|
39
39
|
}
|
|
40
|
-
run = (state,
|
|
40
|
+
run = (state, env) => {
|
|
41
41
|
const options = this.#options;
|
|
42
42
|
const visible = (state.resolvedSeries ?? []);
|
|
43
43
|
// An omitted part of a whole is laid out with the rest, so they keep their places; it is dropped after.
|
|
@@ -66,7 +66,7 @@ export class TransformsPhase {
|
|
|
66
66
|
shared[valueAxis] = (row) => row[GAP_VALUE] ?? read(row);
|
|
67
67
|
}
|
|
68
68
|
const transforms = options.transforms
|
|
69
|
-
? options.transforms(state,
|
|
69
|
+
? options.transforms(state, env)
|
|
70
70
|
: (state.transforms ?? []);
|
|
71
71
|
const rescaled = TransformsPhase.#rescaled(state, transforms, shared, valueAxis);
|
|
72
72
|
if (rescaled)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AxisPlotDefaults } from '../../../types/phases';
|
|
2
|
-
import type { HeatmapPlotState,
|
|
2
|
+
import type { HeatmapPlotState, HeatmapPlotEnv } from '../../../types/plots/correlation/heatmap/state';
|
|
3
3
|
/**
|
|
4
4
|
* HeatmapPlot on the shared pipeline: both axes are categories. A heatmap's
|
|
5
5
|
* `color` is each cell's numeric value, which the "data" phase reads as a
|
|
@@ -8,4 +8,4 @@ import type { HeatmapPlotState, HeatmapPlotCtx } from '../../../types/plots/corr
|
|
|
8
8
|
* cell's tooltip names its coordinates and its value.
|
|
9
9
|
* Its plot defaults to: hover one cell.
|
|
10
10
|
*/
|
|
11
|
-
export declare function buildHeatmapDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<HeatmapPlotState<TData>,
|
|
11
|
+
export declare function buildHeatmapDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<HeatmapPlotState<TData>, HeatmapPlotEnv>;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { ColorRamp } from '../../../utils/color';
|
|
2
|
+
import { HoverDefaults } from '../../../layout/hover/HoverDefaults';
|
|
2
3
|
import { OpacityScale } from '../../../utils/OpacityScale';
|
|
3
4
|
import { ContinuousDomain } from '../../../utils/ContinuousDomain';
|
|
4
5
|
import { buildMarks } from './markers';
|
|
6
|
+
import { RowContext } from '../../../phases/context/RowContext';
|
|
5
7
|
/**
|
|
6
8
|
* HeatmapPlot's own "scales" output, over the band axes the shared phase laid
|
|
7
9
|
* out: the color domain over every cell's value (when `color` is set), and each
|
|
8
10
|
* row's own resolved color and opacity.
|
|
9
11
|
*/
|
|
10
|
-
function extendWithColorScales(state,
|
|
12
|
+
function extendWithColorScales(state, env) {
|
|
11
13
|
const { scales, channels } = state;
|
|
12
|
-
const { range, cfg } =
|
|
14
|
+
const { range, cfg } = env;
|
|
13
15
|
const { color: colorAcc } = channels;
|
|
14
16
|
const rows = (state.processedData?.data ?? []);
|
|
15
17
|
const valueDomain = ContinuousDomain.build(scales.color?.domain, rows.map((d) => Number(colorAcc.accessor(d))), true) ?? [0, 1];
|
|
@@ -68,6 +70,14 @@ export function buildHeatmapDefaults() {
|
|
|
68
70
|
}
|
|
69
71
|
})
|
|
70
72
|
},
|
|
73
|
+
// A cell's value is its color's, not a position's.
|
|
74
|
+
context: (state) => {
|
|
75
|
+
const color = state.channels.color.accessor;
|
|
76
|
+
return ((row) => {
|
|
77
|
+
const value = Number(color(row));
|
|
78
|
+
return { value: Number.isFinite(value) ? value : undefined };
|
|
79
|
+
});
|
|
80
|
+
},
|
|
71
81
|
statistics: { inheritGrouping: false },
|
|
72
82
|
sharedScales: { fromScalesPhase: ['color'] },
|
|
73
83
|
scales: {
|
|
@@ -78,11 +88,16 @@ export function buildHeatmapDefaults() {
|
|
|
78
88
|
styles: { values: { show: true } },
|
|
79
89
|
markers: { marks: buildMarks },
|
|
80
90
|
hover: {
|
|
81
|
-
// A
|
|
91
|
+
// A cell writes its own value: the hover only restyles the cells - the hovered one outlined.
|
|
92
|
+
marker: (_state, env) => HoverDefaults.shapes(env.cfg),
|
|
93
|
+
// A hovered cell reads as its value, as its value labels write it.
|
|
82
94
|
label: (state) => {
|
|
83
|
-
const format = state.values?.format
|
|
84
|
-
const
|
|
85
|
-
return ((row) =>
|
|
95
|
+
const format = state.values?.format;
|
|
96
|
+
const number = (v) => `${v}`;
|
|
97
|
+
return ((row) => {
|
|
98
|
+
const context = RowContext.of(row);
|
|
99
|
+
return format ? format(Number(context.value), number, context) : `${context.value}`;
|
|
100
|
+
});
|
|
86
101
|
},
|
|
87
102
|
// A cell's color is its value, not a series'.
|
|
88
103
|
decorate: () => () => ({ series: undefined, color: undefined })
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HeatmapPlotState,
|
|
1
|
+
import type { HeatmapPlotState, HeatmapPlotEnv } from '../../../types/plots/correlation/heatmap/state';
|
|
2
2
|
import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
|
|
3
3
|
/**
|
|
4
4
|
* HeatmapPlot's "markers" phase: one `'cell'` marker per resolved-style
|
|
@@ -6,4 +6,4 @@ import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
|
|
|
6
6
|
* `fillOpacity`/`strokeWidth` to be constant per mark instance, unlike
|
|
7
7
|
* `fill`, which stays a per-row accessor.
|
|
8
8
|
*/
|
|
9
|
-
export declare function buildMarks<TData extends Record<string, unknown>>(state: HeatmapPlotState<TData>,
|
|
9
|
+
export declare function buildMarks<TData extends Record<string, unknown>>(state: HeatmapPlotState<TData>, env: HeatmapPlotEnv): AxisBasedMarkersConfig[];
|
|
@@ -14,9 +14,9 @@ const OPACITY_STEPS = 32;
|
|
|
14
14
|
* `fillOpacity`/`strokeWidth` to be constant per mark instance, unlike
|
|
15
15
|
* `fill`, which stays a per-row accessor.
|
|
16
16
|
*/
|
|
17
|
-
export function buildMarks(state,
|
|
17
|
+
export function buildMarks(state, env) {
|
|
18
18
|
const { segments, channels, resolveColor, resolveOpacity } = state;
|
|
19
|
-
const { range } =
|
|
19
|
+
const { range } = env;
|
|
20
20
|
const labels = state.valueLabels?.show ? state.valueLabels : undefined;
|
|
21
21
|
const getColor = channels.color.accessor;
|
|
22
22
|
// The cells are the rows the "transforms" phase left, placed by its channels.
|
|
@@ -110,8 +110,9 @@ export function buildMarks(state, ctx) {
|
|
|
110
110
|
borderRadius: group.styles.borderRadius,
|
|
111
111
|
inset: 0.5
|
|
112
112
|
},
|
|
113
|
+
hover: 'style',
|
|
113
114
|
// Each cell writes its own value, in the middle of it.
|
|
114
|
-
label: labels?.shapeLabel(
|
|
115
|
+
label: labels?.shapeLabel(env, (d) => labels.format(Number(getColor(d)), d), {
|
|
115
116
|
fontWeight: 600
|
|
116
117
|
})
|
|
117
118
|
}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { AxisPlotDefaults } from '../../../types/phases';
|
|
2
|
-
import type { ScatterPlotState,
|
|
2
|
+
import type { ScatterPlotState, ScatterPlotEnv } from '../../../types/plots/correlation/scatter/state';
|
|
3
3
|
/** ScatterPlot on the shared pipeline: discrete dots, its own radius-padded scales, and its own markers. Its plot defaults to: hover the exact point. */
|
|
4
|
-
export declare function buildScatterDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<ScatterPlotState<TData>,
|
|
4
|
+
export declare function buildScatterDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<ScatterPlotState<TData>, ScatterPlotEnv>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ProcessedPositions } from '../../../phases/transforms/ProcessedPositions';
|
|
2
|
+
import { HoverDefaults } from '../../../layout/hover/HoverDefaults';
|
|
2
3
|
import { buildMarks, buildValueLabels } from './markers';
|
|
3
4
|
import { Margins } from '../../../layout/margins/Margins';
|
|
4
5
|
import { MarkerAxisValues } from '../../../phases/scales/MarkerAxisValues';
|
|
@@ -25,7 +26,7 @@ function paddedDomain(values, radiusPx, sizePx) {
|
|
|
25
26
|
* the largest dot's radius, and grown to cover what derived markers draw
|
|
26
27
|
* (a density curve's peak, a regression band).
|
|
27
28
|
*/
|
|
28
|
-
function fitDomains(state,
|
|
29
|
+
function fitDomains(state, env) {
|
|
29
30
|
const { scales, channels, groupedSeries } = state;
|
|
30
31
|
const processed = state.processedData;
|
|
31
32
|
const position = ProcessedPositions.of(processed);
|
|
@@ -39,10 +40,10 @@ function fitDomains(state, ctx) {
|
|
|
39
40
|
? r
|
|
40
41
|
: r !== undefined
|
|
41
42
|
? AUTO_SCALED_RADIUS_ESTIMATE
|
|
42
|
-
: Math.max(0, ...groupedSeries.flatMap((group) => group.visualSegments.map((seg) => seg.styles.dotRadius ??
|
|
43
|
+
: Math.max(0, ...groupedSeries.flatMap((group) => group.visualSegments.map((seg) => seg.styles.dotRadius ?? env.cfg.scatter.dotRadius)));
|
|
43
44
|
if (radius <= 0 && !extra.x.length && !extra.y.length)
|
|
44
45
|
return {};
|
|
45
|
-
const box = Margins.resolve({ ...
|
|
46
|
+
const box = Margins.resolve({ ...env.cfg.margins, ...state.margins }).contentBox(state.width, state.height);
|
|
46
47
|
const fit = {};
|
|
47
48
|
for (const [axis, size] of [
|
|
48
49
|
['x', box.width],
|
|
@@ -72,6 +73,11 @@ export function buildScatterDefaults() {
|
|
|
72
73
|
markers: { marks: buildMarks, valueLabels: buildValueLabels },
|
|
73
74
|
hover: {
|
|
74
75
|
strategy: 'punctual',
|
|
76
|
+
// A crosshair through the hovered point, and its label - the point is a dot already.
|
|
77
|
+
marker: () => ({
|
|
78
|
+
back: [HoverDefaults.rule('x'), HoverDefaults.rule('y')],
|
|
79
|
+
front: [HoverDefaults.text()]
|
|
80
|
+
}),
|
|
75
81
|
label: (state) => {
|
|
76
82
|
const { x, y } = state.channels;
|
|
77
83
|
return (row) => `${x.accessor(row)}, ${y.accessor(row)}`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ScatterPlotState,
|
|
1
|
+
import type { ScatterPlotState, ScatterPlotEnv } from '../../../types/plots/correlation/scatter/state';
|
|
2
2
|
import type { TextMarkerConfig } from '../../../types/markers/common';
|
|
3
3
|
import { ValueLabels } from '../../../layout/styles/ValueLabels';
|
|
4
4
|
import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
|
|
@@ -16,4 +16,4 @@ export declare function buildValueLabels<TData extends Record<string, unknown>>(
|
|
|
16
16
|
* "statistics" phase already put in `state.markers` - and its value labels on
|
|
17
17
|
* top of everything.
|
|
18
18
|
*/
|
|
19
|
-
export declare function buildMarks<TData extends Record<string, unknown>>(state: ScatterPlotState<TData>,
|
|
19
|
+
export declare function buildMarks<TData extends Record<string, unknown>>(state: ScatterPlotState<TData>, env: ScatterPlotEnv): AxisBasedMarkersConfig[];
|
|
@@ -21,7 +21,7 @@ export function buildValueLabels(labels, state) {
|
|
|
21
21
|
data: processedData.data,
|
|
22
22
|
x: position.x,
|
|
23
23
|
y: position.y,
|
|
24
|
-
text: (d) => format(Number(valueOf(d)),
|
|
24
|
+
text: (d) => format(Number(valueOf(d)), d, nameOf(d)),
|
|
25
25
|
styles: {
|
|
26
26
|
...font,
|
|
27
27
|
fill: font?.fill ?? 'currentColor',
|
|
@@ -43,9 +43,9 @@ export function buildValueLabels(labels, state) {
|
|
|
43
43
|
* "statistics" phase already put in `state.markers` - and its value labels on
|
|
44
44
|
* top of everything.
|
|
45
45
|
*/
|
|
46
|
-
export function buildMarks(state,
|
|
46
|
+
export function buildMarks(state, env) {
|
|
47
47
|
const { channels, colorForSeries, scales, flat } = state;
|
|
48
|
-
const { cfg, interaction } =
|
|
48
|
+
const { cfg, interaction } = env;
|
|
49
49
|
const r = channels.r;
|
|
50
50
|
const rFn = r === undefined || typeof r === 'number' ? r : r.accessor;
|
|
51
51
|
const opacityFn = channels.opacity?.accessor;
|
|
@@ -98,6 +98,7 @@ export function buildMarks(state, ctx) {
|
|
|
98
98
|
markers.push({
|
|
99
99
|
type: 'dot',
|
|
100
100
|
data: seg.data,
|
|
101
|
+
hover: 'style',
|
|
101
102
|
// Forwarded raw (a field key or function, whatever `series.x`/`.y`
|
|
102
103
|
// is) rather than pre-resolved into a closure - `DotMarker.svelte`
|
|
103
104
|
// and svelteplot's own `resolve()` already handle a field-key
|
|
@@ -1,19 +1,28 @@
|
|
|
1
|
-
import type { HoverPosition } from '../../../types/phases';
|
|
1
|
+
import type { HoverPosition, ProcessedRow } from '../../../types/phases';
|
|
2
2
|
import type { BoxPlotState, BoxSummaryRow } from '../../../types/plots/distribution/box/state';
|
|
3
|
+
import type { BoxSummary } from '../../../types/plots/distribution/box/props';
|
|
3
4
|
/**
|
|
4
|
-
* The boxes a box plot draws, one
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* The boxes a box plot draws, one per series, category and facet panel - the same quartiles and whiskers
|
|
6
|
+
* svelteplot's `BoxX`/`BoxY` compute, worked out once as each value's context ({@link context}), so its hover and
|
|
7
|
+
* tooltip read a box rather than every value inside it ({@link of}).
|
|
7
8
|
*/
|
|
8
9
|
export declare class BoxSummaries {
|
|
9
|
-
|
|
10
|
+
#private;
|
|
11
|
+
/**
|
|
12
|
+
* Each value's context: the summary of the box it is in - its category, quartiles, whisker ends (the farthest
|
|
13
|
+
* values within 1.5 IQR of the box), how many values it has and the ones past the whiskers.
|
|
14
|
+
*/
|
|
15
|
+
static context<TData extends Record<string, unknown>>(state: BoxPlotState<TData>): (row: ProcessedRow) => BoxSummary | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* One summary row per box, in the order the processed rows first reach it, read off its first value's context.
|
|
18
|
+
* Its own context is the box's, with the median for its value, and where it sits for `x` and `y`.
|
|
19
|
+
*/
|
|
10
20
|
static of<TData extends Record<string, unknown>>(state: BoxPlotState<TData>): BoxSummaryRow[];
|
|
11
21
|
/** Where a box's summary sits for hover: at its category, level with its median. */
|
|
12
22
|
static position<TData extends Record<string, unknown>>(state: BoxPlotState<TData>): HoverPosition;
|
|
13
23
|
/**
|
|
14
|
-
* How a hovered box reads by default: its series, when the plot groups its
|
|
15
|
-
*
|
|
16
|
-
* values fall past the whiskers.
|
|
24
|
+
* How a hovered box reads by default, from its context: its series, when the plot groups its rows; its
|
|
25
|
+
* category; then its five numbers, highest first, and how many values fall past the whiskers.
|
|
17
26
|
*/
|
|
18
27
|
static format<TData extends Record<string, unknown>>(state: BoxPlotState<TData>): (row: BoxSummaryRow) => string;
|
|
19
28
|
}
|
|
@@ -4,28 +4,37 @@ import { SERIES_ID_KEY, SERIES_KEY } from '../../../phases/transforms/Transforms
|
|
|
4
4
|
import { PANEL_KEY } from '../../../phases/facet/FacetPanels';
|
|
5
5
|
import { PlotAxes } from '../../../phases/data/PlotAxes';
|
|
6
6
|
import { TooltipState } from '../../../layout/tooltip/TooltipState.svelte';
|
|
7
|
+
import { RowContext } from '../../../phases/context/RowContext';
|
|
7
8
|
import { isGapValue } from '../../utils/gaps';
|
|
8
9
|
/** The whiskers reach this many interquartile ranges past the box, as svelteplot draws them. */
|
|
9
10
|
const WHISKER_IQR = 1.5;
|
|
10
11
|
/** A summary value as a tooltip shows it: at most two decimals. */
|
|
11
12
|
const round = (value) => String(Math.round(value * 100) / 100);
|
|
12
13
|
/**
|
|
13
|
-
* The boxes a box plot draws, one
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* The boxes a box plot draws, one per series, category and facet panel - the same quartiles and whiskers
|
|
15
|
+
* svelteplot's `BoxX`/`BoxY` compute, worked out once as each value's context ({@link context}), so its hover and
|
|
16
|
+
* tooltip read a box rather than every value inside it ({@link of}).
|
|
16
17
|
*/
|
|
17
18
|
export class BoxSummaries {
|
|
18
|
-
/**
|
|
19
|
-
static
|
|
19
|
+
/** Which box a processed row is in: its series, panel and category. */
|
|
20
|
+
static #keyOf(state) {
|
|
21
|
+
const categoryAxis = PlotAxes.categoryOf(state);
|
|
22
|
+
const categoryOf = ProcessedPositions.of(state.processedData, PlotAxes.valueOf(state))[categoryAxis];
|
|
23
|
+
return (row) => JSON.stringify([row[SERIES_ID_KEY], row[PANEL_KEY] ?? null, String(categoryOf(row))]);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Each value's context: the summary of the box it is in - its category, quartiles, whisker ends (the farthest
|
|
27
|
+
* values within 1.5 IQR of the box), how many values it has and the ones past the whiskers.
|
|
28
|
+
*/
|
|
29
|
+
static context(state) {
|
|
20
30
|
const processed = state.processedData;
|
|
21
31
|
if (!processed?.data.length)
|
|
22
|
-
return
|
|
32
|
+
return () => undefined;
|
|
23
33
|
const valueAxis = PlotAxes.valueOf(state);
|
|
24
|
-
const categoryAxis = PlotAxes.categoryOf(state);
|
|
25
34
|
const position = ProcessedPositions.of(processed, valueAxis);
|
|
26
35
|
const valueOf = position[valueAxis];
|
|
27
|
-
const categoryOf = position[
|
|
28
|
-
const
|
|
36
|
+
const categoryOf = position[PlotAxes.categoryOf(state)];
|
|
37
|
+
const keyOf = BoxSummaries.#keyOf(state);
|
|
29
38
|
const boxes = new Map();
|
|
30
39
|
for (const row of processed.data) {
|
|
31
40
|
const value = valueOf(row);
|
|
@@ -34,39 +43,80 @@ export class BoxSummaries {
|
|
|
34
43
|
const n = Number(value);
|
|
35
44
|
if (!Number.isFinite(n))
|
|
36
45
|
continue;
|
|
37
|
-
const key =
|
|
38
|
-
row[SERIES_ID_KEY],
|
|
39
|
-
row[PANEL_KEY] ?? null,
|
|
40
|
-
String(categoryOf(row))
|
|
41
|
-
]);
|
|
46
|
+
const key = keyOf(row);
|
|
42
47
|
const box = boxes.get(key);
|
|
43
48
|
if (box)
|
|
44
49
|
box.values.push(n);
|
|
45
50
|
else
|
|
46
|
-
boxes.set(key, {
|
|
51
|
+
boxes.set(key, { category: categoryOf(row), values: [n] });
|
|
52
|
+
}
|
|
53
|
+
const summaries = new Map([...boxes].map(([key, { category, values }]) => [
|
|
54
|
+
key,
|
|
55
|
+
BoxSummaries.#summarize(category, values)
|
|
56
|
+
]));
|
|
57
|
+
return (row) => summaries.get(keyOf(row));
|
|
58
|
+
}
|
|
59
|
+
static #summarize(category, values) {
|
|
60
|
+
const sorted = values.sort((a, b) => a - b);
|
|
61
|
+
const p25 = quantile(sorted, 0.25);
|
|
62
|
+
const median = quantile(sorted, 0.5);
|
|
63
|
+
const p75 = quantile(sorted, 0.75);
|
|
64
|
+
const reach = (p75 - p25) * WHISKER_IQR;
|
|
65
|
+
const inside = sorted.filter((v) => v >= p25 - reach && v <= p75 + reach);
|
|
66
|
+
return {
|
|
67
|
+
category,
|
|
68
|
+
min: inside[0] ?? p25,
|
|
69
|
+
p25,
|
|
70
|
+
median,
|
|
71
|
+
p75,
|
|
72
|
+
max: inside.at(-1) ?? p75,
|
|
73
|
+
count: sorted.length,
|
|
74
|
+
outliers: sorted.filter((v) => v < p25 - reach || v > p75 + reach)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* One summary row per box, in the order the processed rows first reach it, read off its first value's context.
|
|
79
|
+
* Its own context is the box's, with the median for its value, and where it sits for `x` and `y`.
|
|
80
|
+
*/
|
|
81
|
+
static of(state) {
|
|
82
|
+
const processed = state.processedData;
|
|
83
|
+
if (!processed?.data.length)
|
|
84
|
+
return [];
|
|
85
|
+
const valueAxis = PlotAxes.valueOf(state);
|
|
86
|
+
const categoryAxis = PlotAxes.categoryOf(state);
|
|
87
|
+
const categoryField = processed.channels[categoryAxis];
|
|
88
|
+
const keyOf = BoxSummaries.#keyOf(state);
|
|
89
|
+
const firsts = new Map();
|
|
90
|
+
for (const row of processed.data) {
|
|
91
|
+
const key = keyOf(row);
|
|
92
|
+
if (!firsts.has(key) && RowContext.of(row).median !== undefined)
|
|
93
|
+
firsts.set(key, row);
|
|
47
94
|
}
|
|
48
|
-
return [...
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
const median
|
|
52
|
-
const
|
|
53
|
-
const reach = (p75 - p25) * WHISKER_IQR;
|
|
54
|
-
const inside = sorted.filter((v) => v >= p25 - reach && v <= p75 + reach);
|
|
55
|
-
const category = categoryOf(first);
|
|
56
|
-
return {
|
|
95
|
+
return [...firsts.values()].map((first) => {
|
|
96
|
+
// The box's context, not its first value's row.
|
|
97
|
+
const context = { ...RowContext.of(first), row: undefined };
|
|
98
|
+
const { category, min, p25, median, p75, max, count, outliers } = context;
|
|
99
|
+
const summary = {
|
|
57
100
|
...(typeof categoryField === 'string' ? { [categoryField]: category } : {}),
|
|
58
101
|
[SERIES_KEY]: first[SERIES_KEY],
|
|
59
102
|
[SERIES_ID_KEY]: first[SERIES_ID_KEY],
|
|
60
103
|
...(first[PANEL_KEY] !== undefined ? { [PANEL_KEY]: first[PANEL_KEY] } : {}),
|
|
61
104
|
category,
|
|
62
|
-
min
|
|
105
|
+
min,
|
|
63
106
|
p25,
|
|
64
107
|
median,
|
|
65
108
|
p75,
|
|
66
|
-
max
|
|
67
|
-
count
|
|
68
|
-
outliers
|
|
109
|
+
max,
|
|
110
|
+
count,
|
|
111
|
+
outliers
|
|
69
112
|
};
|
|
113
|
+
RowContext.tag(summary, {
|
|
114
|
+
...context,
|
|
115
|
+
[valueAxis]: median,
|
|
116
|
+
[categoryAxis]: category,
|
|
117
|
+
value: median
|
|
118
|
+
});
|
|
119
|
+
return summary;
|
|
70
120
|
});
|
|
71
121
|
}
|
|
72
122
|
/** Where a box's summary sits for hover: at its category, level with its median. */
|
|
@@ -79,22 +129,22 @@ export class BoxSummaries {
|
|
|
79
129
|
return at;
|
|
80
130
|
}
|
|
81
131
|
/**
|
|
82
|
-
* How a hovered box reads by default: its series, when the plot groups its
|
|
83
|
-
*
|
|
84
|
-
* values fall past the whiskers.
|
|
132
|
+
* How a hovered box reads by default, from its context: its series, when the plot groups its rows; its
|
|
133
|
+
* category; then its five numbers, highest first, and how many values fall past the whiskers.
|
|
85
134
|
*/
|
|
86
135
|
static format(state) {
|
|
87
136
|
const grouped = state.channels?.grouping !== undefined;
|
|
88
137
|
return (row) => {
|
|
89
|
-
const
|
|
90
|
-
lines
|
|
91
|
-
lines.push(
|
|
92
|
-
lines.push(`
|
|
93
|
-
lines.push(`
|
|
94
|
-
lines.push(`
|
|
95
|
-
lines.push(`
|
|
96
|
-
|
|
97
|
-
|
|
138
|
+
const box = RowContext.of(row);
|
|
139
|
+
const lines = grouped ? [String(box.series)] : [];
|
|
140
|
+
lines.push(TooltipState.value(box.category));
|
|
141
|
+
lines.push(`max: ${round(box.max)}`);
|
|
142
|
+
lines.push(`p75: ${round(box.p75)}`);
|
|
143
|
+
lines.push(`median: ${round(box.median)}`);
|
|
144
|
+
lines.push(`p25: ${round(box.p25)}`);
|
|
145
|
+
lines.push(`min: ${round(box.min)}`);
|
|
146
|
+
if (box.outliers.length)
|
|
147
|
+
lines.push(`outliers: ${box.outliers.length}`);
|
|
98
148
|
return lines.join('\n');
|
|
99
149
|
};
|
|
100
150
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AxisPlotDefaults } from '../../../types/phases';
|
|
2
|
-
import type { BoxPlotState,
|
|
2
|
+
import type { BoxPlotState, BoxPlotEnv } from '../../../types/plots/distribution/box/state';
|
|
3
3
|
/**
|
|
4
4
|
* BoxPlot on the shared pipeline: one box per category of each series along
|
|
5
5
|
* one category axis - `x`, or `y` for horizontal boxes, as the "data" phase
|
|
@@ -9,4 +9,4 @@ import type { BoxPlotState, BoxPlotCtx } from '../../../types/plots/distribution
|
|
|
9
9
|
* hover along the category axis, over one summary per box rather than every
|
|
10
10
|
* value inside it.
|
|
11
11
|
*/
|
|
12
|
-
export declare function buildBoxDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<BoxPlotState<TData>,
|
|
12
|
+
export declare function buildBoxDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<BoxPlotState<TData>, BoxPlotEnv>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { buildMarks } from './markers';
|
|
2
|
+
import { PlotAxes } from '../../../phases/data/PlotAxes';
|
|
3
|
+
import { HoverDefaults } from '../../../layout/hover/HoverDefaults';
|
|
2
4
|
import { BoxSummaries } from './BoxSummaries';
|
|
3
|
-
import { TooltipState } from '../../../layout/tooltip/TooltipState.svelte';
|
|
4
5
|
/**
|
|
5
6
|
* BoxPlot on the shared pipeline: one box per category of each series along
|
|
6
7
|
* one category axis - `x`, or `y` for horizontal boxes, as the "data" phase
|
|
@@ -22,17 +23,19 @@ export function buildBoxDefaults() {
|
|
|
22
23
|
},
|
|
23
24
|
scales: {
|
|
24
25
|
bandOrder: 'data',
|
|
25
|
-
defaults: (_state,
|
|
26
|
-
category: { paddingInner:
|
|
26
|
+
defaults: (_state, env) => ({
|
|
27
|
+
category: { paddingInner: env.cfg.box.paddingInner, paddingOuter: env.cfg.box.paddingOuter }
|
|
27
28
|
})
|
|
28
29
|
},
|
|
30
|
+
// Each value's box: its quartiles and whiskers, worked out once for its tooltip and hover to read.
|
|
31
|
+
context: (state) => BoxSummaries.context(state),
|
|
29
32
|
markers: { marks: buildMarks },
|
|
30
33
|
hover: {
|
|
34
|
+
// The hovered category's band behind its shape.
|
|
35
|
+
marker: (state) => HoverDefaults.behindBand(PlotAxes.other(PlotAxes.valueOf(state))),
|
|
31
36
|
rows: BoxSummaries.of,
|
|
32
37
|
position: BoxSummaries.position,
|
|
33
|
-
|
|
34
|
-
format: (state) => BoxSummaries.format(state),
|
|
35
|
-
sort: () => TooltipState.descending((row) => row.median)
|
|
38
|
+
format: (state) => BoxSummaries.format(state)
|
|
36
39
|
}
|
|
37
40
|
};
|
|
38
41
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BoxPlotState,
|
|
1
|
+
import type { BoxPlotState, BoxPlotEnv } from '../../../types/plots/distribution/box/state';
|
|
2
2
|
import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
|
|
3
3
|
/**
|
|
4
4
|
* BoxPlot's "markers" phase: one `box` per visual segment of each series,
|
|
@@ -9,4 +9,4 @@ import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
|
|
|
9
9
|
* A series toggled off in the legend (style mode), or dimmed by hovering
|
|
10
10
|
* another one, takes the legend's disabled fill.
|
|
11
11
|
*/
|
|
12
|
-
export declare function buildMarks<TData extends Record<string, unknown>>(state: BoxPlotState<TData>,
|
|
12
|
+
export declare function buildMarks<TData extends Record<string, unknown>>(state: BoxPlotState<TData>, env: BoxPlotEnv): AxisBasedMarkersConfig[];
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
* A series toggled off in the legend (style mode), or dimmed by hovering
|
|
8
8
|
* another one, takes the legend's disabled fill.
|
|
9
9
|
*/
|
|
10
|
-
export function buildMarks(state,
|
|
10
|
+
export function buildMarks(state, env) {
|
|
11
11
|
const { channels } = state.processedData;
|
|
12
|
-
const { cfg } =
|
|
12
|
+
const { cfg } = env;
|
|
13
13
|
return (state.processedGroups ?? []).flatMap((group) => {
|
|
14
14
|
const name = group.series.name;
|
|
15
|
-
const disabled =
|
|
15
|
+
const disabled = env.interaction?.fillFor(name);
|
|
16
16
|
return group.visualSegments.map((seg) => {
|
|
17
17
|
const { stroke, strokeWidth, strokeOpacity, borderRadius } = seg.styles;
|
|
18
18
|
const fill = disabled?.fill ?? seg.styles.fill ?? state.colorForSeries(name);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { AxisPlotDefaults } from '../../../types/phases';
|
|
2
|
-
import type { DensityPlotState,
|
|
2
|
+
import type { DensityPlotState, DensityPlotEnv } from '../../../types/plots/distribution/density/state';
|
|
3
3
|
/**
|
|
4
4
|
* DensityPlot on the shared pipeline: series read one position channel - `x`,
|
|
5
5
|
* or `y` for a horizontal curve - estimated by the plot's transforms merged
|
|
6
6
|
* over the default density of that axis, drawn from a zero baseline as an area
|
|
7
7
|
* or a line, and its own markers. Its plot defaults to: hover along the estimated axis.
|
|
8
8
|
*/
|
|
9
|
-
export declare function buildDensityDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<DensityPlotState<TData>,
|
|
9
|
+
export declare function buildDensityDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<DensityPlotState<TData>, DensityPlotEnv>;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { buildMarks } from './markers';
|
|
2
|
+
import { RowContext } from '../../../phases/context/RowContext';
|
|
3
|
+
import { TooltipState } from '../../../layout/tooltip/TooltipState.svelte';
|
|
4
|
+
import { NumberFormat } from '../../../utils/NumberFormat';
|
|
5
|
+
import { getConfiguration } from '../../../configuration/config.svelte';
|
|
6
|
+
/** A density is a small number: three decimals tell its points apart. */
|
|
7
|
+
const DENSITY_DECIMALS = 3;
|
|
2
8
|
/**
|
|
3
9
|
* The plot's own density along its axis merged over the default one, the
|
|
4
10
|
* author's props winning, and every other transform kept after it in order. A
|
|
@@ -30,6 +36,16 @@ export function buildDensityDefaults() {
|
|
|
30
36
|
transforms: (state) => mergeDensity(state.transforms ?? [], state.axes?.isHorizontal ?? false)
|
|
31
37
|
},
|
|
32
38
|
scales: { zero: ['value'] },
|
|
33
|
-
markers: { marks: buildMarks }
|
|
39
|
+
markers: { marks: buildMarks },
|
|
40
|
+
hover: {
|
|
41
|
+
// A hovered point reads as its estimate, the value its context gives it - a small number, written with
|
|
42
|
+
// three decimals unless the author asks otherwise.
|
|
43
|
+
label: (state) => {
|
|
44
|
+
const number = NumberFormat.of(NumberFormat.displayOf(state.values), getConfiguration(), DENSITY_DECIMALS);
|
|
45
|
+
return ((row) => number(Number(RowContext.of(row).value)));
|
|
46
|
+
},
|
|
47
|
+
// Its tooltip writes the estimate the same way.
|
|
48
|
+
format: (state) => TooltipState.default(state, NumberFormat.of(NumberFormat.displayOf(state.tooltip), getConfiguration(), DENSITY_DECIMALS))
|
|
49
|
+
}
|
|
34
50
|
};
|
|
35
51
|
}
|