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