@rokkit/chart 1.0.0-next.15 → 1.0.0-next.151
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/LICENSE +1 -1
- package/README.md +150 -46
- package/dist/Plot/index.d.ts +9 -0
- package/dist/PlotState.svelte.d.ts +49 -0
- package/dist/crossfilter/createCrossFilter.svelte.d.ts +13 -0
- package/dist/elements/index.d.ts +6 -0
- package/dist/geoms/lib/areas.d.ts +52 -0
- package/dist/geoms/lib/bars.d.ts +3 -0
- package/dist/index.d.ts +51 -0
- package/dist/lib/brewer.d.ts +9 -0
- package/dist/lib/brewing/BoxBrewer.svelte.d.ts +10 -0
- package/dist/lib/brewing/CartesianBrewer.svelte.d.ts +8 -0
- package/dist/lib/brewing/PieBrewer.svelte.d.ts +8 -0
- package/dist/lib/brewing/ViolinBrewer.svelte.d.ts +9 -0
- package/dist/lib/brewing/axes.svelte.d.ts +66 -0
- package/dist/lib/brewing/bars.svelte.d.ts +56 -0
- package/dist/lib/brewing/brewer.svelte.d.ts +114 -0
- package/dist/lib/brewing/colors.d.ts +17 -0
- package/dist/lib/brewing/dimensions.svelte.d.ts +35 -0
- package/dist/lib/brewing/index.svelte.d.ts +118 -0
- package/dist/lib/brewing/legends.svelte.d.ts +48 -0
- package/dist/lib/brewing/marks/arcs.d.ts +17 -0
- package/dist/lib/brewing/marks/areas.d.ts +31 -0
- package/dist/lib/brewing/marks/bars.d.ts +1 -0
- package/dist/lib/brewing/marks/boxes.d.ts +24 -0
- package/dist/lib/brewing/marks/lines.d.ts +24 -0
- package/dist/lib/brewing/marks/points.d.ts +40 -0
- package/dist/lib/brewing/marks/violins.d.ts +20 -0
- package/dist/lib/brewing/patterns.d.ts +14 -0
- package/dist/lib/brewing/scales.d.ts +28 -0
- package/dist/lib/brewing/scales.svelte.d.ts +24 -0
- package/dist/lib/brewing/stats.d.ts +23 -0
- package/dist/lib/brewing/symbols.d.ts +7 -0
- package/dist/lib/brewing/types.d.ts +162 -0
- package/dist/lib/chart.d.ts +38 -0
- package/dist/lib/context.d.ts +13 -0
- package/dist/lib/grid.d.ts +72 -0
- package/dist/lib/plot/chartProps.d.ts +177 -0
- package/dist/lib/plot/crossfilter.d.ts +13 -0
- package/dist/lib/plot/facet.d.ts +24 -0
- package/dist/lib/plot/frames.d.ts +47 -0
- package/dist/lib/plot/helpers.d.ts +3 -0
- package/dist/lib/plot/preset.d.ts +29 -0
- package/dist/lib/plot/scales.d.ts +5 -0
- package/dist/lib/plot/stat.d.ts +32 -0
- package/dist/lib/plot/types.d.ts +89 -0
- package/dist/lib/scales.svelte.d.ts +35 -0
- package/dist/lib/swatch.d.ts +12 -0
- package/dist/lib/ticks.d.ts +36 -0
- package/dist/lib/utils.d.ts +61 -0
- package/dist/lib/xscale.d.ts +11 -0
- package/dist/patterns/index.d.ts +4 -0
- package/dist/patterns/patterns.d.ts +72 -0
- package/dist/patterns/scale.d.ts +30 -0
- package/dist/symbols/constants/index.d.ts +1 -0
- package/dist/symbols/index.d.ts +5 -0
- package/package.json +41 -45
- package/src/AnimatedPlot.svelte +215 -0
- package/src/Chart.svelte +98 -0
- package/src/FacetPlot/Panel.svelte +23 -0
- package/src/FacetPlot.svelte +90 -0
- package/src/Plot/Arc.svelte +29 -0
- package/src/Plot/Area.svelte +25 -0
- package/src/Plot/Axis.svelte +73 -0
- package/src/Plot/Bar.svelte +96 -0
- package/src/Plot/Grid.svelte +30 -0
- package/src/Plot/Legend.svelte +167 -0
- package/src/Plot/Line.svelte +27 -0
- package/src/Plot/Point.svelte +27 -0
- package/src/Plot/Root.svelte +107 -0
- package/src/Plot/Timeline.svelte +95 -0
- package/src/Plot/Tooltip.svelte +81 -0
- package/src/Plot/index.js +9 -0
- package/src/Plot.svelte +181 -0
- package/src/PlotState.svelte.js +277 -0
- package/src/Sparkline.svelte +69 -0
- package/src/Symbol.svelte +21 -0
- package/src/Texture.svelte +18 -0
- package/src/charts/AreaChart.svelte +25 -0
- package/src/charts/BarChart.svelte +26 -0
- package/src/charts/BoxPlot.svelte +21 -0
- package/src/charts/BubbleChart.svelte +23 -0
- package/src/charts/LineChart.svelte +26 -0
- package/src/charts/PieChart.svelte +25 -0
- package/src/charts/ScatterPlot.svelte +25 -0
- package/src/charts/ViolinPlot.svelte +21 -0
- package/src/crossfilter/CrossFilter.svelte +38 -0
- package/src/crossfilter/FilterBar.svelte +32 -0
- package/src/crossfilter/FilterSlider.svelte +79 -0
- package/src/crossfilter/createCrossFilter.svelte.js +120 -0
- package/src/elements/Bar.svelte +22 -24
- package/src/elements/ColorRamp.svelte +20 -22
- package/src/elements/ContinuousLegend.svelte +20 -17
- package/src/elements/DefinePatterns.svelte +24 -0
- package/src/elements/DiscreteLegend.svelte +15 -15
- package/src/elements/Label.svelte +4 -8
- package/src/elements/SymbolGrid.svelte +22 -0
- package/src/elements/index.js +6 -0
- package/src/examples/BarChartExample.svelte +81 -0
- package/src/geoms/Arc.svelte +81 -0
- package/src/geoms/Area.svelte +50 -0
- package/src/geoms/Bar.svelte +142 -0
- package/src/geoms/Box.svelte +103 -0
- package/src/geoms/LabelPill.svelte +17 -0
- package/src/geoms/Line.svelte +99 -0
- package/src/geoms/Point.svelte +105 -0
- package/src/geoms/Violin.svelte +46 -0
- package/src/geoms/lib/areas.js +131 -0
- package/src/geoms/lib/bars.js +172 -0
- package/src/index.js +67 -16
- package/src/lib/brewer.js +25 -0
- package/src/lib/brewing/BoxBrewer.svelte.js +56 -0
- package/src/lib/brewing/CartesianBrewer.svelte.js +17 -0
- package/src/lib/brewing/PieBrewer.svelte.js +14 -0
- package/src/lib/brewing/ViolinBrewer.svelte.js +55 -0
- package/src/lib/brewing/axes.svelte.js +270 -0
- package/src/lib/brewing/bars.svelte.js +201 -0
- package/src/lib/brewing/brewer.svelte.js +230 -0
- package/src/lib/brewing/colors.js +22 -0
- package/src/lib/brewing/dimensions.svelte.js +56 -0
- package/src/lib/brewing/index.svelte.js +205 -0
- package/src/lib/brewing/legends.svelte.js +137 -0
- package/src/lib/brewing/marks/arcs.js +43 -0
- package/src/lib/brewing/marks/areas.js +59 -0
- package/src/lib/brewing/marks/bars.js +49 -0
- package/src/lib/brewing/marks/boxes.js +75 -0
- package/src/lib/brewing/marks/lines.js +48 -0
- package/src/lib/brewing/marks/points.js +57 -0
- package/src/lib/brewing/marks/violins.js +90 -0
- package/src/lib/brewing/patterns.js +31 -0
- package/src/lib/brewing/scales.js +51 -0
- package/src/lib/brewing/scales.svelte.js +82 -0
- package/src/lib/brewing/stats.js +66 -0
- package/src/lib/brewing/symbols.js +10 -0
- package/src/lib/brewing/types.js +73 -0
- package/src/lib/chart.js +220 -0
- package/src/lib/context.js +131 -0
- package/src/lib/grid.js +85 -0
- package/src/lib/plot/chartProps.js +76 -0
- package/src/lib/plot/crossfilter.js +16 -0
- package/src/lib/plot/facet.js +58 -0
- package/src/lib/plot/frames.js +80 -0
- package/src/lib/plot/helpers.js +14 -0
- package/src/lib/plot/preset.js +53 -0
- package/src/lib/plot/scales.js +56 -0
- package/src/lib/plot/stat.js +92 -0
- package/src/lib/plot/types.js +65 -0
- package/src/lib/scales.svelte.js +151 -0
- package/src/lib/swatch.js +13 -0
- package/src/lib/ticks.js +46 -0
- package/src/lib/utils.js +111 -118
- package/src/lib/xscale.js +31 -0
- package/src/patterns/DefinePatterns.svelte +32 -0
- package/src/patterns/PatternDef.svelte +27 -0
- package/src/patterns/index.js +4 -0
- package/src/patterns/patterns.js +208 -0
- package/src/patterns/scale.js +87 -0
- package/src/spec/chart-spec.js +29 -0
- package/src/symbols/RoundedSquare.svelte +33 -0
- package/src/symbols/Shape.svelte +37 -0
- package/src/symbols/constants/index.js +4 -0
- package/src/symbols/index.js +9 -0
- package/src/symbols/outline.svelte +60 -0
- package/src/symbols/solid.svelte +60 -0
- package/src/chart/FacetGrid.svelte +0 -51
- package/src/chart/Grid.svelte +0 -34
- package/src/chart/Legend.svelte +0 -16
- package/src/chart/PatternDefs.svelte +0 -13
- package/src/chart/Swatch.svelte +0 -93
- package/src/chart/SwatchButton.svelte +0 -29
- package/src/chart/SwatchGrid.svelte +0 -55
- package/src/chart/Symbol.svelte +0 -37
- package/src/chart/Texture.svelte +0 -16
- package/src/chart/TexturedShape.svelte +0 -27
- package/src/chart/TimelapseChart.svelte +0 -97
- package/src/chart/Timer.svelte +0 -27
- package/src/chart.js +0 -9
- package/src/components/charts/Axis.svelte +0 -66
- package/src/components/charts/Chart.svelte +0 -35
- package/src/components/index.js +0 -23
- package/src/components/lib/axis.js +0 -0
- package/src/components/lib/chart.js +0 -187
- package/src/components/lib/color.js +0 -327
- package/src/components/lib/funnel.js +0 -204
- package/src/components/lib/index.js +0 -19
- package/src/components/lib/pattern.js +0 -190
- package/src/components/lib/rollup.js +0 -55
- package/src/components/lib/shape.js +0 -199
- package/src/components/lib/summary.js +0 -145
- package/src/components/lib/theme.js +0 -23
- package/src/components/lib/timer.js +0 -41
- package/src/components/lib/utils.js +0 -165
- package/src/components/plots/BarPlot.svelte +0 -36
- package/src/components/plots/BoxPlot.svelte +0 -54
- package/src/components/plots/ScatterPlot.svelte +0 -30
- package/src/components/store.js +0 -70
- package/src/constants.js +0 -66
- package/src/elements/PatternDefs.svelte +0 -13
- package/src/elements/PatternMask.svelte +0 -20
- package/src/elements/Symbol.svelte +0 -38
- package/src/elements/Tooltip.svelte +0 -23
- package/src/funnel.svelte +0 -35
- package/src/geom.js +0 -105
- package/src/lib/axis.js +0 -75
- package/src/lib/colors.js +0 -32
- package/src/lib/geom.js +0 -4
- package/src/lib/shapes.js +0 -144
- package/src/lib/timer.js +0 -44
- package/src/lookup.js +0 -29
- package/src/plots/BarPlot.svelte +0 -55
- package/src/plots/BoxPlot.svelte +0 -0
- package/src/plots/FunnelPlot.svelte +0 -33
- package/src/plots/HeatMap.svelte +0 -5
- package/src/plots/HeatMapCalendar.svelte +0 -129
- package/src/plots/LinePlot.svelte +0 -55
- package/src/plots/Plot.svelte +0 -25
- package/src/plots/RankBarPlot.svelte +0 -38
- package/src/plots/ScatterPlot.svelte +0 -20
- package/src/plots/ViolinPlot.svelte +0 -11
- package/src/plots/heatmap.js +0 -70
- package/src/plots/index.js +0 -10
- package/src/swatch.js +0 -11
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// packages/chart/src/lib/plot/types.js
|
|
2
|
+
// JSDoc typedefs for the Plot system. No runtime code.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} GeomSpec
|
|
6
|
+
* @property {string} type - Geom type: 'bar'|'line'|'area'|'point'|'box'|'violin'|'arc' or custom
|
|
7
|
+
* @property {string} [x]
|
|
8
|
+
* @property {string} [y]
|
|
9
|
+
* @property {string} [color]
|
|
10
|
+
* @property {string} [fill]
|
|
11
|
+
* @property {string} [size]
|
|
12
|
+
* @property {string} [symbol]
|
|
13
|
+
* @property {string} [pattern]
|
|
14
|
+
* @property {string} [stat] - Built-in or helpers.stats key
|
|
15
|
+
* @property {Record<string, unknown>} [options]
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {Object} PlotSpec
|
|
20
|
+
* @property {Record<string, unknown>[]} data
|
|
21
|
+
* @property {string} [x]
|
|
22
|
+
* @property {string} [y]
|
|
23
|
+
* @property {string} [color]
|
|
24
|
+
* @property {string} [fill]
|
|
25
|
+
* @property {string} [size]
|
|
26
|
+
* @property {string} [symbol]
|
|
27
|
+
* @property {string} [pattern]
|
|
28
|
+
* @property {string} [theta]
|
|
29
|
+
* @property {Record<string, string>} [labels]
|
|
30
|
+
* @property {unknown[]} [xDomain]
|
|
31
|
+
* @property {number[]} [yDomain]
|
|
32
|
+
* @property {string} [xLabel]
|
|
33
|
+
* @property {string} [yLabel]
|
|
34
|
+
* @property {[number, number]} [axisOrigin]
|
|
35
|
+
* @property {'categorical'|'sequential'|'diverging'} [colorScale]
|
|
36
|
+
* @property {string} [colorScheme]
|
|
37
|
+
* @property {number} [colorMidpoint]
|
|
38
|
+
* @property {unknown[]} [colorDomain]
|
|
39
|
+
* @property {GeomSpec[]} geoms
|
|
40
|
+
* @property {{ by: string, cols?: number, scales?: 'fixed'|'free'|'free_x'|'free_y' }} [facet]
|
|
41
|
+
* @property {{ by: string, duration?: number, loop?: boolean }} [animate]
|
|
42
|
+
* @property {boolean} [grid]
|
|
43
|
+
* @property {boolean} [legend]
|
|
44
|
+
* @property {boolean} [tooltip]
|
|
45
|
+
* @property {string} [title]
|
|
46
|
+
* @property {string} [preset]
|
|
47
|
+
* @property {number} [width]
|
|
48
|
+
* @property {number} [height]
|
|
49
|
+
* @property {'light'|'dark'} [mode]
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @typedef {Object} PlotHelpers
|
|
54
|
+
* @property {Record<string, (values: unknown[]) => unknown>} [stats]
|
|
55
|
+
* @property {Record<string, (v: unknown) => string>} [format]
|
|
56
|
+
* @property {(d: Record<string, unknown>) => string} [tooltip]
|
|
57
|
+
* @property {Record<string, unknown>} [geoms] Svelte components keyed by type name
|
|
58
|
+
* @property {unknown} [colorScale] d3 scale override
|
|
59
|
+
* @property {{ colors: string[], patterns: string[], symbols: string[] }} [preset]
|
|
60
|
+
* @property {Record<string, { colors: string[], patterns: string[], symbols: string[] }>} [presets]
|
|
61
|
+
* @property {Record<string, unknown>} [patterns] custom SVG pattern components keyed by name
|
|
62
|
+
* @property {Record<string, unknown>} [symbols] custom symbol shape components keyed by name
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
export {}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { SvelteSet } from 'svelte/reactivity'
|
|
2
|
+
import { scaleLinear, scaleOrdinal } from 'd3-scale'
|
|
3
|
+
import { schemeCategory10 } from 'd3-scale-chromatic'
|
|
4
|
+
import { min, max } from 'd3-array'
|
|
5
|
+
import { buildXScale } from './xscale.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates appropriate scales based on data and dimensions
|
|
9
|
+
*
|
|
10
|
+
* @param {Array} data The dataset
|
|
11
|
+
* @param {Object} dimensions Chart dimensions
|
|
12
|
+
* @param {Object} options Additional options
|
|
13
|
+
* @param {string} options.xKey Field to use for x-axis
|
|
14
|
+
* @param {string} options.yKey Field to use for y-axis
|
|
15
|
+
* @param {string} [options.colorKey] Field to use for color mapping
|
|
16
|
+
* @param {number} [options.padding=0.2] Padding for band scales
|
|
17
|
+
* @returns {Object} Object containing xScale, yScale, and colorScale
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* @param {Array} data
|
|
21
|
+
* @param {string} colorKey
|
|
22
|
+
* @returns {Object}
|
|
23
|
+
*/
|
|
24
|
+
function buildColorScale(data, colorKey) {
|
|
25
|
+
const uniqueCategories = [...new SvelteSet(data.map((d) => d[colorKey]))]
|
|
26
|
+
return scaleOrdinal().domain(uniqueCategories).range(schemeCategory10)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {Object} options
|
|
31
|
+
* @returns {{ xKey: string, yKey: string, colorKey: string|undefined, padding: number }}
|
|
32
|
+
*/
|
|
33
|
+
function parseScaleOptions(options) {
|
|
34
|
+
const opts = options || {}
|
|
35
|
+
return {
|
|
36
|
+
xKey: opts.xKey,
|
|
37
|
+
yKey: opts.yKey,
|
|
38
|
+
colorKey: opts.colorKey,
|
|
39
|
+
padding: opts.padding !== undefined ? opts.padding : 0.2
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createScales(data, dimensions, options) {
|
|
44
|
+
if (!data || !data.length) return {}
|
|
45
|
+
|
|
46
|
+
const { xKey, yKey, colorKey, padding } = parseScaleOptions(options)
|
|
47
|
+
const xValues = data.map((d) => d[xKey])
|
|
48
|
+
const yValues = data.map((d) => d[yKey])
|
|
49
|
+
const colorScale = colorKey ? buildColorScale(data, colorKey) : null
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
xScale: buildXScale(xValues, dimensions, padding),
|
|
53
|
+
yScale: scaleLinear()
|
|
54
|
+
.domain([0, max(yValues) * 1.1])
|
|
55
|
+
.nice()
|
|
56
|
+
.range([dimensions.innerHeight, 0]),
|
|
57
|
+
colorScale
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Calculates the actual chart dimensions after applying margins
|
|
63
|
+
*
|
|
64
|
+
* @param {number} width
|
|
65
|
+
* @param {number} height
|
|
66
|
+
* @param {Object} margin
|
|
67
|
+
* @returns {Object} Dimensions with calculated inner width and height
|
|
68
|
+
*/
|
|
69
|
+
export function calculateChartDimensions(width, height, margin) {
|
|
70
|
+
return {
|
|
71
|
+
width,
|
|
72
|
+
height,
|
|
73
|
+
margin,
|
|
74
|
+
innerWidth: width - margin.left - margin.right,
|
|
75
|
+
innerHeight: height - margin.top - margin.bottom
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Gets the axis origin value
|
|
81
|
+
*
|
|
82
|
+
* @param {Object} scale D3 scale
|
|
83
|
+
* @returns {number} Origin value
|
|
84
|
+
*/
|
|
85
|
+
export function getOriginValue(scale) {
|
|
86
|
+
return scale.ticks ? scale(Math.max(0, min(scale.domain()))) : scale.range()[0]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resolve tick values for a band scale, applying downsampling when needed
|
|
91
|
+
* @param {Object} scale
|
|
92
|
+
* @param {number} count
|
|
93
|
+
* @returns {{ ticks: Array, offset: number }}
|
|
94
|
+
*/
|
|
95
|
+
function bandTicks(scale, count) {
|
|
96
|
+
const offset = scale.bandwidth() / 2
|
|
97
|
+
const domain = scale.domain()
|
|
98
|
+
const cappedCount = Math.min(Math.round(count), domain.length)
|
|
99
|
+
const step = Math.ceil(domain.length / cappedCount)
|
|
100
|
+
const ticks = cappedCount < domain.length ? domain.filter((_, i) => i % step === 0) : domain
|
|
101
|
+
return { ticks, offset }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {number} rangeSize
|
|
106
|
+
* @param {string} axis
|
|
107
|
+
* @param {number} fontSize
|
|
108
|
+
* @returns {number}
|
|
109
|
+
*/
|
|
110
|
+
function defaultTickCount(rangeSize, axis, fontSize) {
|
|
111
|
+
const divisor = fontSize * (axis === 'y' ? 3 : 6)
|
|
112
|
+
return Math.abs(rangeSize / divisor)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @param {Array} ticks
|
|
117
|
+
* @param {Object} scale
|
|
118
|
+
* @param {number} offset
|
|
119
|
+
* @param {boolean} isXAxis
|
|
120
|
+
* @returns {Array}
|
|
121
|
+
*/
|
|
122
|
+
function formatTicks(ticks, scale, offset, isXAxis) {
|
|
123
|
+
const pos = isXAxis ? offset : 0
|
|
124
|
+
return ticks.map((t) => ({ value: t, position: scale(t) + pos }))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Creates axis ticks
|
|
129
|
+
*
|
|
130
|
+
* @param {Object} scale D3 scale
|
|
131
|
+
* @param {string} axis Axis type ('x' or 'y')
|
|
132
|
+
* @param {number} count Number of ticks
|
|
133
|
+
* @param {number} fontSize Font size for determining tick density
|
|
134
|
+
* @returns {Array} Array of tick objects
|
|
135
|
+
*/
|
|
136
|
+
export function createTicks(scale, axis, count = null, fontSize = 12) {
|
|
137
|
+
const [minRange, maxRange] = scale.range()
|
|
138
|
+
const tickCount = count ?? defaultTickCount(maxRange - minRange, axis, fontSize)
|
|
139
|
+
|
|
140
|
+
let ticks, offset
|
|
141
|
+
if (scale.ticks) {
|
|
142
|
+
ticks = scale.ticks(Math.round(tickCount))
|
|
143
|
+
offset = 0
|
|
144
|
+
} else {
|
|
145
|
+
const band = bandTicks(scale, tickCount)
|
|
146
|
+
ticks = band.ticks
|
|
147
|
+
offset = band.offset
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return formatTicks(ticks, scale, offset, axis === 'x')
|
|
151
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import palette from './palette.json'
|
|
2
|
+
import { PATTERN_ORDER } from './brewing/patterns.js'
|
|
3
|
+
import { shapes } from '../symbols'
|
|
4
|
+
|
|
5
|
+
export const swatch = {
|
|
6
|
+
palette,
|
|
7
|
+
keys: {
|
|
8
|
+
gray: ['gray'],
|
|
9
|
+
color: Object.keys(palette).filter((name) => name !== 'gray'),
|
|
10
|
+
symbol: shapes,
|
|
11
|
+
pattern: PATTERN_ORDER
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/lib/ticks.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} TickSteps
|
|
3
|
+
* @property {number} major - count of major ticks
|
|
4
|
+
* @property {number} minor - count of minor ticks
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generate an array of ticks for a given axis and the tick type
|
|
9
|
+
*
|
|
10
|
+
* @param {number} lower - The lower bound of the axis
|
|
11
|
+
* @param {number} upper - The upper bound of the axis
|
|
12
|
+
* @param {TickSteps} steps - The number of steps between major and minor ticks
|
|
13
|
+
* @param {string} type - The type of tick to generate
|
|
14
|
+
*
|
|
15
|
+
* @returns {Array} - An array of objects representing the ticks
|
|
16
|
+
*/
|
|
17
|
+
export function ticksByType(lower, upper, steps, type) {
|
|
18
|
+
if (steps <= 0) return []
|
|
19
|
+
return Array.from({ length: Math.floor((upper - lower) / steps) + 1 }, (_, i) => ({
|
|
20
|
+
position: i * steps,
|
|
21
|
+
label: i * steps,
|
|
22
|
+
type
|
|
23
|
+
})).filter((tick) => tick.position > lower && tick.position < upper)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Generate an array of ticks for a given axis
|
|
28
|
+
*
|
|
29
|
+
* @param {number} lower - The lower bound of the axis
|
|
30
|
+
* @param {number} upper - The upper bound of the axis
|
|
31
|
+
* @param {TickSteps} steps - The number of steps between major and minor ticks
|
|
32
|
+
*
|
|
33
|
+
* @returns {Array} - An array of objects representing the ticks
|
|
34
|
+
*/
|
|
35
|
+
export function getTicks(lower, upper, steps = { major: 10, minor: 0 }) {
|
|
36
|
+
const majorTicks = ticksByType(lower, upper, steps.major, 'major')
|
|
37
|
+
const minorTicks = ticksByType(lower, upper, steps.minor, 'minor').filter(
|
|
38
|
+
(tick) => !majorTicks.find((major) => major.position === tick.position)
|
|
39
|
+
)
|
|
40
|
+
const end = [
|
|
41
|
+
{ position: upper, label: upper, type: 'end' },
|
|
42
|
+
{ position: lower, label: lower, type: 'end' }
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
return [...minorTicks, ...majorTicks, ...end].sort((a, b) => a.position - b.position)
|
|
46
|
+
}
|
package/src/lib/utils.js
CHANGED
|
@@ -1,157 +1,150 @@
|
|
|
1
|
-
import { scaleBand, scaleLinear } from 'd3-scale'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { nest } from 'd3-collection'
|
|
5
|
-
import { omit } from 'ramda'
|
|
1
|
+
import { scaleBand, scaleLinear, scaleOrdinal } from 'd3-scale'
|
|
2
|
+
import { schemeCategory10 } from 'd3-scale-chromatic'
|
|
3
|
+
import { max } from 'd3-array'
|
|
6
4
|
|
|
7
5
|
/**
|
|
8
|
-
*
|
|
6
|
+
* Creates appropriate scales based on data and dimensions
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @param {
|
|
15
|
-
* @param {
|
|
16
|
-
* @
|
|
17
|
-
* @param {number} columns
|
|
18
|
-
* @param {number} rows
|
|
19
|
-
* @returns
|
|
8
|
+
* @param {Array} data The dataset
|
|
9
|
+
* @param {Object} dimensions Chart dimensions
|
|
10
|
+
* @param {Object} [options] Additional options
|
|
11
|
+
* @param {string} options.xKey Field to use for x-axis
|
|
12
|
+
* @param {string} options.yKey Field to use for y-axis
|
|
13
|
+
* @param {string} [options.colorKey] Field to use for color mapping
|
|
14
|
+
* @returns {Object} Object containing xScale, yScale, and colorScale
|
|
20
15
|
*/
|
|
21
|
-
export function
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
export function createScales(data, dimensions, options = {}) {
|
|
17
|
+
if (!data || data.length === 0) return {}
|
|
18
|
+
|
|
19
|
+
const { xKey, yKey, colorKey } = options
|
|
20
|
+
|
|
21
|
+
const xScale = scaleBand()
|
|
22
|
+
.domain(data.map((d) => d[xKey]))
|
|
23
|
+
.range([0, dimensions.width])
|
|
24
|
+
.padding(0.2)
|
|
30
25
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
cy: (size + pad) / 2 + Math.floor(index / columns) * (size + pad),
|
|
36
|
-
r: size / 2
|
|
37
|
-
}))
|
|
26
|
+
const yScale = scaleLinear()
|
|
27
|
+
.domain([0, max(data, (d) => d[yKey]) * 1.1]) // Add 10% padding on top
|
|
28
|
+
.nice()
|
|
29
|
+
.range([dimensions.height, 0])
|
|
38
30
|
|
|
39
|
-
|
|
31
|
+
let colorScale = null
|
|
32
|
+
|
|
33
|
+
if (colorKey) {
|
|
34
|
+
const uniqueCategories = [...new Set(data.map((d) => d[colorKey]))]
|
|
35
|
+
colorScale = scaleOrdinal().domain(uniqueCategories).range(schemeCategory10)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { xScale, yScale, colorScale }
|
|
40
39
|
}
|
|
40
|
+
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
42
|
+
* Calculates the actual chart dimensions after applying margins
|
|
43
43
|
*
|
|
44
|
-
* @param {
|
|
45
|
-
* @
|
|
46
|
-
* @param {number} buffer
|
|
47
|
-
* @returns
|
|
44
|
+
* @param {Object} dimensions Original dimensions
|
|
45
|
+
* @returns {Object} Dimensions with calculated inner width and height
|
|
48
46
|
*/
|
|
49
|
-
export function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
let maxValue = max(values)
|
|
57
|
-
|
|
58
|
-
if (minValue < 0 && maxValue > 0) {
|
|
59
|
-
maxValue = max([-1 * minValue, maxValue])
|
|
60
|
-
minValue = -1 * maxValue
|
|
61
|
-
}
|
|
62
|
-
const margin = (maxValue - minValue) * buffer
|
|
63
|
-
return scaleLinear()
|
|
64
|
-
.domain([minValue - margin, maxValue + margin])
|
|
65
|
-
.range(bounds)
|
|
47
|
+
export function calculateChartDimensions(dimensions) {
|
|
48
|
+
const { width, height, margin } = dimensions
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
...dimensions,
|
|
52
|
+
innerWidth: width - margin.left - margin.right,
|
|
53
|
+
innerHeight: height - margin.top - margin.bottom
|
|
66
54
|
}
|
|
67
55
|
}
|
|
56
|
+
|
|
68
57
|
/**
|
|
69
|
-
*
|
|
58
|
+
* Normalizes data for use with D3 charts
|
|
70
59
|
*
|
|
71
|
-
* @param {
|
|
72
|
-
* @
|
|
73
|
-
* @param {string} y
|
|
74
|
-
* @param {number} width
|
|
75
|
-
* @param {number} height
|
|
76
|
-
* @returns
|
|
60
|
+
* @param {Array|Object} inputData Raw data or dataset object
|
|
61
|
+
* @returns {Array} Normalized data array
|
|
77
62
|
*/
|
|
78
|
-
export function
|
|
79
|
-
|
|
80
|
-
const yValues = [...new Set(data.map((item) => item[y]))]
|
|
63
|
+
export function normalizeData(inputData) {
|
|
64
|
+
if (!inputData) return []
|
|
81
65
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
66
|
+
// If it's a dataset class instance, call select() to get the data
|
|
67
|
+
if (inputData.select && typeof inputData.select === 'function') {
|
|
68
|
+
return inputData.select()
|
|
85
69
|
}
|
|
70
|
+
|
|
71
|
+
// If it's already an array, return as is
|
|
72
|
+
if (Array.isArray(inputData)) {
|
|
73
|
+
return inputData
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return []
|
|
86
77
|
}
|
|
87
78
|
|
|
88
79
|
/**
|
|
89
|
-
*
|
|
90
|
-
* key as unique `x` values and value as statistical summaries of `y` values
|
|
80
|
+
* Generates a unique ID for SVG elements
|
|
91
81
|
*
|
|
92
|
-
* @param {
|
|
93
|
-
* @
|
|
94
|
-
* @param {*} y
|
|
95
|
-
* @returns
|
|
82
|
+
* @param {string} prefix Prefix for the ID
|
|
83
|
+
* @returns {string} A unique ID
|
|
96
84
|
*/
|
|
97
|
-
export function
|
|
98
|
-
|
|
99
|
-
.key((d) => d[x])
|
|
100
|
-
.rollup((d) => {
|
|
101
|
-
let values = d.map((g) => g[y]).sort(ascending)
|
|
102
|
-
let q1 = quantile(values, 0.25)
|
|
103
|
-
let q3 = quantile(values, 0.75)
|
|
104
|
-
let median = quantile(values, 0.5)
|
|
105
|
-
let interQuantileRange = q3 - q1
|
|
106
|
-
let min = q1 - 1.5 * interQuantileRange
|
|
107
|
-
let max = q3 + 1.5 * interQuantileRange
|
|
108
|
-
return { q1, q3, median, interQuantileRange, min, max }
|
|
109
|
-
})
|
|
110
|
-
.entries(data)
|
|
111
|
-
return summary
|
|
85
|
+
export function uniqueId(prefix = 'chart') {
|
|
86
|
+
return `${prefix}-${Math.random().toString(36).substring(2, 10)}`
|
|
112
87
|
}
|
|
113
88
|
|
|
114
89
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
90
|
+
* Format a single key-value pair for tooltip
|
|
91
|
+
* @param {string} key
|
|
92
|
+
* @param {unknown} value
|
|
93
|
+
* @param {Function|undefined} formatter
|
|
94
|
+
* @returns {string}
|
|
95
|
+
*/
|
|
96
|
+
function formatField(key, value, formatter) {
|
|
97
|
+
const formatted = formatter ? formatter(value) : value
|
|
98
|
+
return `${key}: ${formatted}`
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Formats tooltip content for a data point
|
|
117
103
|
*
|
|
118
|
-
* @param {
|
|
119
|
-
* @param {
|
|
120
|
-
* @
|
|
121
|
-
* @returns
|
|
104
|
+
* @param {Object} d Data point
|
|
105
|
+
* @param {Object} options Tooltip format options
|
|
106
|
+
* @returns {string} Formatted tooltip HTML content
|
|
122
107
|
*/
|
|
123
|
-
export function
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
108
|
+
export function formatTooltipContent(d, options = {}) {
|
|
109
|
+
if (!d) return ''
|
|
110
|
+
|
|
111
|
+
const { xKey, yKey, xFormat, yFormat } = options
|
|
112
|
+
|
|
113
|
+
if (xKey && yKey) {
|
|
114
|
+
return [formatField(xKey, d[xKey], xFormat), formatField(yKey, d[yKey], yFormat)].join('<br>')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return Object.entries(d)
|
|
118
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
119
|
+
.join('<br>')
|
|
127
120
|
}
|
|
128
121
|
|
|
129
122
|
/**
|
|
130
|
-
*
|
|
131
|
-
* 'key' contains unique values for the attribute specified by the key parameter
|
|
132
|
-
* and value contains array of all remaining attributes in an array.
|
|
123
|
+
* Generates a tooltip formatter function
|
|
133
124
|
*
|
|
134
|
-
* @param {
|
|
135
|
-
* @
|
|
136
|
-
* @param {string} label
|
|
137
|
-
* @returns
|
|
125
|
+
* @param {Object} options Tooltip format options
|
|
126
|
+
* @returns {Function} A function that formats tooltip content
|
|
138
127
|
*/
|
|
139
|
-
export function
|
|
140
|
-
return
|
|
141
|
-
.key((d) => d[key])
|
|
142
|
-
.rollup((values) => values.map((value) => omit([key], value)))
|
|
143
|
-
.entries(data.sort((a, b) => ascending(a[label], b[label])))
|
|
128
|
+
export function createTooltipFormatter(options = {}) {
|
|
129
|
+
return (d) => formatTooltipContent(d, options)
|
|
144
130
|
}
|
|
131
|
+
|
|
145
132
|
/**
|
|
146
|
-
*
|
|
133
|
+
* Calculates the transform attribute for SVG elements
|
|
147
134
|
*
|
|
148
|
-
* @param {
|
|
149
|
-
* @param {
|
|
150
|
-
* @returns {
|
|
135
|
+
* @param {number} x X position
|
|
136
|
+
* @param {number} y Y position
|
|
137
|
+
* @returns {string} Transform attribute value
|
|
151
138
|
*/
|
|
152
|
-
export function
|
|
153
|
-
return
|
|
154
|
-
|
|
139
|
+
export function transform(x, y) {
|
|
140
|
+
return `translate(${x}, ${y})`
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
import { scaledPath } from '@rokkit/core'
|
|
144
|
+
|
|
145
|
+
export function scaledPathCollection(paths) {
|
|
146
|
+
return Object.entries(paths).reduce(
|
|
147
|
+
(acc, [key, value]) => ({ ...acc, [key]: (s) => scaledPath(s, value) }),
|
|
155
148
|
{}
|
|
156
149
|
)
|
|
157
150
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { scaleBand, scaleLinear, scaleTime } from 'd3-scale'
|
|
2
|
+
import { min, max } from 'd3-array'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Builds an appropriate D3 x-axis scale based on value types.
|
|
6
|
+
* Returns scaleTime for Date values, scaleLinear for numeric values,
|
|
7
|
+
* or scaleBand for categorical values.
|
|
8
|
+
*
|
|
9
|
+
* @param {Array} xValues
|
|
10
|
+
* @param {Object} dimensions
|
|
11
|
+
* @param {number} padding
|
|
12
|
+
* @returns {import('d3-scale').ScaleContinuousNumeric|import('d3-scale').ScaleBand}
|
|
13
|
+
*/
|
|
14
|
+
export function buildXScale(xValues, dimensions, padding) {
|
|
15
|
+
const xIsDate = xValues.some((v) => v instanceof Date)
|
|
16
|
+
const xIsNumeric = !xIsDate && xValues.every((v) => !isNaN(parseFloat(v)))
|
|
17
|
+
|
|
18
|
+
if (xIsDate) {
|
|
19
|
+
return scaleTime()
|
|
20
|
+
.domain([min(xValues), max(xValues)])
|
|
21
|
+
.range([0, dimensions.innerWidth])
|
|
22
|
+
.nice()
|
|
23
|
+
}
|
|
24
|
+
if (xIsNumeric) {
|
|
25
|
+
return scaleLinear()
|
|
26
|
+
.domain([min([0, ...xValues]), max(xValues)])
|
|
27
|
+
.range([0, dimensions.innerWidth])
|
|
28
|
+
.nice()
|
|
29
|
+
}
|
|
30
|
+
return scaleBand().domain(xValues).range([0, dimensions.innerWidth]).padding(padding)
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getContext } from 'svelte'
|
|
3
|
+
import { toPatternId } from '../lib/brewing/patterns.js'
|
|
4
|
+
import { PATTERNS } from './patterns.js'
|
|
5
|
+
import PatternDef from './PatternDef.svelte'
|
|
6
|
+
|
|
7
|
+
/** @type {{ patterns?: Record<string, import('./patterns.js').PatternMark[]> }} */
|
|
8
|
+
let { patterns = PATTERNS } = $props()
|
|
9
|
+
|
|
10
|
+
const state = getContext('plot-state')
|
|
11
|
+
|
|
12
|
+
const patternDefs = $derived.by(() => {
|
|
13
|
+
const defs = []
|
|
14
|
+
for (const [key, patternName] of (state.patterns ?? new Map()).entries()) {
|
|
15
|
+
const colorEntry = state.colors?.get(key) ?? { stroke: '#444' }
|
|
16
|
+
defs.push({
|
|
17
|
+
id: toPatternId(String(key)),
|
|
18
|
+
marks: patterns[patternName] ?? [],
|
|
19
|
+
stroke: colorEntry.stroke ?? '#444'
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
return defs
|
|
23
|
+
})
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
{#if patternDefs.length > 0}
|
|
27
|
+
<defs data-plot-pattern-defs>
|
|
28
|
+
{#each patternDefs as def (def.id)}
|
|
29
|
+
<PatternDef id={def.id} marks={def.marks} stroke={def.stroke} />
|
|
30
|
+
{/each}
|
|
31
|
+
</defs>
|
|
32
|
+
{/if}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { scaleMark, resolveMarkAttrs } from './scale.js'
|
|
3
|
+
|
|
4
|
+
/** @type {{ id: string, marks?: import('./patterns.js').PatternMark[], size?: number, stroke?: string, thickness?: number }} */
|
|
5
|
+
let { id, marks = [], size = 10, stroke = '#444', thickness = 0.5 } = $props()
|
|
6
|
+
|
|
7
|
+
const resolvedMarks = $derived(
|
|
8
|
+
marks.map((m) => resolveMarkAttrs(scaleMark(m, size), { fill: stroke, stroke, thickness }))
|
|
9
|
+
)
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<pattern {id} patternUnits="userSpaceOnUse" width={size} height={size}>
|
|
13
|
+
<rect width={size} height={size} fill="none" />
|
|
14
|
+
{#each resolvedMarks as { type, attrs }, i (i)}
|
|
15
|
+
{#if type === 'line'}
|
|
16
|
+
<line {...attrs} />
|
|
17
|
+
{:else if type === 'circle'}
|
|
18
|
+
<circle {...attrs} />
|
|
19
|
+
{:else if type === 'rect'}
|
|
20
|
+
<rect {...attrs} />
|
|
21
|
+
{:else if type === 'polygon'}
|
|
22
|
+
<polygon {...attrs} />
|
|
23
|
+
{:else if type === 'path'}
|
|
24
|
+
<path {...attrs} />
|
|
25
|
+
{/if}
|
|
26
|
+
{/each}
|
|
27
|
+
</pattern>
|