@rokkit/chart 1.3.15 → 1.4.1

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.
Files changed (47) hide show
  1. package/dist/Plot.svelte +25 -9
  2. package/dist/PlotState.svelte.d.ts +1 -0
  3. package/dist/PlotState.svelte.js +19 -8
  4. package/dist/Spark.svelte +165 -0
  5. package/dist/Spark.svelte.d.ts +31 -0
  6. package/dist/SparkState.svelte.d.ts +138 -0
  7. package/dist/SparkState.svelte.js +331 -0
  8. package/dist/Sparkline.svelte +62 -237
  9. package/dist/Sparkline.svelte.d.ts +2 -0
  10. package/dist/charts/RadarChart.svelte +106 -0
  11. package/dist/charts/RadarChart.svelte.d.ts +43 -0
  12. package/dist/geoms/Area.svelte +24 -9
  13. package/dist/geoms/Area.svelte.d.ts +2 -0
  14. package/dist/geoms/Bar.svelte +9 -16
  15. package/dist/geoms/Highlight.svelte +61 -40
  16. package/dist/geoms/Line.svelte +10 -9
  17. package/dist/geoms/Point.svelte +9 -15
  18. package/dist/geoms/Radar.svelte +352 -0
  19. package/dist/geoms/Radar.svelte.d.ts +42 -0
  20. package/dist/geoms/Trend.svelte +46 -23
  21. package/dist/geoms/lib/aesthetics.d.ts +14 -0
  22. package/dist/geoms/lib/aesthetics.js +21 -0
  23. package/dist/geoms/lib/areas.d.ts +9 -2
  24. package/dist/geoms/lib/areas.js +53 -8
  25. package/dist/geoms/lib/marks/area.d.ts +3 -0
  26. package/dist/geoms/lib/marks/area.js +11 -2
  27. package/dist/geoms/lib/marks/radar.d.ts +68 -0
  28. package/dist/geoms/lib/marks/radar.js +153 -0
  29. package/dist/index.d.ts +6 -0
  30. package/dist/index.js +7 -1
  31. package/dist/lib/brewing/marks/boxes.js +19 -30
  32. package/dist/lib/brewing/polar.d.ts +444 -0
  33. package/dist/lib/brewing/polar.js +645 -0
  34. package/dist/lib/highlight.js +84 -33
  35. package/dist/lib/plot/scales.js +8 -5
  36. package/dist/lib/preset.d.ts +1 -0
  37. package/dist/lib/preset.js +2 -1
  38. package/dist/lib/select.d.ts +25 -11
  39. package/dist/lib/select.js +14 -7
  40. package/dist/lib/trend.js +55 -39
  41. package/package.json +1 -1
  42. package/dist/examples/BarChartExample.svelte +0 -78
  43. package/dist/examples/BarChartExample.svelte.d.ts +0 -18
  44. package/dist/lib/brewing/types.d.ts +0 -162
  45. package/dist/lib/brewing/types.js +0 -73
  46. package/dist/lib/xscale.d.ts +0 -11
  47. package/dist/lib/xscale.js +0 -31
@@ -7,6 +7,9 @@ import { resolveAlpha } from '../aesthetics.js'
7
7
  * are set — so an old `color`-only area stays border-less (backward compatible); the
8
8
  * border uses the interior group's darker stroke shade. Per-field border coloring
9
9
  * (fill ≠ color, two legends) is the deferred dual-scale follow-up.
