@fundar/data-chart-telling 0.0.39 → 0.0.41
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/plots/bar/Plot.svelte +275 -249
- package/dist/plots/line/Plot.svelte +227 -182
- package/dist/plots/line/Plot.svelte.d.ts +2 -2
- package/dist/plots/pyramid/Plot.svelte +288 -260
- package/dist/plots/scatter/Plot.svelte +266 -242
- package/dist/plots/utils/labelOverflow.svelte.d.ts +17 -1
- package/dist/plots/utils/labelOverflow.svelte.js +44 -8
- package/package.json +1 -1
|
@@ -1,291 +1,319 @@
|
|
|
1
1
|
<script lang="ts" generics="TData extends Record<string, unknown>">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
2
|
+
import { resolveAccessor } from '../utils/accessors';
|
|
3
|
+
import { getConfiguration } from '../../configuration/config.svelte';
|
|
4
|
+
import { paletteColor } from '../../utils/color';
|
|
5
|
+
import { buildSeries } from '../../utils/grouping';
|
|
6
|
+
import { getLegendInteraction } from '../../layout/legend/interaction.svelte';
|
|
7
|
+
import { disabledFillOverride } from '../utils/legendDisabled';
|
|
8
|
+
import { buildPyramidBarMarkers } from './buildPyramidBarMarkers';
|
|
9
|
+
import { createLabelMarginTracker } from '../utils/labelOverflow.svelte';
|
|
10
|
+
import {
|
|
11
|
+
buildGroupedSeries,
|
|
12
|
+
validateSegments,
|
|
13
|
+
buildScopedMarkerGroups
|
|
14
|
+
} from '../utils/segments';
|
|
15
|
+
import { isGapValue, resolveGapsForSeries, resolveDiscreteGapFills } from '../utils/gaps';
|
|
16
|
+
import BasePlotLayout from '../../layout/plot/BasePlotLayout.svelte';
|
|
17
|
+
import ValueLabels from './ValueLabels.svelte';
|
|
18
|
+
import {
|
|
19
|
+
hasHoverMarker,
|
|
20
|
+
resolveHoverStrategy,
|
|
21
|
+
resolveHoverSync
|
|
22
|
+
} from '../../layout/tooltip/utils';
|
|
23
|
+
import type { BasePlotProps } from '../../types/plots/props';
|
|
24
|
+
import type { AxisValue, AxisBasedScalesConfig } from '../../types/layout/scales';
|
|
25
|
+
import type { Series, SeriesProps, DataProps } from '../../types/plots/data/common';
|
|
26
|
+
import type { GapsAwarePlotStylesConfig } from '../../types/layout/styles';
|
|
27
|
+
import type { ValueAnchor } from '../../types/plots/constants';
|
|
28
|
+
import type { BarSegmentStyle } from '../../types/plots/segments/common';
|
|
29
|
+
import type { AxisBasedMarkersConfig } from '../../types/markers/common';
|
|
30
|
+
import type { LegendDisabledStyle } from '../../types/layout/legend';
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
/**
|
|
33
|
+
* A population-pyramid-style diverging bar chart: each series is mirrored
|
|
34
|
+
* left or right of a shared zero baseline along a categorical band axis.
|
|
35
|
+
* Mirrors the bar/line plot contract — series in, marks out — so the
|
|
36
|
+
* chart-level container/timeline/facet/legend wrap it unchanged. Like
|
|
37
|
+
* `LinePlot`/`BarPlot`, accepts either pre-built `series` or flat `data` +
|
|
38
|
+
* `x`/`y`/`z` accessors.
|
|
39
|
+
*
|
|
40
|
+
* Side assignment is automatic: series names are sorted alphabetically and
|
|
41
|
+
* the first goes left, the second right. Pass `scales.z.reverse: true` to
|
|
42
|
+
* flip the order.
|
|
43
|
+
*/
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
type PyramidSide = 'left' | 'right';
|
|
46
|
+
type InternalSeries = Series<TData> & { side: PyramidSide };
|
|
39
47
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
type Props = BasePlotProps<
|
|
49
|
+
SeriesProps<TData> | DataProps<TData>,
|
|
50
|
+
TData,
|
|
51
|
+
GapsAwarePlotStylesConfig<BarSegmentStyle>,
|
|
52
|
+
BarSegmentStyle,
|
|
53
|
+
AxisBasedMarkersConfig,
|
|
54
|
+
AxisBasedScalesConfig<TData>
|
|
55
|
+
>;
|
|
48
56
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
let {
|
|
58
|
+
width,
|
|
59
|
+
height,
|
|
60
|
+
series,
|
|
61
|
+
data,
|
|
62
|
+
x: magnitude,
|
|
63
|
+
y: category,
|
|
64
|
+
z: sides,
|
|
65
|
+
styles = {},
|
|
66
|
+
scales = {},
|
|
67
|
+
margins,
|
|
68
|
+
segments = {},
|
|
69
|
+
markers = [],
|
|
70
|
+
tooltip = undefined
|
|
71
|
+
}: Props = $props();
|
|
64
72
|
|
|
65
|
-
|
|
73
|
+
$effect(() => {
|
|
74
|
+
validateSegments(segments);
|
|
75
|
+
});
|
|
66
76
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
const cfg = $derived(getConfiguration());
|
|
78
|
+
const legendInteraction = $derived(getLegendInteraction());
|
|
79
|
+
const hoverIsolate = $derived(styles.values?.hoverIsolate ?? false);
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
81
|
+
// A value label hovered while `styles.values.hoverIsolate` is on dims
|
|
82
|
+
// every other series exactly like a legend toggle would — local to this
|
|
83
|
+
// one `<Plot>` instance (not the shared HoverStore) so the gesture can't
|
|
84
|
+
// leak into unrelated tooltip/crosshair behavior.
|
|
85
|
+
let hoverIsolatedSeries = $state<string | null>(null);
|
|
76
86
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
function effectiveDisabledFor(name: string): LegendDisabledStyle | undefined {
|
|
88
|
+
return (
|
|
89
|
+
legendInteraction?.disabledSeries.get(name) ??
|
|
90
|
+
(hoverIsolate && hoverIsolatedSeries && hoverIsolatedSeries !== name
|
|
91
|
+
? { opacity: cfg.legend.disabledOpacity }
|
|
92
|
+
: undefined)
|
|
93
|
+
);
|
|
94
|
+
}
|
|
85
95
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
const leftColor = $derived(styles.colors?.left ?? paletteColor(0, cfg.palette));
|
|
97
|
+
const rightColor = $derived(styles.colors?.right ?? paletteColor(1, cfg.palette));
|
|
98
|
+
const showVals = $derived(styles.values?.show ?? false);
|
|
99
|
+
const formatValue = $derived(styles.values?.format ?? ((v: number) => `${v}`));
|
|
100
|
+
const valAnchor = $derived<ValueAnchor>(styles.values?.anchor ?? 'outside');
|
|
91
101
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
// Sort series names alphabetically; even index → left, odd → right.
|
|
103
|
+
// scales.z.reverse flips the assignment so the second name goes left.
|
|
104
|
+
const zReversed = $derived(scales.z?.reverse ?? false);
|
|
105
|
+
const resolvedSeries = $derived.by((): InternalSeries[] => {
|
|
106
|
+
const base: Series<TData>[] = series ?? buildSeries(data!, category!, magnitude!, sides);
|
|
107
|
+
const sortedNames = [...base.map((s) => s.name)].sort((a, b) => a.localeCompare(b));
|
|
108
|
+
return base.map((s) => {
|
|
109
|
+
const idx = sortedNames.indexOf(s.name);
|
|
110
|
+
const side: PyramidSide = (idx % 2 === 0) !== zReversed ? 'left' : 'right';
|
|
111
|
+
return { ...s, side };
|
|
112
|
+
});
|
|
113
|
+
});
|
|
104
114
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
const groupedSeries = $derived(
|
|
116
|
+
// connect: false — bars are discrete; there's no visual gap between
|
|
117
|
+
// adjacent categories to bridge (see buildGroupedSeries's own doc).
|
|
118
|
+
buildGroupedSeries<InternalSeries, BarSegmentStyle>(resolvedSeries, segments, false)
|
|
119
|
+
);
|
|
110
120
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
function signedValue(s: InternalSeries, d: TData): number {
|
|
122
|
+
const v = Math.abs(Number(resolveAccessor(s.y)(d)));
|
|
123
|
+
return s.side === 'left' ? -v : v;
|
|
124
|
+
}
|
|
115
125
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
126
|
+
// Signs an already-resolved magnitude (e.g. a gap-fill's interpolated
|
|
127
|
+
// value, which has no row of its own to read via `signedValue`).
|
|
128
|
+
function signFor(s: InternalSeries, magnitude: number): number {
|
|
129
|
+
return s.side === 'left' ? -magnitude : magnitude;
|
|
130
|
+
}
|
|
121
131
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
// Fills leading/internal/trailing missing-data holes with an
|
|
133
|
+
// interpolated, distinctly-styled bar, resolved per series like
|
|
134
|
+
// `segments` — see `GapsAwarePlotStylesConfig` (`types/layout/styles.ts`).
|
|
135
|
+
const gaps = $derived(styles.gaps ?? {});
|
|
136
|
+
const gapFillMaps = $derived(
|
|
137
|
+
new Map(
|
|
138
|
+
resolvedSeries.map((s) => [
|
|
139
|
+
s.name,
|
|
140
|
+
resolveDiscreteGapFills(s, resolveGapsForSeries(gaps, s.name))
|
|
141
|
+
])
|
|
142
|
+
)
|
|
143
|
+
);
|
|
129
144
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
145
|
+
// svelteplot's `Pointer` rebuilds its quadtree from shallow-cloned rows
|
|
146
|
+
// (`{ ...d }`), never the original references, so a row's owning series
|
|
147
|
+
// can't be recovered by identity once hover is active. Pre-compute
|
|
148
|
+
// everything `getX`/`getY`/`labelFor` need as plain enumerable properties —
|
|
149
|
+
// they survive the clone automatically.
|
|
150
|
+
type TaggedRow = TData & {
|
|
151
|
+
__side: string;
|
|
152
|
+
__x: number;
|
|
153
|
+
__y: AxisValue;
|
|
154
|
+
__label: string;
|
|
155
|
+
__series: string;
|
|
156
|
+
};
|
|
142
157
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
const flat = $derived.by((): TaggedRow[] =>
|
|
159
|
+
resolvedSeries.flatMap((s) => {
|
|
160
|
+
const getCategory = resolveAccessor(s.x);
|
|
161
|
+
const getMagnitude = resolveAccessor(s.y);
|
|
162
|
+
return s.data
|
|
163
|
+
.filter((d) => !isGapValue(getMagnitude(d)))
|
|
164
|
+
.map((d) => {
|
|
165
|
+
const v = Math.abs(Number(getMagnitude(d)));
|
|
166
|
+
return {
|
|
167
|
+
...d,
|
|
168
|
+
__side: s.side,
|
|
169
|
+
__x: s.side === 'left' ? -v : v,
|
|
170
|
+
__y: getCategory(d) as AxisValue,
|
|
171
|
+
__label: formatValue(v, s.name),
|
|
172
|
+
__series: s.name
|
|
173
|
+
} as TaggedRow;
|
|
174
|
+
});
|
|
175
|
+
})
|
|
176
|
+
);
|
|
162
177
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
178
|
+
const getX = (d: TaggedRow) => d.__x;
|
|
179
|
+
const getY = (d: TaggedRow) => d.__y;
|
|
180
|
+
const labelFor = (d: TaggedRow) => d.__label;
|
|
166
181
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
182
|
+
// A legend-disabled series is dimmed on the bar itself, but never surfaces
|
|
183
|
+
// in hover/tooltip — its points are simply absent from the candidate list.
|
|
184
|
+
const hoverPoints = $derived(
|
|
185
|
+
flat
|
|
186
|
+
.filter((d) => !legendInteraction?.disabledSeries.has(d.__series))
|
|
187
|
+
.map((d) => ({
|
|
188
|
+
x: d.__x,
|
|
189
|
+
y: d.__y,
|
|
190
|
+
row: d,
|
|
191
|
+
label: d.__label,
|
|
192
|
+
series: d.__series,
|
|
193
|
+
color: d.__side === 'left' ? leftColor : rightColor
|
|
194
|
+
}))
|
|
195
|
+
);
|
|
181
196
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
197
|
+
const yDomain = $derived([
|
|
198
|
+
...new Set(
|
|
199
|
+
resolvedSeries.flatMap((s) => {
|
|
200
|
+
const getCategory = resolveAccessor(s.x);
|
|
201
|
+
return s.data.map((d) => getCategory(d) as AxisValue);
|
|
202
|
+
})
|
|
203
|
+
)
|
|
204
|
+
]);
|
|
190
205
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
206
|
+
const maxMagnitude = $derived.by(() => {
|
|
207
|
+
const vals = resolvedSeries.flatMap((s) => {
|
|
208
|
+
const getMagnitude = resolveAccessor(s.y);
|
|
209
|
+
const fillMap = gapFillMaps.get(s.name);
|
|
210
|
+
return s.data.flatMap((d) => {
|
|
211
|
+
const raw = getMagnitude(d);
|
|
212
|
+
if (!isGapValue(raw)) return [Math.abs(Number(raw))];
|
|
213
|
+
const fill = fillMap?.get(d);
|
|
214
|
+
return fill ? [Math.abs(fill.value)] : [];
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
return vals.length ? Math.max(...vals) : 1;
|
|
218
|
+
});
|
|
204
219
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
220
|
+
// The single, fully-resolved scales object BasePlotLayout reads for
|
|
221
|
+
// everything (ticks, grid, sort, and the real svelteplot scale) — avoids
|
|
222
|
+
// keeping a second, separately-defaulted copy of `scales.x`/`scales.y`
|
|
223
|
+
// (our own diverging-domain + band-scale defaults) out of sync with the
|
|
224
|
+
// one actually forwarded.
|
|
225
|
+
const resolvedScales = $derived<AxisBasedScalesConfig<TData>>({
|
|
226
|
+
...scales,
|
|
227
|
+
x: {
|
|
228
|
+
domain: [-maxMagnitude, maxMagnitude],
|
|
229
|
+
tickFormat: (v: unknown) => formatValue(Math.abs(Number(v)), ''),
|
|
230
|
+
...(scales.x ?? {})
|
|
231
|
+
},
|
|
232
|
+
y: { type: 'band', padding: 0.2, domain: yDomain, ...(scales.y ?? {}) }
|
|
233
|
+
});
|
|
219
234
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
235
|
+
const hover = $derived({
|
|
236
|
+
active: tooltip != null || hasHoverMarker(markers),
|
|
237
|
+
points: hoverPoints,
|
|
238
|
+
label: labelFor,
|
|
239
|
+
strategy: resolveHoverStrategy(tooltip?.strategy, markers, 'y'),
|
|
240
|
+
sync: resolveHoverSync(tooltip?.sync, markers),
|
|
241
|
+
tooltip
|
|
242
|
+
});
|
|
228
243
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
244
|
+
const pyramidMarkers = $derived(
|
|
245
|
+
buildPyramidBarMarkers({
|
|
246
|
+
groupedSeries,
|
|
247
|
+
valueFor: signedValue,
|
|
248
|
+
signFor,
|
|
249
|
+
colorFor: (s) => (s.side === 'left' ? leftColor : rightColor),
|
|
250
|
+
disabledFillFor: (name) => disabledFillOverride(effectiveDisabledFor(name)),
|
|
251
|
+
gapFillMaps
|
|
252
|
+
})
|
|
253
|
+
);
|
|
239
254
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
255
|
+
const scopedGroups = $derived(
|
|
256
|
+
buildScopedMarkerGroups<TData, InternalSeries, BarSegmentStyle>({
|
|
257
|
+
groupedSeries,
|
|
258
|
+
segments,
|
|
259
|
+
colorFor: (series) => (series.side === 'left' ? leftColor : rightColor),
|
|
260
|
+
segmentColorFor: (style, defaultColor) => style?.fill ?? defaultColor
|
|
261
|
+
})
|
|
262
|
+
);
|
|
248
263
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
264
|
+
// A pyramid bar's value label can grow past the plot's own content box —
|
|
265
|
+
// reserving real space there is what each label's own overflow
|
|
266
|
+
// measurement feeds into here, via a tracker local to this one `<Plot>`
|
|
267
|
+
// instance. See `labelOverflow.svelte.ts`. Resets on `resolvedSeries` so
|
|
268
|
+
// overflow from an earlier, differently-shaped frame doesn't linger once
|
|
269
|
+
// that frame is gone.
|
|
270
|
+
const labelMargin = createLabelMarginTracker(
|
|
271
|
+
() => margins,
|
|
272
|
+
() => resolvedSeries
|
|
273
|
+
);
|
|
254
274
|
</script>
|
|
255
275
|
|
|
256
276
|
<BasePlotLayout
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
277
|
+
{width}
|
|
278
|
+
{height}
|
|
279
|
+
{styles}
|
|
280
|
+
data={flat}
|
|
281
|
+
{getX}
|
|
282
|
+
{getY}
|
|
283
|
+
{hover}
|
|
284
|
+
scales={resolvedScales}
|
|
285
|
+
{...labelMargin.plotProps}
|
|
286
|
+
xTickRotate={resolvedScales?.x?.tickRotate}
|
|
287
|
+
markers={[...pyramidMarkers, ...markers]}
|
|
288
|
+
seriesData={resolvedSeries}
|
|
289
|
+
{scopedGroups}
|
|
270
290
|
>
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
+
{#snippet children({ numericMargins })}
|
|
292
|
+
{#if showVals}
|
|
293
|
+
{#each groupedSeries as group (group.series.name)}
|
|
294
|
+
<ValueLabels
|
|
295
|
+
{group}
|
|
296
|
+
{valAnchor}
|
|
297
|
+
{formatValue}
|
|
298
|
+
values={styles.values}
|
|
299
|
+
axisColor={cfg.axis.color}
|
|
300
|
+
disabled={effectiveDisabledFor(group.series.name)}
|
|
301
|
+
{width}
|
|
302
|
+
{height}
|
|
303
|
+
{numericMargins}
|
|
304
|
+
onHoverSeries={hoverIsolate
|
|
305
|
+
? () => {
|
|
306
|
+
hoverIsolatedSeries = group.series.name;
|
|
307
|
+
}
|
|
308
|
+
: undefined}
|
|
309
|
+
onHoverEnd={hoverIsolate
|
|
310
|
+
? () => {
|
|
311
|
+
hoverIsolatedSeries = null;
|
|
312
|
+
}
|
|
313
|
+
: undefined}
|
|
314
|
+
onOverflow={labelMargin.report}
|
|
315
|
+
/>
|
|
316
|
+
{/each}
|
|
317
|
+
{/if}
|
|
318
|
+
{/snippet}
|
|
291
319
|
</BasePlotLayout>
|