1
- import { ChartData , ChartOptions , ChartType as ChartJSChartType } from 'chart.js'
1
+ import { ChartData , ChartDataset , ChartOptions , ChartType as ChartJSChartType } from 'chart.js'
2
2
3
+ import { LEGENDS_LABEL_CONFIG } from './constants'
3
4
import { ChartType , SimpleDataset } from './types'
4
5
5
6
const getCSSVariableValue = ( variableName : string ) => {
@@ -12,7 +13,7 @@ const getCSSVariableValue = (variableName: string) => {
12
13
console . error ( `CSS variable "${ variableName } " not found` )
13
14
}
14
15
15
- return value ?? 'rgba(0, 0, 0, 0.1) '
16
+ return value ?? 'transparent '
16
17
}
17
18
18
19
// Map our chart types to Chart.js types
@@ -35,64 +36,75 @@ export const getDefaultOptions = (type: ChartType): ChartOptions => {
35
36
const baseOptions : ChartOptions = {
36
37
responsive : true ,
37
38
maintainAspectRatio : false ,
39
+ devicePixelRatio : 3 ,
38
40
plugins : {
39
41
legend : {
40
42
position : 'bottom' as const ,
43
+ labels : LEGENDS_LABEL_CONFIG ,
41
44
} ,
42
45
title : {
43
46
display : false ,
44
47
} ,
45
48
} ,
49
+ elements : {
50
+ line : {
51
+ fill : true ,
52
+ tension : 0.4 ,
53
+ } ,
54
+ bar : {
55
+ borderSkipped : 'start' as const ,
56
+ borderWidth : 2 ,
57
+ borderColor : 'transparent' ,
58
+ borderRadius : 4 ,
59
+ } ,
60
+ arc : {
61
+ spacing : 2 ,
62
+ } ,
63
+ } ,
64
+ }
65
+
66
+ const gridConfig = {
67
+ color : getCSSVariableValue ( '--N50' ) ,
46
68
}
47
69
48
70
switch ( type ) {
49
71
case 'area' :
50
72
return {
51
73
...baseOptions ,
52
- elements : {
53
- line : {
54
- fill : true ,
74
+ plugins : {
75
+ ...baseOptions . plugins ,
76
+ tooltip : {
77
+ mode : 'index' ,
55
78
} ,
56
79
} ,
80
+ interaction : {
81
+ mode : 'nearest' ,
82
+ axis : 'x' ,
83
+ intersect : false ,
84
+ } ,
57
85
scales : {
58
86
y : {
87
+ stacked : true ,
59
88
beginAtZero : true ,
60
- grid : {
61
- color : getCSSVariableValue ( '--N50' ) ,
62
- } ,
89
+ grid : gridConfig ,
63
90
} ,
64
91
x : {
65
- grid : {
66
- color : getCSSVariableValue ( '--N50' ) ,
67
- } ,
92
+ grid : gridConfig ,
68
93
} ,
69
94
} ,
70
- }
95
+ } as ChartOptions < 'line' >
71
96
case 'stackedBar' :
72
97
return {
73
98
...baseOptions ,
74
99
scales : {
75
100
x : {
76
101
stacked : true ,
77
- grid : {
78
- color : getCSSVariableValue ( '--N50' ) ,
79
- } ,
102
+ grid : gridConfig ,
80
103
} ,
81
104
y : {
82
105
stacked : true ,
83
106
beginAtZero : true ,
84
- grid : {
85
- color : getCSSVariableValue ( '--N50' ) ,
86
- } ,
87
- } ,
88
- } ,
89
- elements : {
90
- bar : {
91
- // Add gap between bars
92
- borderSkipped : 'start' ,
93
- borderWidth : 2 ,
94
- borderColor : 'transparent' ,
95
- borderRadius : 4 ,
107
+ grid : gridConfig ,
96
108
} ,
97
109
} ,
98
110
}
@@ -104,24 +116,11 @@ export const getDefaultOptions = (type: ChartType): ChartOptions => {
104
116
x : {
105
117
stacked : true ,
106
118
beginAtZero : true ,
107
- grid : {
108
- color : getCSSVariableValue ( '--N50' ) ,
109
- } ,
119
+ grid : gridConfig ,
110
120
} ,
111
121
y : {
112
122
stacked : true ,
113
- grid : {
114
- color : getCSSVariableValue ( '--N50' ) ,
115
- } ,
116
- } ,
117
- } ,
118
- elements : {
119
- bar : {
120
- // Add gap between bars
121
- borderSkipped : 'start' ,
122
- borderWidth : 2 ,
123
- borderColor : 'transparent' ,
124
- borderRadius : 4 ,
123
+ grid : gridConfig ,
125
124
} ,
126
125
} ,
127
126
}
@@ -135,32 +134,39 @@ export const getDefaultOptions = (type: ChartType): ChartOptions => {
135
134
align : 'center' ,
136
135
} ,
137
136
} ,
138
- elements : {
139
- arc : {
140
- spacing : 2 ,
141
- } ,
142
- } ,
143
137
}
144
138
default :
145
139
return baseOptions
146
140
}
147
141
}
148
142
149
- // Define color palette for consistent styling
150
- const getColorPalette = ( ) => [
151
- 'rgba(54, 162, 235, 0.8)' , // Blue
152
- 'rgba(255, 99, 132, 0.8)' , // Red
153
- 'rgba(255, 205, 86, 0.8)' , // Yellow
154
- 'rgba(75, 192, 192, 0.8)' , // Green
155
- 'rgba(153, 102, 255, 0.8)' , // Purple
156
- 'rgba(255, 159, 64, 0.8)' , // Orange
157
- 'rgba(199, 199, 199, 0.8)' , // Grey
158
- 'rgba(83, 102, 255, 0.8)' , // Indigo
159
- ]
143
+ // Generates a palette of pastel HSL colors
144
+ const generateColors = ( count : number ) : string [ ] => {
145
+ const colors : string [ ] = [ ]
146
+ for ( let i = 0 ; i < count ; i ++ ) {
147
+ const hue = ( i * 360 ) / count
148
+ const saturation = 50 // Pastel: 40-60%
149
+ const lightness = 75 // Pastel: 80-90%
150
+ colors . push ( `hsl(${ hue } , ${ saturation } %, ${ lightness } %)` )
151
+ }
152
+ return colors
153
+ }
154
+
155
+ // Generates a slightly darker shade for a given HSL color string
156
+ const generateCorrespondingBorderColor = ( hsl : string ) : string => {
157
+ // Parse hsl string: hsl(hue, saturation%, lightness%)
158
+ const match = hsl . match ( / h s l \( ( \d + ) , \s * ( [ \d . ] + ) % , \s * ( [ \d . ] + ) % \) / )
159
+ if ( ! match ) throw new Error ( 'Invalid HSL color format' )
160
+ const hue = Number ( match [ 1 ] )
161
+ const saturation = Number ( match [ 2 ] )
162
+ let lightness = Number ( match [ 3 ] )
163
+ lightness = Math . max ( 0 , lightness - 15 ) // Clamp to 0
164
+ return `hsl(${ hue } , ${ saturation } %, ${ lightness } %)`
165
+ }
160
166
161
167
// Transform simple data to Chart.js format with consistent styling
162
168
export const transformDataForChart = ( labels : string [ ] , datasets : SimpleDataset [ ] , type : ChartType ) : ChartData => {
163
- const colors = getColorPalette ( )
169
+ const colors = generateColors ( type === 'pie' ? datasets [ 0 ] . data . length : datasets . length )
164
170
165
171
const transformedDatasets = datasets . map ( ( dataset , index ) => {
166
172
const colorIndex = index % colors . length
@@ -175,20 +181,21 @@ export const transformDataForChart = (labels: string[], datasets: SimpleDataset[
175
181
return {
176
182
...baseDataset ,
177
183
fill : true ,
178
- tension : 0.4 ,
179
184
pointRadius : 0 ,
180
- backgroundColor : colors [ colorIndex ] . replace ( '0.8' , '0.2' ) ,
181
- }
185
+ pointHoverRadius : 10 ,
186
+ pointHitRadius : 20 ,
187
+ pointStyle : 'rectRounded' ,
188
+ pointBorderWidth : 0 ,
189
+ borderWidth : 2 ,
190
+ borderColor : generateCorrespondingBorderColor ( colors [ colorIndex ] ) ,
191
+ } as ChartDataset < 'line' >
182
192
case 'pie' :
183
193
return {
184
194
...baseDataset ,
185
195
backgroundColor : colors . slice ( 0 , dataset . data . length ) ,
186
196
}
187
197
case 'stackedBar' :
188
198
case 'stackedBarHorizontal' :
189
- return {
190
- ...baseDataset ,
191
- }
192
199
default :
193
200
return baseDataset
194
201
}
0 commit comments