10
+ * `options.baseline`, when set, is passed straight to `buildAreas` (identity-position charts
11
+ * only — stacked/100% areas ignore it) and splits each segment into signed above/below copies
12
+ * (#148).
10
13
  * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
11
14
  */
12
15
  export function buildAreaMarks({ data, plot, channels, options = {}, alpha, type = 'area' }) {
@@ -27,12 +30,18 @@ export function buildAreaMarks({ data, plot, channels, options = {}, alpha, type
27
30
  const stacked = position === 'stack' || position === 'fill'
28
31
  const raw = stacked
29
32
  ? buildStackedAreas(data, areaChannels, xScale, yScale, colors, options.curve, patterns, place, position === 'fill')
30
- : buildAreas(data, areaChannels, xScale, yScale, colors, options.curve, patterns, place)
33
+ : buildAreas(data, areaChannels, xScale, yScale, colors, options.curve, patterns, place, options.baseline)
31
34
 
32
35
  const a = resolveAlpha(alpha, type, plot.chartPreset)
33
36
  return raw.map((seg) => ({
37
+ // `d`/`fill`/`key`/`patternId`/`sign` all flow through unchanged via this spread;
38
+ // only `stroke` and `alpha` are actually recomputed below.
34
39
  ...seg,
35
- stroke: hasBorder ? (colors.get(seg.key)?.stroke ?? 'none') : 'none',
40
+ // `seg.groupKey` (when present) is the raw, unsigned group value — a baseline split
41
+ // gives `seg.key` a sign suffix, so looking the border color up by `seg.key` directly
42
+ // would miss for every signed segment. `buildStackedAreas` segments don't carry a
43
+ // groupKey (they're never signed), so `seg.key` there is already the raw colorKey.
44
+ stroke: hasBorder ? (colors.get(seg.groupKey ?? seg.key)?.stroke ?? 'none') : 'none',
36
45
  alpha: a
37
46
  }))
38
47
  }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * The one definition of radar's outer radius and which form it is drawing.
3
+ *
4
+ * Both `Radar.svelte` (grid, spokes, labels, vertices) and `buildRadarMarks` (the
5
+ * polygons) need this, and they used to derive it separately from the same
6
+ * `innerWidth`/`innerHeight` — two copies of one rule, free to drift apart. A test had to
7
+ * pin the outermost grid ring to the polygon's own radius to catch that drift; this
8
+ * removes the possibility instead of policing it.
9
+ *
10
+ * The micro form draws no labels, so it spends the whole half-extent on the polygon.
11
+ * Reserving the label margin there is what collapsed it: a Spark is 80×24, so the
12
+ * half-extent is 12px against a 32px margin, giving R = 0 and an invisible glyph.
13
+ *
14
+ * @param {number} innerWidth
15
+ * @param {number} innerHeight
16
+ * @returns {{ R: number, micro: boolean }}
17
+ */
18
+ export function resolveRadarRadius(innerWidth: number, innerHeight: number): {
19
+ R: number;
20
+ micro: boolean;
21
+ };
22
+ /**
23
+ * Build renderable radar/spider polygon marks: one closed polygon per series, split into a
24
+ * fill-only copy and a stroke-only copy so every series' fill can be drawn before any series'
25
+ * stroke (see the return-order note below).
26
+ *
27
+ * Geometry comes from the pure `buildRadarLayout` (shared with the `Spark` radar form); this
28
+ * adapter's job is purely the Svelte/Plot-facing plumbing: deriving the outer radius from the
29
+ * plot's actual pixel size, resolving each series' fill/stroke/pattern through the shared
30
+ * palette, and applying the geom's alpha.
31
+ *
32
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
33
+ * @returns {{ d: string, fill: string, stroke: string, alpha: number, key: string, patternId: string|null }[]}
34
+ * Fill marks (stroke: 'none') for every series, followed by stroke marks (fill: 'none') for
35
+ * every series — never interleaved. A smaller series nested inside a larger one would have its
36
+ * outline buried under the next series' fill if fills and strokes were paired up per series
37
+ * instead, so paint order is fills-then-strokes across the WHOLE series set, not per series.
38
+ */
39
+ export function buildRadarMarks({ data, plot, channels, options, alpha, type }: {
40
+ data: any[];
41
+ plot: any;
42
+ channels: any;
43
+ options?: any;
44
+ alpha?: number;
45
+ type?: string;
46
+ }): {
47
+ d: string;
48
+ fill: string;
49
+ stroke: string;
50
+ alpha: number;
51
+ key: string;
52
+ patternId: string | null;
53
+ }[];
54
+ export const LABEL_MARGIN: 32;
55
+ /**
56
+ * Smallest outer radius at which the full form is actually usable: 24px is the minimum
57
+ * reachable hit target, and a vertex sits ON the outer radius, so below this the chrome
58
+ * is unreadable and the targets unreachable.
59
+ */
60
+ export const MIN_PLOT_RADIUS: 24;
61
+ /**
62
+ * Below this extent (the smaller of width/height) there is no room for BOTH the label
63
+ * margin and a usable plot radius, so radar drops to its micro form.
64
+ */
65
+ export const MICRO_THRESHOLD: number;
66
+ /** Legible axis-count range for the micro form — see the warn in `buildRadarMarks`. */
67
+ export const MICRO_MIN_AXES: 3;
68
+ export const MICRO_MAX_AXES: 5;
@@ -0,0 +1,153 @@
1
+ import { lineRadial, curveLinearClosed } from 'd3-shape'
2
+ import { buildRadarLayout } from '../../../lib/brewing/polar.js'
3
+ import { resolveFillStroke, resolveAlpha } from '../aesthetics.js'
4
+ import { toPatternId } from '../../../lib/brewing/patterns.js'
5
+
6
+ // Space reserved outside the outer ring for axis labels, in pixels. `buildArcs` (the pie/donut
7
+ // adapter) reserves none because a pie's labels sit INSIDE the wedge; a radar's axis labels sit
8
+ // OUTSIDE the outer ring (beyond the last vertex), so the plotted radius has to stop short of
9
+ // `min(width, height) / 2` or a spoke's label collides with the plot's own edge. This is a fixed
10
+ // pixel budget (rather than a fraction of R) because label text is a roughly fixed size
11
+ // regardless of how big the plot is.
12
+ export const LABEL_MARGIN = 32
13
+
14
+ /**
15
+ * Smallest outer radius at which the full form is actually usable: 24px is the minimum
16
+ * reachable hit target, and a vertex sits ON the outer radius, so below this the chrome
17
+ * is unreadable and the targets unreachable.
18
+ */
19
+ export const MIN_PLOT_RADIUS = 24
20
+
21
+ /**
22
+ * Below this extent (the smaller of width/height) there is no room for BOTH the label
23
+ * margin and a usable plot radius, so radar drops to its micro form.
24
+ */
25
+ export const MICRO_THRESHOLD = 2 * (LABEL_MARGIN + MIN_PLOT_RADIUS)
26
+
27
+ /** Legible axis-count range for the micro form — see the warn in `buildRadarMarks`. */
28
+ export const MICRO_MIN_AXES = 3
29
+ export const MICRO_MAX_AXES = 5
30
+
31
+ /**
32
+ * The one definition of radar's outer radius and which form it is drawing.
33
+ *
34
+ * Both `Radar.svelte` (grid, spokes, labels, vertices) and `buildRadarMarks` (the
35
+ * polygons) need this, and they used to derive it separately from the same
36
+ * `innerWidth`/`innerHeight` — two copies of one rule, free to drift apart. A test had to
37
+ * pin the outermost grid ring to the polygon's own radius to catch that drift; this
38
+ * removes the possibility instead of policing it.
39
+ *
40
+ * The micro form draws no labels, so it spends the whole half-extent on the polygon.
41
+ * Reserving the label margin there is what collapsed it: a Spark is 80×24, so the
42
+ * half-extent is 12px against a 32px margin, giving R = 0 and an invisible glyph.
43
+ *
44
+ * @param {number} innerWidth
45
+ * @param {number} innerHeight
46
+ * @returns {{ R: number, micro: boolean }}
47
+ */
48
+ export function resolveRadarRadius(innerWidth, innerHeight) {
49
+ const extent = Math.min(innerWidth, innerHeight)
50
+ const micro = extent < MICRO_THRESHOLD
51
+ const half = extent / 2
52
+ return { R: Math.max(0, micro ? half : half - LABEL_MARGIN), micro }
53
+ }
54
+
55
+ /**
56
+ * Converts `polar.js`'s angle convention (degrees, -90 = top, increasing clockwise) to
57
+ * `lineRadial`'s (radians, 0 = top/12 o'clock, increasing clockwise).
58
+ *
59
+ * Both conventions already agree on the direction of travel (clockwise), so the only
60
+ * difference is where zero sits: polar.js's zero (3 o'clock) is a quarter turn AFTER
61
+ * lineRadial's zero (12 o'clock). Rotating polar.js's angle forward by the 90° between
62
+ * them, then converting to radians, aligns the two: `-90°` (polar.js's top) maps to `0`
63
+ * (lineRadial's top).
64
+ *
65
+ * @param {number} degrees - polar.js convention, -90 = top
66
+ * @returns {number} radians, 0 = top, for `lineRadial`'s `.angle()`
67
+ */
68
+ function toRadialAngle(degrees) {
69
+ return ((degrees + 90) * Math.PI) / 180
70
+ }
71
+
72
+ /**
73
+ * Build renderable radar/spider polygon marks: one closed polygon per series, split into a
74
+ * fill-only copy and a stroke-only copy so every series' fill can be drawn before any series'
75
+ * stroke (see the return-order note below).
76
+ *
77
+ * Geometry comes from the pure `buildRadarLayout` (shared with the `Spark` radar form); this
78
+ * adapter's job is purely the Svelte/Plot-facing plumbing: deriving the outer radius from the
79
+ * plot's actual pixel size, resolving each series' fill/stroke/pattern through the shared
80
+ * palette, and applying the geom's alpha.
81
+ *
82
+ * @param {{ data: any[], plot: any, channels: any, options?: any, alpha?: number, type?: string }} ctx
83
+ * @returns {{ d: string, fill: string, stroke: string, alpha: number, key: string, patternId: string|null }[]}
84
+ * Fill marks (stroke: 'none') for every series, followed by stroke marks (fill: 'none') for
85
+ * every series — never interleaved. A smaller series nested inside a larger one would have its
86
+ * outline buried under the next series' fill if fills and strokes were paired up per series
87
+ * instead, so paint order is fills-then-strokes across the WHOLE series set, not per series.
88
+ */
89
+ export function buildRadarMarks({ data, plot, channels, options = {}, alpha, type = 'radar' }) {
90
+ if (!data?.length || !channels?.x || !channels?.y) return []
91
+
92
+ const { innerWidth, innerHeight, colors, patterns, chartPreset } = plot
93
+
94
+ const { R, micro } = resolveRadarRadius(innerWidth, innerHeight)
95
+
96
+ const layout = buildRadarLayout(data, channels, {
97
+ axes: options.axes,
98
+ sharedDomain: options.sharedDomain,
99
+ rings: options.rings,
100
+ radiusScale: options.radiusScale,
101
+ R
102
+ })
103
+
104
+ // The micro form is read as a gestalt shape, not measured: below 3 axes there is no area
105
+ // to perceive, and above 5 at glyph size the spokes collapse into an unreadable star.
106
+ // Warned rather than clamped — silently dropping a caller's axis would change what the
107
+ // glyph asserts about the data. Lives here, alongside polar.js's own dev warns, rather
108
+ // than in Radar.svelte, so the component stays render-only.
109
+ if (micro && (layout.axes.length < MICRO_MIN_AXES || layout.axes.length > MICRO_MAX_AXES))
110
+ // eslint-disable-next-line no-console
111
+ console.warn(
112
+ `[Radar] ${layout.axes.length} axes in a micro (Spark-sized) radar — ` +
113
+ `${MICRO_MIN_AXES}–${MICRO_MAX_AXES} is the legible range at glyph size. ` +
114
+ 'The shape is meant to be read at a glance, not measured.'
115
+ )
116
+
117
+ const radial = lineRadial()
118
+ .angle((p) => p.angleRad)
119
+ .radius((p) => p.radius)
120
+ .defined((p) => p.radius !== null)
121
+ .curve(curveLinearClosed)
122
+
123
+ const a = resolveAlpha(alpha, type, chartPreset)
124
+
125
+ const fills = []
126
+ const strokes = []
127
+
128
+ for (const [seriesKey, vertices] of layout.series) {
129
+ const points = vertices.map((vertex, i) => ({
130
+ angleRad: toRadialAngle(layout.angles[i]),
131
+ radius: vertex ? vertex.radius : null
132
+ }))
133
+ const d = radial(points)
134
+
135
+ // One representative row for this series — mirrors the pattern `buildLines` uses for
136
+ // its grouped path — so the shared fill/stroke resolution reads real row data instead
137
+ // of a synthetic aggregate.
138
+ const repRow = vertices.find((vertex) => vertex)?.row ?? {}
139
+ const { fill, stroke } = resolveFillStroke(repRow, channels, colors)
140
+
141
+ const patternKey = channels.pattern ? repRow[channels.pattern] : null
142
+ const patternId =
143
+ patternKey !== null && patternKey !== undefined && patterns?.has(patternKey)
144
+ ? toPatternId(String(patternKey))
145
+ : null
146
+
147
+ const keyBase = String(seriesKey)
148
+ fills.push({ d, fill, stroke: 'none', alpha: a, key: `${keyBase}::fill`, patternId })
149
+ strokes.push({ d, fill: 'none', stroke, alpha: 1, key: `${keyBase}::stroke`, patternId: null })
150
+ }
151
+
152
+ return [...fills, ...strokes]
153
+ }
package/dist/index.d.ts CHANGED
@@ -19,6 +19,7 @@ export namespace Plot {
19
19
  export { Ribbon };
20
20
  export { Waterfall };
21
21
  export { Rule };
22
+ export { Radar };
22
23
  }
23
24
  export namespace PlotLayers { }
24
25
  export { default as PlotChart } from "./Plot.svelte";
@@ -32,6 +33,7 @@ export { default as GeomLine } from "./geoms/Line.svelte";
32
33
  export { default as GeomArea } from "./geoms/Area.svelte";
33
34
  export { default as GeomPoint } from "./geoms/Point.svelte";
34
35
  export { default as GeomArc } from "./geoms/Arc.svelte";
36
+ export { default as GeomRadar } from "./geoms/Radar.svelte";
35
37
  export { default as GeomBox } from "./geoms/Box.svelte";
36
38
  export { default as GeomViolin } from "./geoms/Violin.svelte";
37
39
  export { default as GeomHeatmap } from "./geoms/Heatmap.svelte";
@@ -45,10 +47,12 @@ export { default as GeomJitter } from "./geoms/Jitter.svelte";
45
47
  export { default as GeomRule } from "./geoms/Rule.svelte";
46
48
  export { default as Chart } from "./Chart.svelte";
47
49
  export { default as Sparkline } from "./Sparkline.svelte";
50
+ export { default as Spark } from "./Spark.svelte";
48
51
  export { default as BarChart } from "./charts/BarChart.svelte";
49
52
  export { default as LineChart } from "./charts/LineChart.svelte";
50
53
  export { default as AreaChart } from "./charts/AreaChart.svelte";
51
54
  export { default as PieChart } from "./charts/PieChart.svelte";
55
+ export { default as RadarChart } from "./charts/RadarChart.svelte";
52
56
  export { default as ScatterPlot } from "./charts/ScatterPlot.svelte";
53
57
  export { default as BoxPlot } from "./charts/BoxPlot.svelte";
54
58
  export { default as ViolinPlot } from "./charts/ViolinPlot.svelte";
@@ -79,5 +83,7 @@ import Hexbin from './geoms/Hexbin.svelte';
79
83
  import Ribbon from './geoms/Ribbon.svelte';
80
84
  import Waterfall from './geoms/Waterfall.svelte';
81
85
  import Rule from './geoms/Rule.svelte';
86
+ import Radar from './geoms/Radar.svelte';
82
87
  export { createChartPreset, defaultPreset } from "./lib/preset.js";
88
+ export { SparkState, GEOM_CONTRACT } from "./SparkState.svelte.js";
83
89
  export { buildSequentialScale, buildDivergingScale } from "./lib/brewing/colors.js";
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ import Hexbin from './geoms/Hexbin.svelte'
18
18
  import Ribbon from './geoms/Ribbon.svelte'
19
19
  import Waterfall from './geoms/Waterfall.svelte'
20
20
  import Rule from './geoms/Rule.svelte'
21
+ import Radar from './geoms/Radar.svelte'
21
22
 
22
23
  // Composable Plot primitives — use as <Plot.Root>, <Plot.Axis>, <Plot.Bar>, etc.
23
24
  export const Plot = {
@@ -40,7 +41,8 @@ export const Plot = {
40
41
  Hexbin,
41
42
  Ribbon,
42
43
  Waterfall,
43
- Rule
44
+ Rule,
45
+ Radar
44
46
  }
45
47
 
46
48
  /** @deprecated Use Plot instead */
@@ -63,6 +65,7 @@ export { default as GeomLine } from './geoms/Line.svelte'
63
65
  export { default as GeomArea } from './geoms/Area.svelte'
64
66
  export { default as GeomPoint } from './geoms/Point.svelte'
65
67
  export { default as GeomArc } from './geoms/Arc.svelte'
68
+ export { default as GeomRadar } from './geoms/Radar.svelte'
66
69
  export { default as GeomBox } from './geoms/Box.svelte'
67
70
  export { default as GeomViolin } from './geoms/Violin.svelte'
68
71
  export { default as GeomHeatmap } from './geoms/Heatmap.svelte'
@@ -78,10 +81,12 @@ export { default as GeomRule } from './geoms/Rule.svelte'
78
81
  // Export standalone components
79
82
  export { default as Chart } from './Chart.svelte'
80
83
  export { default as Sparkline } from './Sparkline.svelte'
84
+ export { default as Spark } from './Spark.svelte'
81
85
  export { default as BarChart } from './charts/BarChart.svelte'
82
86
  export { default as LineChart } from './charts/LineChart.svelte'
83
87
  export { default as AreaChart } from './charts/AreaChart.svelte'
84
88
  export { default as PieChart } from './charts/PieChart.svelte'
89
+ export { default as RadarChart } from './charts/RadarChart.svelte'
85
90
  export { default as ScatterPlot } from './charts/ScatterPlot.svelte'
86
91
  export { default as BoxPlot } from './charts/BoxPlot.svelte'
87
92
  export { default as ViolinPlot } from './charts/ViolinPlot.svelte'
@@ -89,6 +94,7 @@ export { default as BubbleChart } from './charts/BubbleChart.svelte'
89
94
 
90
95
  // Export state and types
91
96
  export { PlotState } from './PlotState.svelte.js'
97
+ export { SparkState, GEOM_CONTRACT } from './SparkState.svelte.js'
92
98
 
93
99
  // Export utilities
94
100
  export { buildSequentialScale, buildDivergingScale } from './lib/brewing/colors.js'
@@ -37,6 +37,23 @@ export function buildBoxes(data, channels, xScale, yScale, colors, opts = {}) {
37
37
  const key = pf ? d[pf] : null
38
38
  return key !== null && key !== undefined && patterns?.has(key) ? toPatternId(String(key)) : null
39
39
  }
40
+ // Every box shares the same vertical quartile geometry; only horizontal placement,
41
+ // thickness and colour differ between the grouped and non-grouped branches.
42
+ const toBox = (d, { cx, boxWidth, whiskerWidth, colorEntry }) => ({
43
+ data: d,
44
+ cx,
45
+ q1: yScale(d.q1),
46
+ median: yScale(d.median),
47
+ q3: yScale(d.q3),
48
+ iqr_min: yScale(d.iqr_min),
49
+ iqr_max: yScale(d.iqr_max),
50
+ outliers: (d.outliers ?? []).map((v) => ({ cy: yScale(v), value: v })),
51
+ width: boxWidth,
52
+ whiskerWidth,
53
+ fill: colorEntry.fill,
54
+ stroke: colorEntry.stroke,
55
+ patternId: patternIdFor(d)
56
+ })
40
57
 
41
58
  if (grouped) {
42
59
  const fillValues = [...new Set(data.map((d) => d[ff]))]
@@ -52,21 +69,7 @@ export function buildBoxes(data, channels, xScale, yScale, colors, opts = {}) {
52
69
  const cx = bandStart + subIndex * subBandWidth + subBandWidth / 2 + laneOffset(subBandWidth)
53
70
  const colorEntry = markEntry(lit, fillVal, colors, { fill: '#aaa', stroke: '#666' })
54
71
 
55
- return {
56
- data: d,
57
- cx,
58
- q1: yScale(d.q1),
59
- median: yScale(d.median),
60
- q3: yScale(d.q3),
61
- iqr_min: yScale(d.iqr_min),
62
- iqr_max: yScale(d.iqr_max),
63
- outliers: (d.outliers ?? []).map((v) => ({ cy: yScale(v), value: v })),
64
- width: boxWidth,
65
- whiskerWidth,
66
- fill: colorEntry.fill,
67
- stroke: colorEntry.stroke,
68
- patternId: patternIdFor(d)
69
- }
72
+ return toBox(d, { cx, boxWidth, whiskerWidth, colorEntry })
70
73
  })
71
74
  }
72
75
 
@@ -81,20 +84,6 @@ export function buildBoxes(data, channels, xScale, yScale, colors, opts = {}) {
81
84
  (xScale(d[xf]) ?? 0) +
82
85
  (typeof xScale.bandwidth === 'function' ? bw / 2 : 0) +
83
86
  laneOffset(bw)
84
- return {
85
- data: d,
86
- cx,
87
- q1: yScale(d.q1),
88
- median: yScale(d.median),
89
- q3: yScale(d.q3),
90
- iqr_min: yScale(d.iqr_min),
91
- iqr_max: yScale(d.iqr_max),
92
- outliers: (d.outliers ?? []).map((v) => ({ cy: yScale(v), value: v })),
93
- width: boxWidth,
94
- whiskerWidth,
95
- fill: colorEntry.fill,
96
- stroke: colorEntry.stroke,
97
- patternId: patternIdFor(d)
98
- }
87
+ return toBox(d, { cx, boxWidth, whiskerWidth, colorEntry })
99
88
  })
100
89
  }