@rokkit/chart 1.0.0-next.12 → 1.0.0-next.122
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/README.md +140 -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 +72 -0
- package/dist/lib/brewing/bars.svelte.d.ts +54 -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 +54 -0
- package/dist/lib/brewing/scales.svelte.d.ts +29 -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 +58 -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 +28 -44
- package/src/Plot/Axis.svelte +95 -0
- package/src/Plot/Bar.svelte +95 -0
- package/src/Plot/Grid.svelte +68 -0
- package/src/Plot/Legend.svelte +127 -0
- package/src/Plot/Root.svelte +114 -0
- package/src/Plot/index.js +5 -0
- package/src/Symbol.svelte +21 -0
- package/src/{chart/Texture.svelte → Texture.svelte} +3 -3
- package/src/elements/Bar.svelte +2 -8
- package/src/elements/ColorRamp.svelte +20 -22
- package/src/elements/ContinuousLegend.svelte +5 -6
- package/src/elements/DefinePatterns.svelte +22 -0
- package/src/elements/DiscreteLegend.svelte +4 -6
- package/src/elements/Label.svelte +6 -5
- 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 +177 -0
- package/src/lib/brewing/bars.svelte.js +114 -0
- package/src/lib/brewing/dimensions.svelte.js +56 -0
- package/src/lib/brewing/index.svelte.js +202 -0
- package/src/lib/brewing/legends.svelte.js +94 -0
- package/src/lib/brewing/scales.svelte.js +85 -0
- package/src/lib/brewing/types.js +73 -0
- package/src/lib/context.js +132 -0
- package/src/lib/scales.svelte.js +122 -0
- package/src/lib/utils.js +96 -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 +17 -0
- package/src/patterns/Circles.svelte +18 -0
- package/src/patterns/CrossHatch.svelte +14 -0
- package/src/patterns/CurvedWave.svelte +9 -0
- package/src/patterns/Dots.svelte +19 -0
- package/src/patterns/OutlineCircles.svelte +15 -0
- package/src/patterns/Tile.svelte +17 -0
- package/src/patterns/Triangles.svelte +15 -0
- package/src/patterns/Waves.svelte +13 -0
- package/src/patterns/index.js +14 -0
- package/src/patterns/paths/NamedPattern.svelte +12 -0
- package/src/patterns/paths/constants.js +4 -0
- package/src/symbols/RoundedSquare.svelte +26 -0
- package/src/symbols/Shape.svelte +31 -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 +16 -0
- package/src/template/constants.js +43 -0
- package/src/template/shapes/Circles.svelte +16 -0
- package/src/template/shapes/Lines.svelte +17 -0
- package/src/template/shapes/Path.svelte +12 -0
- package/src/template/shapes/Polygons.svelte +18 -0
- package/src/template/shapes/index.js +4 -0
- package/LICENSE +0 -21
- 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/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,132 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte'
|
|
2
|
+
import { writable, derived } from 'svelte/store'
|
|
3
|
+
import * as d3 from 'd3'
|
|
4
|
+
|
|
5
|
+
const CHART_CONTEXT = 'chart-context'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Creates chart context and provides it to child components
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} options Initial chart options
|
|
11
|
+
* @returns {Object} Chart context with all stores and methods
|
|
12
|
+
*/
|
|
13
|
+
export function createChartContext(options = {}) {
|
|
14
|
+
// Default config values
|
|
15
|
+
const defaultOptions = {
|
|
16
|
+
width: 600,
|
|
17
|
+
height: 400,
|
|
18
|
+
margin: { top: 20, right: 30, bottom: 40, left: 50 },
|
|
19
|
+
padding: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
20
|
+
responsive: true,
|
|
21
|
+
animationDuration: 300,
|
|
22
|
+
data: []
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Merge options with defaults
|
|
26
|
+
const config = { ...defaultOptions, ...options }
|
|
27
|
+
|
|
28
|
+
// Create stores for reactive properties
|
|
29
|
+
const dimensions = writable({
|
|
30
|
+
width: config.width,
|
|
31
|
+
height: config.height,
|
|
32
|
+
margin: { ...config.margin },
|
|
33
|
+
padding: { ...config.padding }
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const data = writable(config.data)
|
|
37
|
+
const scales = writable({})
|
|
38
|
+
|
|
39
|
+
// Compute inner dimensions (subtracting margins)
|
|
40
|
+
const innerDimensions = derived(dimensions, ($dimensions) => {
|
|
41
|
+
return {
|
|
42
|
+
width:
|
|
43
|
+
$dimensions.width -
|
|
44
|
+
$dimensions.margin.left -
|
|
45
|
+
$dimensions.margin.right -
|
|
46
|
+
$dimensions.padding.left -
|
|
47
|
+
$dimensions.padding.right,
|
|
48
|
+
height:
|
|
49
|
+
$dimensions.height -
|
|
50
|
+
$dimensions.margin.top -
|
|
51
|
+
$dimensions.margin.bottom -
|
|
52
|
+
$dimensions.padding.top -
|
|
53
|
+
$dimensions.padding.bottom
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// Store for plot elements (bars, lines, etc.)
|
|
58
|
+
const plots = writable([])
|
|
59
|
+
|
|
60
|
+
// Store for axes
|
|
61
|
+
const axes = writable({
|
|
62
|
+
x: null,
|
|
63
|
+
y: null
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const legend = writable({
|
|
67
|
+
enabled: false,
|
|
68
|
+
items: []
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// Helper to add a new plot
|
|
72
|
+
function addPlot(plot) {
|
|
73
|
+
plots.update((currentPlots) => [...currentPlots, plot])
|
|
74
|
+
return () => {
|
|
75
|
+
plots.update((currentPlots) => currentPlots.filter((p) => p !== plot))
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Helper to update scales based on data and dimensions
|
|
80
|
+
function updateScales(xKey, yKey, colorKey = null) {
|
|
81
|
+
return derived([data, innerDimensions], ([$data, $innerDimensions]) => {
|
|
82
|
+
if (!$data || $data.length === 0) return null
|
|
83
|
+
|
|
84
|
+
const xScale = d3
|
|
85
|
+
.scaleBand()
|
|
86
|
+
.domain($data.map((d) => d[xKey]))
|
|
87
|
+
.range([0, $innerDimensions.width])
|
|
88
|
+
.padding(0.2)
|
|
89
|
+
|
|
90
|
+
const yScale = d3
|
|
91
|
+
.scaleLinear()
|
|
92
|
+
.domain([0, d3.max($data, (d) => d[yKey])])
|
|
93
|
+
.nice()
|
|
94
|
+
.range([$innerDimensions.height, 0])
|
|
95
|
+
|
|
96
|
+
let colorScale = null
|
|
97
|
+
|
|
98
|
+
if (colorKey) {
|
|
99
|
+
const uniqueCategories = [...new Set($data.map((d) => d[colorKey]))]
|
|
100
|
+
colorScale = d3.scaleOrdinal().domain(uniqueCategories).range(d3.schemeCategory10)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { xScale, yScale, colorScale }
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Create and set context
|
|
108
|
+
const chartContext = {
|
|
109
|
+
dimensions,
|
|
110
|
+
innerDimensions,
|
|
111
|
+
data,
|
|
112
|
+
scales,
|
|
113
|
+
plots,
|
|
114
|
+
axes,
|
|
115
|
+
legend,
|
|
116
|
+
addPlot,
|
|
117
|
+
updateScales
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
setContext(CHART_CONTEXT, chartContext)
|
|
121
|
+
|
|
122
|
+
return chartContext
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Gets chart context provided by parent component
|
|
127
|
+
*
|
|
128
|
+
* @returns {Object} Chart context
|
|
129
|
+
*/
|
|
130
|
+
export function getChartContext() {
|
|
131
|
+
return getContext(CHART_CONTEXT)
|
|
132
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { scaleBand, scaleLinear, scaleTime, scaleOrdinal } from 'd3-scale'
|
|
2
|
+
import { schemeCategory10 } from 'd3-scale-chromatic'
|
|
3
|
+
import { min, max } from 'd3-array'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates appropriate scales based on data and dimensions
|
|
7
|
+
*
|
|
8
|
+
* @param {Array} data The dataset
|
|
9
|
+
* @param {string} xKey Field to use for x-axis
|
|
10
|
+
* @param {string} yKey Field to use for y-axis
|
|
11
|
+
* @param {Object} dimensions Chart dimensions
|
|
12
|
+
* @param {Object} options Additional options
|
|
13
|
+
* @returns {Object} Object containing xScale, yScale, and colorScale
|
|
14
|
+
*/
|
|
15
|
+
export function createScales(data, xKey, yKey, dimensions, options = {}) {
|
|
16
|
+
if (!data || data.length === 0) return {}
|
|
17
|
+
|
|
18
|
+
const { colorKey = null, padding = 0.2 } = options
|
|
19
|
+
|
|
20
|
+
// Determine if x values are numeric, dates, or categorical
|
|
21
|
+
const xValues = data.map((d) => d[xKey])
|
|
22
|
+
const xIsDate = xValues.some((v) => v instanceof Date)
|
|
23
|
+
const xIsNumeric = !xIsDate && xValues.every((v) => !isNaN(parseFloat(v)))
|
|
24
|
+
|
|
25
|
+
// Create x-scale based on data type
|
|
26
|
+
let xScale
|
|
27
|
+
if (xIsDate) {
|
|
28
|
+
xScale = scaleTime()
|
|
29
|
+
.domain([min(xValues), max(xValues)])
|
|
30
|
+
.range([0, dimensions.innerWidth])
|
|
31
|
+
.nice()
|
|
32
|
+
} else if (xIsNumeric) {
|
|
33
|
+
xScale = scaleLinear()
|
|
34
|
+
.domain([min([0, ...xValues]), max(xValues)])
|
|
35
|
+
.range([0, dimensions.innerWidth])
|
|
36
|
+
.nice()
|
|
37
|
+
} else {
|
|
38
|
+
xScale = scaleBand().domain(xValues).range([0, dimensions.innerWidth]).padding(padding)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Create y-scale
|
|
42
|
+
const yValues = data.map((d) => d[yKey])
|
|
43
|
+
const yScale = scaleLinear()
|
|
44
|
+
.domain([0, max(yValues) * 1.1]) // Add 10% padding on top
|
|
45
|
+
.nice()
|
|
46
|
+
.range([dimensions.innerHeight, 0])
|
|
47
|
+
|
|
48
|
+
// Create color scale if colorKey is provided
|
|
49
|
+
let colorScale = null
|
|
50
|
+
if (colorKey) {
|
|
51
|
+
const uniqueCategories = [...new Set(data.map((d) => d[colorKey]))]
|
|
52
|
+
colorScale = scaleOrdinal().domain(uniqueCategories).range(schemeCategory10)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { xScale, yScale, colorScale }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Calculates the actual chart dimensions after applying margins
|
|
60
|
+
*
|
|
61
|
+
* @param {Object} dimensions Original dimensions
|
|
62
|
+
* @returns {Object} Dimensions with calculated inner width and height
|
|
63
|
+
*/
|
|
64
|
+
export function calculateChartDimensions(width, height, margin) {
|
|
65
|
+
return {
|
|
66
|
+
width,
|
|
67
|
+
height,
|
|
68
|
+
margin,
|
|
69
|
+
innerWidth: width - margin.left - margin.right,
|
|
70
|
+
innerHeight: height - margin.top - margin.bottom
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Gets the axis origin value
|
|
76
|
+
*
|
|
77
|
+
* @param {Object} scale D3 scale
|
|
78
|
+
* @returns {number} Origin value
|
|
79
|
+
*/
|
|
80
|
+
export function getOriginValue(scale) {
|
|
81
|
+
return scale.ticks ? scale(Math.max(0, min(scale.domain()))) : scale.range()[0]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Creates axis ticks
|
|
86
|
+
*
|
|
87
|
+
* @param {Object} scale D3 scale
|
|
88
|
+
* @param {string} axis Axis type ('x' or 'y')
|
|
89
|
+
* @param {number} count Number of ticks
|
|
90
|
+
* @param {number} fontSize Font size for determining tick density
|
|
91
|
+
* @returns {Array} Array of tick objects
|
|
92
|
+
*/
|
|
93
|
+
export function createTicks(scale, axis, count = null, fontSize = 12) {
|
|
94
|
+
const [minRange, maxRange] = scale.range()
|
|
95
|
+
let ticks = []
|
|
96
|
+
let offset = 0
|
|
97
|
+
|
|
98
|
+
// Calculate default count based on available space
|
|
99
|
+
if (!count) {
|
|
100
|
+
count = Math.abs((maxRange - minRange) / (fontSize * (axis === 'y' ? 3 : 6)))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Get ticks based on scale type
|
|
104
|
+
if (scale.ticks) {
|
|
105
|
+
ticks = scale.ticks(Math.round(count))
|
|
106
|
+
} else {
|
|
107
|
+
offset = scale.bandwidth() / 2
|
|
108
|
+
count = Math.min(Math.round(count), scale.domain().length)
|
|
109
|
+
|
|
110
|
+
ticks = scale.domain()
|
|
111
|
+
if (count < scale.domain().length) {
|
|
112
|
+
const step = Math.ceil(scale.domain().length / count)
|
|
113
|
+
ticks = ticks.filter((_, i) => i % step === 0)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Format ticks with positions
|
|
118
|
+
return ticks.map((t) => ({
|
|
119
|
+
value: t,
|
|
120
|
+
position: scale(t) + (axis === 'x' ? offset : 0)
|
|
121
|
+
}))
|
|
122
|
+
}
|
package/src/lib/utils.js
CHANGED
|
@@ -1,157 +1,133 @@
|
|
|
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 {string} xKey Field to use for x-axis
|
|
10
|
+
* @param {string} yKey Field to use for y-axis
|
|
11
|
+
* @param {Object} dimensions Chart dimensions
|
|
12
|
+
* @param {Object} [options] Additional options
|
|
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
|
-
rows = Math.ceil(count / columns)
|
|
29
|
-
}
|
|
16
|
+
export function createScales(data, xKey, yKey, dimensions, options = {}) {
|
|
17
|
+
if (!data || data.length === 0) return {}
|
|
18
|
+
|
|
19
|
+
const xScale = scaleBand()
|
|
20
|
+
.domain(data.map((d) => d[xKey]))
|
|
21
|
+
.range([0, dimensions.width])
|
|
22
|
+
.padding(0.2)
|
|
30
23
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
24
|
+
const yScale = scaleLinear()
|
|
25
|
+
.domain([0, max(data, (d) => d[yKey]) * 1.1]) // Add 10% padding on top
|
|
26
|
+
.nice()
|
|
27
|
+
.range([dimensions.height, 0])
|
|
28
|
+
|
|
29
|
+
let colorScale = null
|
|
30
|
+
|
|
31
|
+
if (options.colorKey) {
|
|
32
|
+
const uniqueCategories = [...new Set(data.map((d) => d[options.colorKey]))]
|
|
33
|
+
colorScale = scaleOrdinal().domain(uniqueCategories).range(schemeCategory10)
|
|
34
|
+
}
|
|
38
35
|
|
|
39
|
-
return {
|
|
36
|
+
return { xScale, yScale, colorScale }
|
|
40
37
|
}
|
|
38
|
+
|
|
41
39
|
/**
|
|
42
|
-
*
|
|
40
|
+
* Calculates the actual chart dimensions after applying margins
|
|
43
41
|
*
|
|
44
|
-
* @param {
|
|
45
|
-
* @
|
|
46
|
-
* @param {number} buffer
|
|
47
|
-
* @returns
|
|
42
|
+
* @param {Object} dimensions Original dimensions
|
|
43
|
+
* @returns {Object} Dimensions with calculated inner width and height
|
|
48
44
|
*/
|
|
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)
|
|
45
|
+
export function calculateChartDimensions(dimensions) {
|
|
46
|
+
const { width, height, margin } = dimensions
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
...dimensions,
|
|
50
|
+
innerWidth: width - margin.left - margin.right,
|
|
51
|
+
innerHeight: height - margin.top - margin.bottom
|
|
66
52
|
}
|
|
67
53
|
}
|
|
54
|
+
|
|
68
55
|
/**
|
|
69
|
-
*
|
|
56
|
+
* Normalizes data for use with D3 charts
|
|
70
57
|
*
|
|
71
|
-
* @param {
|
|
72
|
-
* @
|
|
73
|
-
* @param {string} y
|
|
74
|
-
* @param {number} width
|
|
75
|
-
* @param {number} height
|
|
76
|
-
* @returns
|
|
58
|
+
* @param {Array|Object} inputData Raw data or dataset object
|
|
59
|
+
* @returns {Array} Normalized data array
|
|
77
60
|
*/
|
|
78
|
-
export function
|
|
79
|
-
|
|
80
|
-
const yValues = [...new Set(data.map((item) => item[y]))]
|
|
61
|
+
export function normalizeData(inputData) {
|
|
62
|
+
if (!inputData) return []
|
|
81
63
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
64
|
+
// If it's a dataset class instance, call select() to get the data
|
|
65
|
+
if (inputData.select && typeof inputData.select === 'function') {
|
|
66
|
+
return inputData.select()
|
|
85
67
|
}
|
|
68
|
+
|
|
69
|
+
// If it's already an array, return as is
|
|
70
|
+
if (Array.isArray(inputData)) {
|
|
71
|
+
return inputData
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return []
|
|
86
75
|
}
|
|
87
76
|
|
|
88
77
|
/**
|
|
89
|
-
*
|
|
90
|
-
* key as unique `x` values and value as statistical summaries of `y` values
|
|
78
|
+
* Generates a unique ID for SVG elements
|
|
91
79
|
*
|
|
92
|
-
* @param {
|
|
93
|
-
* @
|
|
94
|
-
* @param {*} y
|
|
95
|
-
* @returns
|
|
80
|
+
* @param {string} prefix Prefix for the ID
|
|
81
|
+
* @returns {string} A unique ID
|
|
96
82
|
*/
|
|
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
|
|
83
|
+
export function uniqueId(prefix = 'chart') {
|
|
84
|
+
return `${prefix}-${Math.random().toString(36).substring(2, 10)}`
|
|
112
85
|
}
|
|
113
86
|
|
|
114
87
|
/**
|
|
115
|
-
*
|
|
116
|
-
* After the palette array is exhausted the fallback value is used
|
|
88
|
+
* Formats tooltip content for a data point
|
|
117
89
|
*
|
|
118
|
-
* @param {
|
|
119
|
-
* @param {
|
|
120
|
-
* @
|
|
121
|
-
* @returns
|
|
90
|
+
* @param {Object} d Data point
|
|
91
|
+
* @param {Object} options Tooltip format options
|
|
92
|
+
* @returns {string} Formatted tooltip HTML content
|
|
122
93
|
*/
|
|
123
|
-
export function
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
94
|
+
export function formatTooltipContent(d, options = {}) {
|
|
95
|
+
if (!d) return ''
|
|
96
|
+
|
|
97
|
+
const { xKey, yKey, xFormat, yFormat } = options
|
|
98
|
+
|
|
99
|
+
if (xKey && yKey) {
|
|
100
|
+
const xValue = d[xKey]
|
|
101
|
+
const yValue = d[yKey]
|
|
102
|
+
|
|
103
|
+
const xFormatted = xFormat ? xFormat(xValue) : xValue
|
|
104
|
+
const yFormatted = yFormat ? yFormat(yValue) : yValue
|
|
105
|
+
|
|
106
|
+
return `${xKey}: ${xFormatted}<br>${yKey}: ${yFormatted}`
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return Object.entries(d)
|
|
110
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
111
|
+
.join('<br>')
|
|
127
112
|
}
|
|
128
113
|
|
|
129
114
|
/**
|
|
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.
|
|
115
|
+
* Generates a tooltip formatter function
|
|
133
116
|
*
|
|
134
|
-
* @param {
|
|
135
|
-
* @
|
|
136
|
-
* @param {string} label
|
|
137
|
-
* @returns
|
|
117
|
+
* @param {Object} options Tooltip format options
|
|
118
|
+
* @returns {Function} A function that formats tooltip content
|
|
138
119
|
*/
|
|
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])))
|
|
120
|
+
export function createTooltipFormatter(options = {}) {
|
|
121
|
+
return (d) => formatTooltipContent(d, options)
|
|
144
122
|
}
|
|
123
|
+
|
|
145
124
|
/**
|
|
146
|
-
*
|
|
125
|
+
* Calculates the transform attribute for SVG elements
|
|
147
126
|
*
|
|
148
|
-
* @param {
|
|
149
|
-
* @param {
|
|
150
|
-
* @returns {
|
|
127
|
+
* @param {number} x X position
|
|
128
|
+
* @param {number} y Y position
|
|
129
|
+
* @returns {string} Transform attribute value
|
|
151
130
|
*/
|
|
152
|
-
export function
|
|
153
|
-
return
|
|
154
|
-
(acc, item, index) => ({ ...acc, [item]: b[index % b.length] }),
|
|
155
|
-
{}
|
|
156
|
-
)
|
|
131
|
+
export function transform(x, y) {
|
|
132
|
+
return `translate(${x}, ${y})`
|
|
157
133
|
}
|
|
@@ -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() {}
|