@@ -6,7 +6,7 @@ import { areArraysEqual } from '../../utilities/utilities';
6
6
import { DataVizPalette , getColorFromToken , getNextColor } from '../../utilities/colors' ;
7
7
import { scaleLinear as d3ScaleLinear } from 'd3-scale' ;
8
8
9
- type PlotlyColorway = 'plotly' | 'others' ;
9
+ type PlotlyColorway = 'plotly' | 'd3' | ' others';
10
10
11
11
// The color sequences in plotly express are defined here:
12
12
// https://plotly.com/python/discrete-color/#:~:text=Join%20now.-,Color%20Sequences%20in%20Plotly%20Express,-By%20default%2C%20Plotly
@@ -25,6 +25,21 @@ const DEFAULT_PLOTLY_COLORWAY = [
25
25
'#fecb52' , //10
26
26
] ;
27
27
28
+ // Default D3 qualitative colorway (Category10), matching Plotly Express px.colors.qualitative.D3
29
+ // Source: https://plotly.com/python/discrete-color/#:~:text=Join%20now.-,Color%20Sequences%20in%20Plotly%20Express,-By%20default%2C%20Plotly
30
+ export const DEFAULT_D3_COLORWAY = [
31
+ '#1f77b4' , //1
32
+ '#ff7f0e' , //2
33
+ '#2ca02c' , //3
34
+ '#d62728' , //4
35
+ '#9467bd' , //5
36
+ '#8c564b' , //6
37
+ '#e377c2' , //7
38
+ '#7f7f7f' , //8
39
+ '#bcbd22' , //9
40
+ '#17becf' , //10
41
+ ] ;
42
+
28
43
const PLOTLY_FLUENTVIZ_COLORWAY_MAPPING = [
29
44
DataVizPalette . color1 , //1
30
45
DataVizPalette . warning , //2
@@ -38,38 +53,74 @@ const PLOTLY_FLUENTVIZ_COLORWAY_MAPPING = [
38
53
DataVizPalette . color10 , //10
39
54
] ;
40
55
41
- function getPlotlyColorway ( colorway : string [ ] | undefined ) : PlotlyColorway {
42
- const isPlotlyColorway =
43
- isArrayOrTypedArray ( colorway ) &&
44
- areArraysEqual (
45
- colorway ?. map ( c => c . toLowerCase ( ) ) ,
46
- DEFAULT_PLOTLY_COLORWAY ,
47
- ) ;
56
+ // Mapping from D3 Category10 order to Fluent DataViz tokens (light/dark handled via getColorFromToken)
57
+ // D3: [blue, orange, green, red, purple, brown, pink, gray, olive, cyan]
58
+ export const D3_FLUENTVIZ_COLORWAY_MAPPING : string [ ] = [
59
+ DataVizPalette . color26 , // blue -> lightBlue.shade10
60
+ DataVizPalette . warning , // orange -> semantic warning
61
+ DataVizPalette . color5 , // green -> lightGreen.primary
62
+ DataVizPalette . error , // red -> semantic error
63
+ DataVizPalette . color4 , // purple -> orchid.tint10
64
+ DataVizPalette . color17 , // brown -> pumpkin.shade20
65
+ DataVizPalette . color22 , // pink -> hotPink.tint20
66
+ DataVizPalette . disabled , // gray -> semantic disabled
67
+ DataVizPalette . color10 , // olive/yellow-green -> gold.shade10
68
+ DataVizPalette . color3 , // cyan/teal -> teal.tint20
69
+ ] ;
48
70
49
- return isPlotlyColorway ? 'plotly' : 'others' ;
71
+ function getPlotlyColorway ( colorway : string [ ] | undefined , isDonut : boolean = false ) : PlotlyColorway {
72
+ if ( ! colorway || ! isArrayOrTypedArray ( colorway ) ) {
73
+ return 'others' ;
74
+ }
75
+ const lower = colorway . map ( c => c . toLowerCase ( ) ) ;
76
+ if ( isDonut && areArraysEqual ( lower , D3_FLUENTVIZ_COLORWAY_MAPPING ) ) {
77
+ return 'd3' ;
78
+ }
79
+ if ( areArraysEqual ( lower , DEFAULT_PLOTLY_COLORWAY ) ) {
80
+ return 'plotly' ;
81
+ }
82
+ return 'others' ;
50
83
}
51
84
52
- function tryMapFluentDataViz ( hexColor : string , templateColorway : PlotlyColorway , isDarkTheme ?: boolean ) : string {
85
+ function tryMapFluentDataViz (
86
+ hexColor : string ,
87
+ templateColorway : PlotlyColorway ,
88
+ isDarkTheme ?: boolean ,
89
+ isDonut ?: boolean ,
90
+ ) : string {
53
91
if ( templateColorway !== 'plotly' ) {
54
92
return hexColor ;
55
93
}
56
- const index = DEFAULT_PLOTLY_COLORWAY . indexOf ( hexColor . toLowerCase ( ) ) ;
57
- if ( index !== - 1 ) {
58
- return getColorFromToken ( PLOTLY_FLUENTVIZ_COLORWAY_MAPPING [ index ] , isDarkTheme ) ;
94
+ let defaultColorway : string [ ] = DEFAULT_PLOTLY_COLORWAY ;
95
+ let defaultMapping : string [ ] = PLOTLY_FLUENTVIZ_COLORWAY_MAPPING ;
96
+ if ( isDonut ) {
97
+ defaultColorway = templateColorway === 'plotly' ? DEFAULT_PLOTLY_COLORWAY : DEFAULT_D3_COLORWAY ;
98
+ defaultMapping = templateColorway === 'plotly' ? PLOTLY_FLUENTVIZ_COLORWAY_MAPPING : D3_FLUENTVIZ_COLORWAY_MAPPING ;
99
+ }
100
+ const idx = defaultColorway . indexOf ( hexColor . toLowerCase ( ) ) ;
101
+ if ( idx !== - 1 ) {
102
+ return getColorFromToken ( defaultMapping [ idx ] , ! ! isDarkTheme ) ;
59
103
}
60
104
return hexColor ;
61
105
}
62
106
63
107
export const getColor = (
64
108
legendLabel : string ,
65
109
colorMap : React . MutableRefObject < Map < string , string > > ,
110
+ templateColorway : PlotlyColorway ,
66
111
isDarkTheme ?: boolean ,
112
+ isDonut ?: boolean ,
67
113
) : string => {
68
114
if ( ! colorMap . current . has ( legendLabel ) ) {
69
115
let nextColor : string ;
70
- if ( colorMap . current . size < PLOTLY_FLUENTVIZ_COLORWAY_MAPPING . length ) {
116
+ const defaultColorMapping = isDonut
117
+ ? templateColorway === 'plotly'
118
+ ? PLOTLY_FLUENTVIZ_COLORWAY_MAPPING
119
+ : D3_FLUENTVIZ_COLORWAY_MAPPING
120
+ : PLOTLY_FLUENTVIZ_COLORWAY_MAPPING ;
121
+ if ( colorMap . current . size < defaultColorMapping . length ) {
71
122
// Get first 10 colors from plotly-fluentviz colorway mapping
72
- nextColor = getColorFromToken ( PLOTLY_FLUENTVIZ_COLORWAY_MAPPING [ colorMap . current . size ] , isDarkTheme ) ;
123
+ nextColor = getColorFromToken ( defaultColorMapping [ colorMap . current . size ] , isDarkTheme ) ;
73
124
} else {
74
125
nextColor = getNextColor ( colorMap . current . size , 0 , isDarkTheme ) ;
75
126
}
@@ -85,17 +136,18 @@ export const getSchemaColors = (
85
136
colors : PieColors | Color | Color [ ] | string | null | undefined ,
86
137
colorMap : React . MutableRefObject < Map < string , string > > ,
87
138
isDarkTheme ?: boolean ,
139
+ isDonut ?: boolean ,
88
140
) : string [ ] | string | undefined => {
89
141
const hexColors : string [ ] = [ ] ;
90
142
if ( ! colors ) {
91
143
return undefined ;
92
144
}
93
- const templateColorway = getPlotlyColorway ( colorway ) ;
145
+ const templateColorway = getPlotlyColorway ( colorway , isDonut ) ;
94
146
if ( isArrayOrTypedArray ( colors ) ) {
95
147
// eslint-disable-next-line @typescript-eslint/no-explicit-any
96
148
( colors as any [ ] ) . forEach ( ( element , index ) => {
97
149
const colorString = element ?. toString ( ) . trim ( ) ;
98
- const nextFluentColor = getColor ( `Label_${ index } ` , colorMap , isDarkTheme ) ;
150
+ const nextFluentColor = getColor ( `Label_${ index } ` , colorMap , templateColorway , isDarkTheme , isDonut ) ;
99
151
if ( colorString ) {
100
152
const parsedColor = d3Color ( colorString ) ;
101
153
hexColors . push (
@@ -109,7 +161,7 @@ export const getSchemaColors = (
109
161
const parsedColor = d3Color ( colors ) ;
110
162
return parsedColor
111
163
? tryMapFluentDataViz ( parsedColor . formatHex ( ) , templateColorway , isDarkTheme )
112
- : getColor ( 'Label_0' , colorMap , isDarkTheme ) ;
164
+ : getColor ( 'Label_0' , colorMap , templateColorway , isDarkTheme , isDonut ) ;
113
165
}
114
166
return hexColors ;
115
167
} ;
@@ -120,24 +172,30 @@ export const extractColor = (
120
172
colors : PieColors | Color | Color [ ] | string | null | undefined ,
121
173
colorMap : React . MutableRefObject < Map < string , string > > ,
122
174
isDarkTheme ?: boolean ,
175
+ isDonut ?: boolean ,
123
176
) : string | string [ ] | undefined => {
124
- return colorwayType === 'default' && colors ? getSchemaColors ( colorway , colors , colorMap , isDarkTheme ) : undefined ;
177
+ return colorwayType === 'default' && colors
178
+ ? getSchemaColors ( colorway , colors , colorMap , isDarkTheme , isDonut )
179
+ : undefined ;
125
180
} ;
126
181
127
182
export const resolveColor = (
128
183
extractedColors : string [ ] | string | null | undefined ,
129
184
index : number ,
130
185
legend : string ,
131
186
colorMap : React . MutableRefObject < Map < string , string > > ,
187
+ colorway : string [ ] | undefined ,
132
188
isDarkTheme ?: boolean ,
189
+ isDonut ?: boolean ,
133
190
) : string => {
134
191
let color = '' ;
192
+ const templateColorway = getPlotlyColorway ( colorway , isDonut ) ;
135
193
if ( extractedColors && isArrayOrTypedArray ( extractedColors ) && extractedColors . length > 0 ) {
136
194
color = extractedColors [ index % extractedColors . length ] ;
137
195
} else if ( typeof extractedColors === 'string' ) {
138
196
color = extractedColors ;
139
197
} else {
140
- color = getColor ( legend , colorMap , isDarkTheme ) ;
198
+ color = getColor ( legend , colorMap , templateColorway , isDarkTheme , isDonut ) ;
141
199
}
142
200
return color ;
143
201
} ;
0 commit comments