@rokkit/chart 1.0.0-next.14 → 1.0.0-next.141
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 +5 -0
- package/dist/elements/index.d.ts +6 -0
- package/dist/index.d.ts +14 -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/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/scales.svelte.d.ts +24 -0
- package/dist/lib/brewing/types.d.ts +162 -0
- package/dist/lib/context.d.ts +13 -0
- package/dist/lib/scales.svelte.d.ts +35 -0
- package/dist/lib/utils.d.ts +60 -0
- package/dist/old_lib/brewer.d.ts +9 -0
- package/dist/old_lib/chart.d.ts +40 -0
- package/dist/old_lib/grid.d.ts +72 -0
- package/dist/old_lib/index.d.ts +4 -0
- package/dist/old_lib/plots.d.ts +3 -0
- package/dist/old_lib/swatch.d.ts +285 -0
- package/dist/old_lib/ticks.d.ts +36 -0
- package/dist/old_lib/utils.d.ts +1 -0
- package/dist/patterns/index.d.ts +9 -0
- package/dist/patterns/paths/constants.d.ts +1 -0
- package/dist/symbols/constants/index.d.ts +1 -0
- package/dist/symbols/index.d.ts +5 -0
- package/dist/template/constants.d.ts +43 -0
- package/dist/template/shapes/index.d.ts +4 -0
- package/package.json +34 -44
- package/src/Plot/Axis.svelte +95 -0
- package/src/Plot/Bar.svelte +96 -0
- package/src/Plot/Grid.svelte +68 -0
- package/src/Plot/Legend.svelte +127 -0
- package/src/Plot/Root.svelte +107 -0
- package/src/Plot/index.js +5 -0
- package/src/Symbol.svelte +21 -0
- package/src/Texture.svelte +18 -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 +23 -0
- package/src/elements/index.js +6 -0
- package/src/examples/BarChartExample.svelte +81 -0
- package/src/index.js +18 -16
- package/src/lib/brewing/axes.svelte.js +265 -0
- package/src/lib/brewing/bars.svelte.js +177 -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/scales.svelte.js +106 -0
- package/src/lib/brewing/types.js +73 -0
- package/src/lib/context.js +133 -0
- package/src/lib/scales.svelte.js +172 -0
- package/src/lib/utils.js +107 -120
- package/src/old_lib/brewer.js +25 -0
- package/src/old_lib/chart.js +213 -0
- package/src/old_lib/grid.js +85 -0
- package/src/old_lib/index.js +4 -0
- package/src/old_lib/plots.js +27 -0
- package/src/old_lib/swatch.js +16 -0
- package/src/old_lib/ticks.js +46 -0
- package/src/old_lib/utils.js +8 -0
- package/src/patterns/Brick.svelte +15 -0
- package/src/patterns/Circles.svelte +18 -0
- package/src/patterns/CrossHatch.svelte +12 -0
- package/src/patterns/CurvedWave.svelte +7 -0
- package/src/patterns/Dots.svelte +20 -0
- package/src/patterns/OutlineCircles.svelte +13 -0
- package/src/patterns/README.md +3 -0
- package/src/patterns/Tile.svelte +16 -0
- package/src/patterns/Triangles.svelte +13 -0
- package/src/patterns/Waves.svelte +9 -0
- package/src/patterns/index.js +14 -0
- package/src/patterns/paths/NamedPattern.svelte +9 -0
- package/src/patterns/paths/constants.js +4 -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/template/Texture.svelte +13 -0
- package/src/template/constants.js +43 -0
- package/src/template/shapes/Circles.svelte +15 -0
- package/src/template/shapes/Lines.svelte +16 -0
- package/src/template/shapes/Path.svelte +9 -0
- package/src/template/shapes/Polygons.svelte +15 -0
- package/src/template/shapes/index.js +4 -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
package/src/lib/utils.js
CHANGED
|
@@ -1,157 +1,144 @@
|
|
|
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
|
-
|
|
36
|
-
|
|
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])
|
|
30
|
+
|
|
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
|
+
}
|
|
38
37
|
|
|
39
|
-
return {
|
|
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()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// If it's already an array, return as is
|
|
72
|
+
if (Array.isArray(inputData)) {
|
|
73
|
+
return inputData
|
|
85
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
|
-
|
|
95
|
-
|
|
82
|
+
* @param {string} prefix Prefix for the ID
|
|
83
|
+
* @returns {string} A unique ID
|
|
84
|
+
*/
|
|
85
|
+
export function uniqueId(prefix = 'chart') {
|
|
86
|
+
return `${prefix}-${Math.random().toString(36).substring(2, 10)}`
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
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}
|
|
96
95
|
*/
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
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
|
|
96
|
+
function formatField(key, value, formatter) {
|
|
97
|
+
const formatted = formatter ? formatter(value) : value
|
|
98
|
+
return `${key}: ${formatted}`
|
|
112
99
|
}
|
|
113
100
|
|
|
114
101
|
/**
|
|
115
|
-
*
|
|
116
|
-
* After the palette array is exhausted the fallback value is used
|
|
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 [
|
|
115
|
+
formatField(xKey, d[xKey], xFormat),
|
|
116
|
+
formatField(yKey, d[yKey], yFormat)
|
|
117
|
+
].join('<br>')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return Object.entries(d)
|
|
121
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
122
|
+
.join('<br>')
|
|
127
123
|
}
|
|
128
124
|
|
|
129
125
|
/**
|
|
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.
|
|
126
|
+
* Generates a tooltip formatter function
|
|
133
127
|
*
|
|
134
|
-
* @param {
|
|
135
|
-
* @
|
|
136
|
-
* @param {string} label
|
|
137
|
-
* @returns
|
|
128
|
+
* @param {Object} options Tooltip format options
|
|
129
|
+
* @returns {Function} A function that formats tooltip content
|
|
138
130
|
*/
|
|
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])))
|
|
131
|
+
export function createTooltipFormatter(options = {}) {
|
|
132
|
+
return (d) => formatTooltipContent(d, options)
|
|
144
133
|
}
|
|
134
|
+
|
|
145
135
|
/**
|
|
146
|
-
*
|
|
136
|
+
* Calculates the transform attribute for SVG elements
|
|
147
137
|
*
|
|
148
|
-
* @param {
|
|
149
|
-
* @param {
|
|
150
|
-
* @returns {
|
|
138
|
+
* @param {number} x X position
|
|
139
|
+
* @param {number} y Y position
|
|
140
|
+
* @returns {string} Transform attribute value
|
|
151
141
|
*/
|
|
152
|
-
export function
|
|
153
|
-
return
|
|
154
|
-
(acc, item, index) => ({ ...acc, [item]: b[index % b.length] }),
|
|
155
|
-
{}
|
|
156
|
-
)
|
|
142
|
+
export function transform(x, y) {
|
|
143
|
+
return `translate(${x}, ${y})`
|
|
157
144
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get fill patterns for a set of values
|
|
3
|
+
*
|
|
4
|
+
* @param {Array} values - Array of values
|
|
5
|
+
* @param {Object} swatch - Object with keys for color, gray, and pattern
|
|
6
|
+
* @param {Boolean} gray - Boolean to determine if gray or color
|
|
7
|
+
* @returns {Object} - Object with keys for pattern and color
|
|
8
|
+
*/
|
|
9
|
+
export function getFillPatterns(values, swatch, gray = false) {
|
|
10
|
+
const colors = gray ? swatch.keys.gray : swatch.keys.color
|
|
11
|
+
const max_colors = colors.length
|
|
12
|
+
const max_patterns = swatch.keys.pattern.length
|
|
13
|
+
|
|
14
|
+
const mix = values
|
|
15
|
+
.map((value, index) => ({
|
|
16
|
+
[value]: {
|
|
17
|
+
pattern: swatch.keys.pattern[index % max_patterns],
|
|
18
|
+
color: colors[index % max_colors]
|
|
19
|
+
}
|
|
20
|
+
}))
|
|
21
|
+
.reduce((acc, current) => ({ ...acc, ...current }), {})
|
|
22
|
+
return mix
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// export function getStrokePatterns() {}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { min, max } from 'd3-array'
|
|
2
|
+
import { scaleBand, scaleLinear, scaleTime } from 'd3-scale'
|
|
3
|
+
|
|
4
|
+
function getOriginValue(scale) {
|
|
5
|
+
return scale.ticks ? scale(Math.max(0, Math.min(...scale.domain()))) : scale.range()[0]
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function getScale(domain, range, padding = 0) {
|
|
9
|
+
if (domain.some(isNaN)) {
|
|
10
|
+
return scaleBand().domain(domain).range(range).padding(padding)
|
|
11
|
+
} else if (domain[0] instanceof Date) {
|
|
12
|
+
return scaleTime()
|
|
13
|
+
.domain([min(domain), max(domain)])
|
|
14
|
+
.range(range)
|
|
15
|
+
.nice()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return scaleLinear()
|
|
19
|
+
.domain([min([0, ...domain]), max([0, ...domain])])
|
|
20
|
+
.range(range)
|
|
21
|
+
.nice()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class Chart {
|
|
25
|
+
// data = []
|
|
26
|
+
// width = 512
|
|
27
|
+
// height = 512
|
|
28
|
+
// origin = { x: 0, y: 0 }
|
|
29
|
+
// range = {
|
|
30
|
+
// x: [0, this.width],
|
|
31
|
+
// y: [this.height, 0]
|
|
32
|
+
// }
|
|
33
|
+
// x
|
|
34
|
+
// y
|
|
35
|
+
// stat = 'identity'
|
|
36
|
+
// scale
|
|
37
|
+
// fill
|
|
38
|
+
// color
|
|
39
|
+
// value
|
|
40
|
+
// shape
|
|
41
|
+
// valueFormat
|
|
42
|
+
// valueLabel
|
|
43
|
+
// domain
|
|
44
|
+
// margin
|
|
45
|
+
// spacing
|
|
46
|
+
// padding
|
|
47
|
+
// flipCoords = false
|
|
48
|
+
|
|
49
|
+
constructor(data, opts) {
|
|
50
|
+
this.width = Number(opts.width) || 2048
|
|
51
|
+
this.height = Number(opts.height) || 2048
|
|
52
|
+
this.flipCoords = opts.flipCoords || false
|
|
53
|
+
this.x = opts.x
|
|
54
|
+
this.y = opts.y
|
|
55
|
+
this.value = opts.value || opts.y
|
|
56
|
+
this.valueLabel = opts.valueLabel || this.value
|
|
57
|
+
this.valueFormat = opts.valueFormat || ((d) => d)
|
|
58
|
+
this.fill = opts.fill || opts.x
|
|
59
|
+
this.color = opts.color || opts.fill
|
|
60
|
+
this.shape = opts.shape || opts.fill
|
|
61
|
+
|
|
62
|
+
this.padding = opts.padding !== undefined ? Number(opts.padding) : 32
|
|
63
|
+
|
|
64
|
+
this.spacing =
|
|
65
|
+
Number(opts.spacing) >= 0 && Number(opts.spacing) <= 0.5 ? Number(opts.spacing) : 0
|
|
66
|
+
this.margin = {
|
|
67
|
+
top: Number(opts.margin?.top) || 0,
|
|
68
|
+
left: Number(opts.margin?.left) || 0,
|
|
69
|
+
right: Number(opts.margin?.right) || 0,
|
|
70
|
+
bottom: Number(opts.margin?.bottom) || 0
|
|
71
|
+
}
|
|
72
|
+
this.domain = {
|
|
73
|
+
x: [...new Set(data.map((d) => d[this.x]))],
|
|
74
|
+
y: [...new Set(data.map((d) => d[this.y]))]
|
|
75
|
+
}
|
|
76
|
+
if (this.flipCoords) {
|
|
77
|
+
this.domain = { y: this.domain.x, x: this.domain.y }
|
|
78
|
+
}
|
|
79
|
+
this.stat = opts.stat || 'identity'
|
|
80
|
+
|
|
81
|
+
this.data = data.map((d) => ({
|
|
82
|
+
x: this.flipCoords ? d[this.y] : d[this.x],
|
|
83
|
+
y: this.flipCoords ? d[this.x] : d[this.y],
|
|
84
|
+
// fill: d[this.fill],
|
|
85
|
+
color: d[this.color]
|
|
86
|
+
// shape: d[this.shape]
|
|
87
|
+
}))
|
|
88
|
+
|
|
89
|
+
this.refresh()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
padding(value) {
|
|
93
|
+
this.padding = value
|
|
94
|
+
return this.refresh()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
margin(value) {
|
|
98
|
+
this.margin = value
|
|
99
|
+
return this.refresh()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
refresh() {
|
|
103
|
+
this.range = {
|
|
104
|
+
x: [this.margin.left + this.padding, this.width - this.margin.right - this.padding],
|
|
105
|
+
y: [this.height - this.padding - this.margin.bottom, this.margin.top + this.padding]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const scale = {
|
|
109
|
+
x: getScale(this.domain.x, this.range.x, this.spacing),
|
|
110
|
+
y: getScale(this.domain.y, this.range.y, this.spacing)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// scale['value'] = this.value === this.x ? scale.x : scale.y
|
|
114
|
+
|
|
115
|
+
this.origin = {
|
|
116
|
+
x: getOriginValue(scale.x),
|
|
117
|
+
y: getOriginValue(scale.y)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.scale = scale
|
|
121
|
+
|
|
122
|
+
return this
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// get scale() {
|
|
126
|
+
// return this.scale
|
|
127
|
+
// }
|
|
128
|
+
// get origin() {
|
|
129
|
+
// return this.origin
|
|
130
|
+
// }
|
|
131
|
+
// get margin() {
|
|
132
|
+
// return this.margin
|
|
133
|
+
// }
|
|
134
|
+
// get range() {
|
|
135
|
+
// const [x1, x2] = this.scale.x.range()
|
|
136
|
+
// const [y1, y2] = this.scale.y.range()
|
|
137
|
+
|
|
138
|
+
// return { x1, y1, x2, y2 }
|
|
139
|
+
// }
|
|
140
|
+
// get data() {
|
|
141
|
+
// // aggregate data group by x,y,fill,shape, color
|
|
142
|
+
// // stat = [min, max, avg, std, q1, q3, median, sum, count, box, all]
|
|
143
|
+
|
|
144
|
+
// return this.data
|
|
145
|
+
// }
|
|
146
|
+
// get width() {
|
|
147
|
+
// return this.width
|
|
148
|
+
// }
|
|
149
|
+
// get height() {
|
|
150
|
+
// return this.height
|
|
151
|
+
// }
|
|
152
|
+
// set width(value) {
|
|
153
|
+
// this.width = value
|
|
154
|
+
// }
|
|
155
|
+
// set height(value) {
|
|
156
|
+
// this.height = value
|
|
157
|
+
// }
|
|
158
|
+
// get domain() {
|
|
159
|
+
// return this.domain
|
|
160
|
+
// }
|
|
161
|
+
// get flipCoords() {
|
|
162
|
+
// return this.flipCoords
|
|
163
|
+
// }
|
|
164
|
+
aggregate(value, stat) {
|
|
165
|
+
this.value = value
|
|
166
|
+
this.stat = stat
|
|
167
|
+
|
|
168
|
+
// this.data = nest(this.data)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
ticks(axis, count, fontSize = 8) {
|
|
172
|
+
const scale = this.scale[axis]
|
|
173
|
+
const [minRange, maxRange] = scale.range()
|
|
174
|
+
let ticks = []
|
|
175
|
+
let offset = 0
|
|
176
|
+
|
|
177
|
+
count = count || Math.abs((maxRange - minRange) / (fontSize * (axis === 'y' ? 8 : 8)))
|
|
178
|
+
|
|
179
|
+
if (scale.ticks) {
|
|
180
|
+
ticks = scale.ticks(Math.round(count))
|
|
181
|
+
} else {
|
|
182
|
+
offset = scale.bandwidth() / 2
|
|
183
|
+
count = Math.min(Math.round(count), scale.domain().length)
|
|
184
|
+
|
|
185
|
+
ticks = scale.domain()
|
|
186
|
+
if (count < scale.domain().length) {
|
|
187
|
+
const diff = scale.domain().length - count
|
|
188
|
+
ticks = ticks.filter((d, i) => i % diff === 0)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
ticks = ticks
|
|
193
|
+
.map((t) => ({
|
|
194
|
+
label: t,
|
|
195
|
+
pos: scale(t)
|
|
196
|
+
}))
|
|
197
|
+
.map(({ label, pos }) => ({
|
|
198
|
+
label,
|
|
199
|
+
offset: {
|
|
200
|
+
x: axis === 'x' ? offset : 0,
|
|
201
|
+
y: axis === 'y' ? offset : 0
|
|
202
|
+
},
|
|
203
|
+
x: axis === 'x' ? pos : this.origin.x,
|
|
204
|
+
y: axis === 'y' ? pos : this.origin.y
|
|
205
|
+
}))
|
|
206
|
+
|
|
207
|
+
return ticks
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function chart(data, aes) {
|
|
212
|
+
return new Chart(data, aes)
|
|
213
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef GridPoint
|
|
3
|
+
* @property {number} x - x-coordinate of the point
|
|
4
|
+
* @property {number} y - y-coordinate of the point
|
|
5
|
+
* @property {number} r - radius of the point
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef SwatchGrid
|
|
10
|
+
* @property {number} width - width of the grid
|
|
11
|
+
* @property {number} height - height of the grid
|
|
12
|
+
* @property {GridPoint[]} data - data points of the grid
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @tyoedef {Object} GridOptions
|
|
17
|
+
* @property {number} [pad=0] - The padding between the items
|
|
18
|
+
* @property {number} [columns=0] - The number of columns
|
|
19
|
+
* @property {number} [rows=0] - The number of rows
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Calculates a grid of centres to fit a list of items of `size` within the number of `columns` and `rows`.
|
|
24
|
+
*
|
|
25
|
+
* - Attempts to find a best fit square if both columns and rows are not specified
|
|
26
|
+
* - Value in columns is prioritized over rows for recalculating the grid
|
|
27
|
+
* - Supports padding between the items
|
|
28
|
+
*
|
|
29
|
+
* @param {number} count - number of items
|
|
30
|
+
* @param {number} size - size of the items
|
|
31
|
+
* @param {GridOptions} options - options for the grid
|
|
32
|
+
* @returns {SwatchGrid}
|
|
33
|
+
*/
|
|
34
|
+
export function swatchGrid(count, size, options) {
|
|
35
|
+
const { pad = 0 } = options || {}
|
|
36
|
+
let { columns = 0, rows = 0 } = options || {}
|
|
37
|
+
if (columns > 0) {
|
|
38
|
+
rows = Math.ceil(count / columns)
|
|
39
|
+
} else if (rows > 0) {
|
|
40
|
+
columns = Math.ceil(count / rows)
|
|
41
|
+
} else {
|
|
42
|
+
columns = Math.ceil(Math.sqrt(count))
|
|
43
|
+
rows = Math.ceil(count / columns)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const width = (size + pad) * columns + pad
|
|
47
|
+
const height = (size + pad) * rows + pad
|
|
48
|
+
const radius = size / 2
|
|
49
|
+
const data = [...Array(count).keys()].map((index) => ({
|
|
50
|
+
x: pad + radius + (index % columns) * (size + pad),
|
|
51
|
+
y: pad + radius + Math.floor(index / columns) * (size + pad),
|
|
52
|
+
r: radius
|
|
53
|
+
}))
|
|
54
|
+
|
|
55
|
+
return { width, height, data }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Spreads values as patterns with colors from a palette
|
|
60
|
+
*
|
|
61
|
+
* @param {number[]} values - values to spread
|
|
62
|
+
* @param {string[]} patterns - patterns to spread
|
|
63
|
+
* @param {string[]} palette - colors to spread
|
|
64
|
+
* @returns {Record<number, { id: string, pattern: string, color: string }>}
|
|
65
|
+
*/
|
|
66
|
+
export function spreadValuesAsPatterns(values, patterns, palette) {
|
|
67
|
+
const result = values
|
|
68
|
+
.map((value, index) => ({
|
|
69
|
+
pattern: patterns[index % patterns.length],
|
|
70
|
+
color: palette[index % palette.length],
|
|
71
|
+
value
|
|
72
|
+
}))
|
|
73
|
+
.reduce(
|
|
74
|
+
(acc, { value, pattern, color }) => ({
|
|
75
|
+
...acc,
|
|
76
|
+
[value]: {
|
|
77
|
+
id: `${pattern}_${color}`,
|
|
78
|
+
pattern,
|
|
79
|
+
color
|
|
80
|
+
}
|
|
81
|
+
}),
|
|
82
|
+
{}
|
|
83
|
+
)
|
|
84
|
+
return result
|
|
85
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// skipcq: JS-C1003 - Importing all plots from observablehq/plot
|
|
2
|
+
import * as Plot from '@observablehq/plot'
|
|
3
|
+
|
|
4
|
+
export function plotter(node, options) {
|
|
5
|
+
let { data, aes, type, opts } = options
|
|
6
|
+
// let chart = Plot.lineY(options.data).plot({ grid: true })
|
|
7
|
+
|
|
8
|
+
function render() {
|
|
9
|
+
const chart = Plot[type](data, aes).plot({
|
|
10
|
+
color: { scheme: 'turbo', range: [0.1, 0.9] },
|
|
11
|
+
...opts
|
|
12
|
+
})
|
|
13
|
+
node.firstChild?.remove() // remove old chart, if any
|
|
14
|
+
node.append(chart)
|
|
15
|
+
}
|
|
16
|
+
render()
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
update: (input) => {
|
|
20
|
+
data = input.data
|
|
21
|
+
aes = input.aes
|
|
22
|
+
type = input.type
|
|
23
|
+
opts = input.opts
|
|
24
|
+
render()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { writable } from 'svelte/store'
|
|
2
|
+
import palette from './palette.json'
|
|
3
|
+
// skipcq: JS-C1003 - Importing all patterns
|
|
4
|
+
import * as patterns from '../patterns'
|
|
5
|
+
import { shapes } from '../symbols'
|
|
6
|
+
|
|
7
|
+
export const swatch = writable({
|
|
8
|
+
palette,
|
|
9
|
+
patterns,
|
|
10
|
+
keys: {
|
|
11
|
+
gray: ['gray'],
|
|
12
|
+
color: Object.keys(palette).filter((name) => name !== 'gray'),
|
|
13
|
+
symbol: shapes,
|
|
14
|
+
pattern: Object.keys(patterns)
|
|
15
|
+
}
|
|
16
|
+
})
|
|
@@ -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
|
+
}
|