@rokkit/chart 1.0.0-next.121 → 1.0.0-next.123
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 +43 -35
- package/package.json +3 -3
- package/src/Plot/Axis.svelte +87 -95
- package/src/Plot/Bar.svelte +86 -86
- package/src/Plot/Grid.svelte +57 -57
- package/src/Plot/Legend.svelte +120 -122
- package/src/Plot/Root.svelte +107 -105
- package/src/elements/ColorRamp.svelte +19 -19
- package/src/elements/ContinuousLegend.svelte +1 -1
- package/src/elements/DefinePatterns.svelte +1 -1
- package/src/elements/DiscreteLegend.svelte +1 -1
- package/src/elements/SymbolGrid.svelte +1 -1
- package/src/examples/BarChartExample.svelte +42 -42
- package/src/index.js +13 -13
- package/src/lib/brewing/axes.svelte.js +120 -122
- package/src/lib/brewing/bars.svelte.js +70 -70
- package/src/lib/brewing/dimensions.svelte.js +25 -23
- package/src/lib/brewing/index.svelte.js +192 -204
- package/src/lib/brewing/legends.svelte.js +51 -52
- package/src/lib/brewing/scales.svelte.js +59 -68
- package/src/lib/brewing/types.js +2 -2
- package/src/lib/context.js +117 -107
- package/src/lib/scales.svelte.js +80 -87
- package/src/lib/utils.js +70 -72
- package/src/old_lib/chart.js +4 -4
- package/src/patterns/Brick.svelte +1 -1
- package/src/patterns/Circles.svelte +1 -1
- package/src/patterns/CrossHatch.svelte +1 -1
- package/src/patterns/Dots.svelte +1 -1
- package/src/patterns/OutlineCircles.svelte +1 -1
- package/src/patterns/Tile.svelte +1 -1
- package/src/patterns/Triangles.svelte +1 -1
- package/src/symbols/RoundedSquare.svelte +0 -1
- package/src/template/shapes/Circles.svelte +1 -1
- package/src/template/shapes/Lines.svelte +1 -1
- package/src/template/shapes/Polygons.svelte +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { min, max } from 'd3-array'
|
|
2
|
-
import { scaleBand, scaleLinear, scaleTime, scaleOrdinal } from 'd3-scale'
|
|
3
|
-
import { schemeCategory10 } from 'd3-scale-chromatic'
|
|
4
|
-
import {
|
|
1
|
+
import { min, max } from 'd3-array'
|
|
2
|
+
import { scaleBand, scaleLinear, scaleTime, scaleOrdinal } from 'd3-scale'
|
|
3
|
+
import { schemeCategory10 } from 'd3-scale-chromatic'
|
|
4
|
+
import {} from './types.js'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @typedef {import('./types').ChartScales} ChartScales
|
|
@@ -11,7 +11,7 @@ import { } from './types.js';
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Creates scales based on data, fields, and dimensions
|
|
14
|
-
*
|
|
14
|
+
*
|
|
15
15
|
* @param {Array} data - Chart data
|
|
16
16
|
* @param {ScaleFields} fields - Field mappings
|
|
17
17
|
* @param {Object} dimensions - Chart dimensions
|
|
@@ -20,75 +20,66 @@ import { } from './types.js';
|
|
|
20
20
|
* @returns {ChartScales} Chart scales
|
|
21
21
|
*/
|
|
22
22
|
export function createScales(data, fields, dimensions, options = {}) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.domain(colorValues)
|
|
72
|
-
.range(schemeCategory10);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return scales;
|
|
23
|
+
const scales = {
|
|
24
|
+
x: null,
|
|
25
|
+
y: null,
|
|
26
|
+
color: null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!data || data.length === 0 || !fields.x || !fields.y) {
|
|
30
|
+
return scales
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const padding = options.padding !== undefined ? options.padding : 0.2
|
|
34
|
+
|
|
35
|
+
// Extract values
|
|
36
|
+
const xValues = data.map((d) => d[fields.x])
|
|
37
|
+
const yValues = data.map((d) => d[fields.y])
|
|
38
|
+
|
|
39
|
+
// Determine x scale type
|
|
40
|
+
const xIsDate = xValues.some((v) => v instanceof Date)
|
|
41
|
+
const xIsNumeric = !xIsDate && xValues.every((v) => !isNaN(parseFloat(v)))
|
|
42
|
+
|
|
43
|
+
// Create x scale based on data type
|
|
44
|
+
if (xIsDate) {
|
|
45
|
+
scales.x = scaleTime()
|
|
46
|
+
.domain([min(xValues), max(xValues)])
|
|
47
|
+
.range([0, dimensions.innerWidth])
|
|
48
|
+
.nice()
|
|
49
|
+
} else if (xIsNumeric) {
|
|
50
|
+
scales.x = scaleLinear()
|
|
51
|
+
.domain([min([0, ...xValues]), max(xValues)])
|
|
52
|
+
.range([0, dimensions.innerWidth])
|
|
53
|
+
.nice()
|
|
54
|
+
} else {
|
|
55
|
+
scales.x = scaleBand().domain(xValues).range([0, dimensions.innerWidth]).padding(padding)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Create y scale
|
|
59
|
+
scales.y = scaleLinear()
|
|
60
|
+
.domain([0, max(yValues) * 1.1]) // Add 10% padding on top
|
|
61
|
+
.nice()
|
|
62
|
+
.range([dimensions.innerHeight, 0])
|
|
63
|
+
|
|
64
|
+
// Create color scale if color field is set
|
|
65
|
+
if (fields.color) {
|
|
66
|
+
const colorValues = [...new Set(data.map((d) => d[fields.color]))]
|
|
67
|
+
scales.color = scaleOrdinal().domain(colorValues).range(schemeCategory10)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return scales
|
|
76
71
|
}
|
|
77
72
|
|
|
78
73
|
/**
|
|
79
74
|
* Gets the origin coordinates for the axes
|
|
80
|
-
*
|
|
75
|
+
*
|
|
81
76
|
* @param {ChartScales} scales - Chart scales
|
|
82
77
|
* @param {Object} dimensions - Chart dimensions
|
|
83
78
|
* @returns {Object} Origin coordinates
|
|
84
79
|
*/
|
|
85
80
|
export function getOrigin(scales, dimensions) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
: 0
|
|
92
|
-
) : 0
|
|
93
|
-
};
|
|
94
|
-
}
|
|
81
|
+
return {
|
|
82
|
+
x: scales.y ? scales.y(0) : dimensions.innerHeight,
|
|
83
|
+
y: scales.x ? (scales.x.ticks ? scales.x(Math.max(0, min(scales.x.domain()))) : 0) : 0
|
|
84
|
+
}
|
|
85
|
+
}
|
package/src/lib/brewing/types.js
CHANGED
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
* @property {number} x - X position
|
|
51
51
|
* @property {number} y - Y position
|
|
52
52
|
* @property {number} width - Width of the bar
|
|
53
|
-
* @property {number} height - Height of the bar
|
|
53
|
+
* @property {number} height - Height of the bar
|
|
54
54
|
* @property {string} color - Color of the bar
|
|
55
55
|
*/
|
|
56
56
|
|
|
@@ -70,4 +70,4 @@
|
|
|
70
70
|
* @property {string} transform - SVG transform attribute value
|
|
71
71
|
*/
|
|
72
72
|
|
|
73
|
-
export {}
|
|
73
|
+
export {}
|
package/src/lib/context.js
CHANGED
|
@@ -1,122 +1,132 @@
|
|
|
1
|
-
import { getContext, setContext } from 'svelte'
|
|
2
|
-
import { writable, derived } from 'svelte/store'
|
|
3
|
-
import * as d3 from 'd3'
|
|
1
|
+
import { getContext, setContext } from 'svelte'
|
|
2
|
+
import { writable, derived } from 'svelte/store'
|
|
3
|
+
import * as d3 from 'd3'
|
|
4
4
|
|
|
5
|
-
const CHART_CONTEXT = 'chart-context'
|
|
5
|
+
const CHART_CONTEXT = 'chart-context'
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Creates chart context and provides it to child components
|
|
9
|
-
*
|
|
9
|
+
*
|
|
10
10
|
* @param {Object} options Initial chart options
|
|
11
11
|
* @returns {Object} Chart context with all stores and methods
|
|
12
12
|
*/
|
|
13
13
|
export function createChartContext(options = {}) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
|
113
123
|
}
|
|
114
124
|
|
|
115
125
|
/**
|
|
116
126
|
* Gets chart context provided by parent component
|
|
117
|
-
*
|
|
127
|
+
*
|
|
118
128
|
* @returns {Object} Chart context
|
|
119
129
|
*/
|
|
120
130
|
export function getChartContext() {
|
|
121
|
-
|
|
122
|
-
}
|
|
131
|
+
return getContext(CHART_CONTEXT)
|
|
132
|
+
}
|
package/src/lib/scales.svelte.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { scaleBand, scaleLinear, scaleTime, scaleOrdinal } from 'd3-scale'
|
|
2
|
-
import { schemeCategory10 } from 'd3-scale-chromatic'
|
|
3
|
-
import { min, max } from 'd3-array'
|
|
1
|
+
import { scaleBand, scaleLinear, scaleTime, scaleOrdinal } from 'd3-scale'
|
|
2
|
+
import { schemeCategory10 } from 'd3-scale-chromatic'
|
|
3
|
+
import { min, max } from 'd3-array'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Creates appropriate scales based on data and dimensions
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
8
|
* @param {Array} data The dataset
|
|
9
9
|
* @param {string} xKey Field to use for x-axis
|
|
10
10
|
* @param {string} yKey Field to use for y-axis
|
|
@@ -13,84 +13,77 @@ import { min, max } from 'd3-array';
|
|
|
13
13
|
* @returns {Object} Object containing xScale, yScale, and colorScale
|
|
14
14
|
*/
|
|
15
15
|
export function createScales(data, xKey, yKey, dimensions, options = {}) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
.domain(uniqueCategories)
|
|
57
|
-
.range(schemeCategory10);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return { xScale, yScale, colorScale };
|
|
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 }
|
|
61
56
|
}
|
|
62
57
|
|
|
63
58
|
/**
|
|
64
59
|
* Calculates the actual chart dimensions after applying margins
|
|
65
|
-
*
|
|
60
|
+
*
|
|
66
61
|
* @param {Object} dimensions Original dimensions
|
|
67
62
|
* @returns {Object} Dimensions with calculated inner width and height
|
|
68
63
|
*/
|
|
69
64
|
export function calculateChartDimensions(width, height, margin) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
return {
|
|
66
|
+
width,
|
|
67
|
+
height,
|
|
68
|
+
margin,
|
|
69
|
+
innerWidth: width - margin.left - margin.right,
|
|
70
|
+
innerHeight: height - margin.top - margin.bottom
|
|
71
|
+
}
|
|
77
72
|
}
|
|
78
73
|
|
|
79
74
|
/**
|
|
80
75
|
* Gets the axis origin value
|
|
81
|
-
*
|
|
76
|
+
*
|
|
82
77
|
* @param {Object} scale D3 scale
|
|
83
78
|
* @returns {number} Origin value
|
|
84
79
|
*/
|
|
85
80
|
export function getOriginValue(scale) {
|
|
86
|
-
|
|
87
|
-
? scale(Math.max(0, min(scale.domain())))
|
|
88
|
-
: scale.range()[0];
|
|
81
|
+
return scale.ticks ? scale(Math.max(0, min(scale.domain()))) : scale.range()[0]
|
|
89
82
|
}
|
|
90
83
|
|
|
91
84
|
/**
|
|
92
85
|
* Creates axis ticks
|
|
93
|
-
*
|
|
86
|
+
*
|
|
94
87
|
* @param {Object} scale D3 scale
|
|
95
88
|
* @param {string} axis Axis type ('x' or 'y')
|
|
96
89
|
* @param {number} count Number of ticks
|
|
@@ -98,32 +91,32 @@ export function getOriginValue(scale) {
|
|
|
98
91
|
* @returns {Array} Array of tick objects
|
|
99
92
|
*/
|
|
100
93
|
export function createTicks(scale, axis, count = null, fontSize = 12) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
94
|
+
const [minRange, maxRange] = scale.range()
|
|
95
|
+
let ticks = []
|
|
96
|
+
let offset = 0
|
|
104
97
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
// Calculate default count based on available space
|
|
99
|
+
if (!count) {
|
|
100
|
+
count = Math.abs((maxRange - minRange) / (fontSize * (axis === 'y' ? 3 : 6)))
|
|
101
|
+
}
|
|
109
102
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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)
|
|
116
109
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
+
}
|
|
123
116
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
117
|
+
// Format ticks with positions
|
|
118
|
+
return ticks.map((t) => ({
|
|
119
|
+
value: t,
|
|
120
|
+
position: scale(t) + (axis === 'x' ? offset : 0)
|
|
121
|
+
}))
|
|
122
|
+
}
|