@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.
- package/dist/Plot.svelte +25 -9
- package/dist/PlotState.svelte.d.ts +1 -0
- package/dist/PlotState.svelte.js +19 -8
- package/dist/Spark.svelte +165 -0
- package/dist/Spark.svelte.d.ts +31 -0
- package/dist/SparkState.svelte.d.ts +138 -0
- package/dist/SparkState.svelte.js +331 -0
- package/dist/Sparkline.svelte +62 -237
- package/dist/Sparkline.svelte.d.ts +2 -0
- package/dist/charts/RadarChart.svelte +106 -0
- package/dist/charts/RadarChart.svelte.d.ts +43 -0
- package/dist/geoms/Area.svelte +24 -9
- package/dist/geoms/Area.svelte.d.ts +2 -0
- package/dist/geoms/Bar.svelte +9 -16
- package/dist/geoms/Highlight.svelte +61 -40
- package/dist/geoms/Line.svelte +10 -9
- package/dist/geoms/Point.svelte +9 -15
- package/dist/geoms/Radar.svelte +352 -0
- package/dist/geoms/Radar.svelte.d.ts +42 -0
- package/dist/geoms/Trend.svelte +46 -23
- package/dist/geoms/lib/aesthetics.d.ts +14 -0
- package/dist/geoms/lib/aesthetics.js +21 -0
- package/dist/geoms/lib/areas.d.ts +9 -2
- package/dist/geoms/lib/areas.js +53 -8
- package/dist/geoms/lib/marks/area.d.ts +3 -0
- package/dist/geoms/lib/marks/area.js +11 -2
- package/dist/geoms/lib/marks/radar.d.ts +68 -0
- package/dist/geoms/lib/marks/radar.js +153 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -1
- package/dist/lib/brewing/marks/boxes.js +19 -30
- package/dist/lib/brewing/polar.d.ts +444 -0
- package/dist/lib/brewing/polar.js +645 -0
- package/dist/lib/highlight.js +84 -33
- package/dist/lib/plot/scales.js +8 -5
- package/dist/lib/preset.d.ts +1 -0
- package/dist/lib/preset.js +2 -1
- package/dist/lib/select.d.ts +25 -11
- package/dist/lib/select.js +14 -7
- package/dist/lib/trend.js +55 -39
- package/package.json +1 -1
- package/dist/examples/BarChartExample.svelte +0 -78
- package/dist/examples/BarChartExample.svelte.d.ts +0 -18
- package/dist/lib/brewing/types.d.ts +0 -162
- package/dist/lib/brewing/types.js +0 -73
- package/dist/lib/xscale.d.ts +0 -11
- 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
|
-
|
|
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
|
}
|