@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,53 +1,53 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { Plot } from '../index.js'
|
|
3
|
+
import { dataset } from '@rokkit/data'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
// Sample data
|
|
6
|
+
const sampleData = [
|
|
7
|
+
{ model: 'Model A', name: 'Product 1', category: 'Electronics', count: 45 },
|
|
8
|
+
{ model: 'Model B', name: 'Product 2', category: 'Clothing', count: 32 },
|
|
9
|
+
{ model: 'Model C', name: 'Product 3', category: 'Electronics', count: 62 },
|
|
10
|
+
{ model: 'Model D', name: 'Product 4', category: 'Home', count: 28 },
|
|
11
|
+
{ model: 'Model E', name: 'Product 5', category: 'Electronics', count: 53 },
|
|
12
|
+
{ model: 'Model F', name: 'Product 6', category: 'Clothing', count: 24 },
|
|
13
|
+
{ model: 'Model G', name: 'Product 7', category: 'Home', count: 35 }
|
|
14
|
+
]
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
// Use the dataset class to process the data
|
|
17
|
+
const data = dataset(sampleData)
|
|
18
|
+
.groupBy('category')
|
|
19
|
+
.summarize('name', { count: (values) => values.length })
|
|
20
|
+
.rollup()
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
// Chart dimensions
|
|
23
|
+
const width = 600
|
|
24
|
+
const height = 400
|
|
25
|
+
const margin = { top: 20, right: 100, bottom: 60, left: 60 }
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
// Click handler for bars
|
|
28
|
+
function handleBarClick(item) {
|
|
29
|
+
console.log('Bar clicked:', item)
|
|
30
|
+
alert(`Clicked on ${item.category} with count ${item.count}`)
|
|
31
|
+
}
|
|
32
32
|
</script>
|
|
33
33
|
|
|
34
34
|
<div class="example">
|
|
35
|
-
|
|
35
|
+
<h2>Bar Chart Example</h2>
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
<div class="chart-wrapper">
|
|
38
|
+
<Plot.Root {data} {width} {height} {margin} fill="category">
|
|
39
|
+
<Plot.Grid direction="y" lineStyle="dashed" />
|
|
40
|
+
<Plot.Axis type="x" field="category" label="Product Category" />
|
|
41
|
+
<Plot.Axis type="y" field="count" label="Number of Products" />
|
|
42
|
+
<Plot.Bar x="category" y="count" fill="category" onClick={handleBarClick} />
|
|
43
|
+
<Plot.Legend title="Categories" />
|
|
44
|
+
</Plot.Root>
|
|
45
|
+
</div>
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
<script>
|
|
47
|
+
<div class="code-example">
|
|
48
|
+
<h3>Example Code:</h3>
|
|
49
|
+
<pre>
|
|
50
|
+
<!-- <script>
|
|
51
51
|
import { Plot } from '@rokkit/chart';
|
|
52
52
|
import { dataset } from '@rokkit/data';
|
|
53
53
|
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
<Plot.Axis type="y" field="count" label="Number of Products" />
|
|
76
76
|
<Plot.Bar x="category" y="count" fill="category" onClick={handleBarClick} />
|
|
77
77
|
<Plot.Legend title="Categories" />
|
|
78
|
-
</Plot.Root>
|
|
78
|
+
</Plot.Root> -->
|
|
79
79
|
</pre>
|
|
80
|
-
|
|
80
|
+
</div>
|
|
81
81
|
</div>
|
package/src/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import Root from './Plot/Root.svelte'
|
|
2
|
-
import Axis from './Plot/Axis.svelte'
|
|
3
|
-
import Bar from './Plot/Bar.svelte'
|
|
4
|
-
import Grid from './Plot/Grid.svelte'
|
|
5
|
-
import Legend from './Plot/Legend.svelte'
|
|
1
|
+
import Root from './Plot/Root.svelte'
|
|
2
|
+
import Axis from './Plot/Axis.svelte'
|
|
3
|
+
import Bar from './Plot/Bar.svelte'
|
|
4
|
+
import Grid from './Plot/Grid.svelte'
|
|
5
|
+
import Legend from './Plot/Legend.svelte'
|
|
6
6
|
|
|
7
7
|
// Export components
|
|
8
8
|
export const Plot = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
9
|
+
Root,
|
|
10
|
+
Axis,
|
|
11
|
+
Bar,
|
|
12
|
+
Grid,
|
|
13
|
+
Legend
|
|
14
|
+
}
|
|
15
15
|
|
|
16
16
|
// Export utilities
|
|
17
|
-
export { ChartBrewer } from './lib/brewing/index.svelte.js'
|
|
18
|
-
export * from './lib/brewing/index.svelte.js'
|
|
17
|
+
export { ChartBrewer } from './lib/brewing/index.svelte.js'
|
|
18
|
+
export * from './lib/brewing/index.svelte.js'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { scaleBand } from 'd3-scale'
|
|
1
|
+
// import { scaleBand } from 'd3-scale'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {} from './types.js'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @typedef {import('./types').TickData} TickData
|
|
@@ -11,7 +11,7 @@ import { } from './types.js';
|
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Creates x-axis tick data for rendering
|
|
14
|
-
*
|
|
14
|
+
*
|
|
15
15
|
* @param {Object} scales - Chart scales
|
|
16
16
|
* @param {Function} scales.x - X-axis scale
|
|
17
17
|
* @param {Object} dimensions - Chart dimensions
|
|
@@ -22,52 +22,50 @@ import { } from './types.js';
|
|
|
22
22
|
* @returns {AxisData} Axis rendering data
|
|
23
23
|
*/
|
|
24
24
|
export function createXAxis(scales, dimensions, options = {}) {
|
|
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
|
-
labelTransform: `translate(${dimensions.innerWidth / 2}, 35)`
|
|
65
|
-
};
|
|
25
|
+
if (!scales.x) return { ticks: [] }
|
|
26
|
+
|
|
27
|
+
const { tickCount = null, tickFormat = null, label = '' } = options
|
|
28
|
+
const xScale = scales.x
|
|
29
|
+
|
|
30
|
+
// Determine tick values
|
|
31
|
+
let tickValues
|
|
32
|
+
if (xScale.ticks) {
|
|
33
|
+
// For continuous scales (linear, time)
|
|
34
|
+
tickValues = tickCount !== null ? xScale.ticks(tickCount) : xScale.ticks()
|
|
35
|
+
} else {
|
|
36
|
+
// For band scales
|
|
37
|
+
tickValues = xScale.domain()
|
|
38
|
+
if (tickCount !== null && tickCount < tickValues.length) {
|
|
39
|
+
const step = Math.max(1, Math.floor(tickValues.length / tickCount))
|
|
40
|
+
tickValues = tickValues.filter((_, i) => i % step === 0)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Format ticks
|
|
45
|
+
const formatter = tickFormat || ((v) => v)
|
|
46
|
+
|
|
47
|
+
// Generate tick data
|
|
48
|
+
const ticks = tickValues.map((value) => {
|
|
49
|
+
const position = xScale.bandwidth ? xScale(value) + xScale.bandwidth() / 2 : xScale(value)
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
value,
|
|
53
|
+
position,
|
|
54
|
+
formattedValue: formatter(value)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
ticks,
|
|
60
|
+
label,
|
|
61
|
+
transform: `translate(0, ${dimensions.innerHeight})`,
|
|
62
|
+
labelTransform: `translate(${dimensions.innerWidth / 2}, 35)`
|
|
63
|
+
}
|
|
66
64
|
}
|
|
67
65
|
|
|
68
66
|
/**
|
|
69
67
|
* Creates y-axis tick data for rendering
|
|
70
|
-
*
|
|
68
|
+
*
|
|
71
69
|
* @param {Object} scales - Chart scales
|
|
72
70
|
* @param {Function} scales.y - Y-axis scale
|
|
73
71
|
* @param {Object} dimensions - Chart dimensions
|
|
@@ -78,45 +76,45 @@ export function createXAxis(scales, dimensions, options = {}) {
|
|
|
78
76
|
* @returns {AxisData} Axis rendering data
|
|
79
77
|
*/
|
|
80
78
|
export function createYAxis(scales, dimensions, options = {}) {
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
79
|
+
if (!scales.y) return { ticks: [] }
|
|
80
|
+
|
|
81
|
+
const { tickCount = null, tickFormat = null, label = '' } = options
|
|
82
|
+
const yScale = scales.y
|
|
83
|
+
|
|
84
|
+
// Determine tick values
|
|
85
|
+
let tickValues = []
|
|
86
|
+
|
|
87
|
+
if (yScale.ticks) {
|
|
88
|
+
tickValues = tickCount !== null ? yScale.ticks(tickCount) : yScale.ticks()
|
|
89
|
+
} else if (yScale.domain) {
|
|
90
|
+
tickValues = yScale.domain()
|
|
91
|
+
if (tickCount !== null && tickValues.length > tickCount) {
|
|
92
|
+
const step = Math.max(1, Math.floor(tickValues.length / tickCount))
|
|
93
|
+
tickValues = tickValues.filter((_, i) => i % step === 0)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Format ticks
|
|
98
|
+
const formatter = tickFormat || ((v) => v)
|
|
99
|
+
|
|
100
|
+
// Generate tick data
|
|
101
|
+
const ticks = tickValues.map((value) => ({
|
|
102
|
+
value,
|
|
103
|
+
position: yScale(value),
|
|
104
|
+
formattedValue: formatter(value)
|
|
105
|
+
}))
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
ticks,
|
|
109
|
+
label,
|
|
110
|
+
transform: 'translate(0, 0)',
|
|
111
|
+
labelTransform: `translate(-40, ${dimensions.innerHeight / 2}) rotate(-90)`
|
|
112
|
+
}
|
|
115
113
|
}
|
|
116
114
|
|
|
117
115
|
/**
|
|
118
116
|
* Creates grid line data for rendering
|
|
119
|
-
*
|
|
117
|
+
*
|
|
120
118
|
* @param {Object} scales - Chart scales
|
|
121
119
|
* @param {Object} dimensions - Chart dimensions
|
|
122
120
|
* @param {Object} options - Grid options
|
|
@@ -126,54 +124,54 @@ export function createYAxis(scales, dimensions, options = {}) {
|
|
|
126
124
|
* @returns {Object} Grid rendering data
|
|
127
125
|
*/
|
|
128
126
|
export function createGrid(scales, dimensions, options = {}) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
127
|
+
const { direction = 'both', xTickCount = null, yTickCount = null } = options
|
|
128
|
+
const result = { xLines: [], yLines: [] }
|
|
129
|
+
|
|
130
|
+
// Generate X grid lines (vertical)
|
|
131
|
+
if ((direction === 'x' || direction === 'both') && scales.x) {
|
|
132
|
+
const xAxis = createXAxis(scales, dimensions, { tickCount: xTickCount })
|
|
133
|
+
result.xLines = xAxis.ticks.map((tick) => ({
|
|
134
|
+
x1: tick.position,
|
|
135
|
+
y1: 0,
|
|
136
|
+
x2: tick.position,
|
|
137
|
+
y2: dimensions.innerHeight
|
|
138
|
+
}))
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Generate Y grid lines (horizontal)
|
|
142
|
+
if ((direction === 'y' || direction === 'both') && scales.y) {
|
|
143
|
+
const yAxis = createYAxis(scales, dimensions, { tickCount: yTickCount })
|
|
144
|
+
result.yLines = yAxis.ticks.map((tick) => ({
|
|
145
|
+
x1: 0,
|
|
146
|
+
y1: tick.position,
|
|
147
|
+
x2: dimensions.innerWidth,
|
|
148
|
+
y2: tick.position
|
|
149
|
+
}))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return result
|
|
155
153
|
}
|
|
156
154
|
|
|
157
155
|
/**
|
|
158
156
|
* Creates DOM attributes for a tick element
|
|
159
|
-
*
|
|
157
|
+
*
|
|
160
158
|
* @param {TickData} tick - Tick data
|
|
161
159
|
* @param {string} axis - Axis type ('x' or 'y')
|
|
162
160
|
* @returns {Object} Attributes for the tick
|
|
163
161
|
*/
|
|
164
162
|
export function createTickAttributes(tick, axis) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
163
|
+
if (axis === 'x') {
|
|
164
|
+
return {
|
|
165
|
+
'data-plot-tick': 'major',
|
|
166
|
+
transform: `translate(${tick.position}, 0)`,
|
|
167
|
+
'text-anchor': 'middle'
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
return {
|
|
171
|
+
'data-plot-tick': 'major',
|
|
172
|
+
transform: `translate(0, ${tick.position})`,
|
|
173
|
+
'text-anchor': 'end',
|
|
174
|
+
'dominant-baseline': 'middle'
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {} from './types.js'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @typedef {import('./types').BarData} BarData
|
|
@@ -9,7 +9,7 @@ import { } from './types.js';
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Creates bar data for rendering
|
|
12
|
-
*
|
|
12
|
+
*
|
|
13
13
|
* @param {Array} data - Chart data
|
|
14
14
|
* @param {Object} fields - Field mappings
|
|
15
15
|
* @param {string} fields.x - X-axis field
|
|
@@ -24,47 +24,47 @@ import { } from './types.js';
|
|
|
24
24
|
* @returns {BarData[]} Bar data for rendering
|
|
25
25
|
*/
|
|
26
26
|
export function createBars(data, fields, scales, dimensions, defaultColor = '#4682b4') {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
27
|
+
if (!data || data.length === 0 || !scales.x || !scales.y) return []
|
|
28
|
+
|
|
29
|
+
const { x: xField, y: yField, color: colorField } = fields
|
|
30
|
+
|
|
31
|
+
return data.map((d) => {
|
|
32
|
+
const barWidth = scales.x.bandwidth ? scales.x.bandwidth() : 10
|
|
33
|
+
const barX = scales.x.bandwidth ? scales.x(d[xField]) : scales.x(d[xField]) - barWidth / 2
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
data: d,
|
|
37
|
+
x: barX,
|
|
38
|
+
y: scales.y(d[yField]),
|
|
39
|
+
width: barWidth,
|
|
40
|
+
height: dimensions.innerHeight - scales.y(d[yField]),
|
|
41
|
+
color: colorField && scales.color ? scales.color(d[colorField]) : defaultColor
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Filter bars based on a selection criteria
|
|
48
|
-
*
|
|
48
|
+
*
|
|
49
49
|
* @param {BarData[]} bars - Bar data array
|
|
50
50
|
* @param {Object} selection - Selection criteria
|
|
51
51
|
* @returns {BarData[]} Filtered bars
|
|
52
52
|
*/
|
|
53
53
|
export function filterBars(bars, selection) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
if (!selection) return bars
|
|
55
|
+
|
|
56
|
+
return bars.filter((bar) => {
|
|
57
|
+
for (const [key, value] of Object.entries(selection)) {
|
|
58
|
+
if (bar.data[key] !== value) return false
|
|
59
|
+
}
|
|
60
|
+
return true
|
|
61
|
+
})
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* Creates a grouped bars layout
|
|
66
|
-
*
|
|
67
|
-
* @param {Array} data - Chart data
|
|
66
|
+
*
|
|
67
|
+
* @param {Array} data - Chart data
|
|
68
68
|
* @param {Object} fields - Field mappings
|
|
69
69
|
* @param {Object} scales - Chart scales
|
|
70
70
|
* @param {Object} dimensions - Chart dimensions
|
|
@@ -72,43 +72,43 @@ export function filterBars(bars, selection) {
|
|
|
72
72
|
* @returns {Object} Grouped bar data
|
|
73
73
|
*/
|
|
74
74
|
export function createGroupedBars(data, fields, scales, dimensions, options = {}) {
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
75
|
+
if (!data || data.length === 0 || !fields.group) return { groups: [], bars: [] }
|
|
76
|
+
|
|
77
|
+
const { x: xField, y: yField, group: groupField, color: colorField = groupField } = fields
|
|
78
|
+
const { padding = 0.1 } = options
|
|
79
|
+
|
|
80
|
+
// Get unique groups and x values
|
|
81
|
+
const groups = [...new Set(data.map((d) => d[groupField]))]
|
|
82
|
+
const xValues = [...new Set(data.map((d) => d[xField]))]
|
|
83
|
+
|
|
84
|
+
// Calculate group width and individual bar width
|
|
85
|
+
const xScale = scales.x
|
|
86
|
+
const groupWidth = xScale.bandwidth ? xScale.bandwidth() : 20
|
|
87
|
+
const barWidth = (groupWidth - padding * (groups.length - 1)) / groups.length
|
|
88
|
+
|
|
89
|
+
// Create bars for each group
|
|
90
|
+
const bars = []
|
|
91
|
+
|
|
92
|
+
xValues.forEach((xValue) => {
|
|
93
|
+
const groupItems = data.filter((d) => d[xField] === xValue)
|
|
94
|
+
const xPos = xScale(xValue)
|
|
95
|
+
|
|
96
|
+
groups.forEach((group, i) => {
|
|
97
|
+
const item = groupItems.find((d) => d[groupField] === group)
|
|
98
|
+
if (!item) return
|
|
99
|
+
|
|
100
|
+
const barX = xPos + i * (barWidth + padding)
|
|
101
|
+
bars.push({
|
|
102
|
+
data: item,
|
|
103
|
+
group,
|
|
104
|
+
x: barX,
|
|
105
|
+
y: scales.y(item[yField]),
|
|
106
|
+
width: barWidth,
|
|
107
|
+
height: dimensions.innerHeight - scales.y(item[yField]),
|
|
108
|
+
color: scales.color ? scales.color(item[colorField]) : '#4682b4'
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
return { groups, bars }
|
|
114
|
+
}
|