@automattic/charts 4.3.0 → 4.5.0
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/CHANGELOG.md +20 -0
- package/TOKENS.md +2 -2
- package/dist/index.cjs +584 -209
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +4 -0
- package/dist/index.d.cts +14 -3
- package/dist/index.d.ts +14 -3
- package/dist/index.js +585 -210
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/charts/bar-chart/bar-chart.tsx +57 -23
- package/src/charts/bar-chart/private/band-tooltip.tsx +18 -12
- package/src/charts/bar-chart/private/classified-bar-series.tsx +85 -0
- package/src/charts/bar-chart/private/comparison-bars-geometry.ts +3 -6
- package/src/charts/bar-chart/private/index.ts +1 -0
- package/src/charts/bar-chart/private/test/comparison-bars-geometry.test.ts +2 -3
- package/src/charts/bar-chart/test/bar-chart.test.tsx +130 -1
- package/src/charts/bar-chart/types.ts +5 -1
- package/src/charts/geo-chart/geo-chart.tsx +4 -0
- package/src/charts/geo-chart/test/geo-chart.test.tsx +8 -0
- package/src/charts/geo-chart/types.ts +5 -0
- package/src/charts/heatmap-chart/test/heatmap-chart.test.tsx +1 -1
- package/src/charts/pie-chart/pie-chart.module.scss +4 -0
- package/src/charts/pie-chart/pie-chart.tsx +103 -7
- package/src/charts/pie-chart/test/pie-chart.test.tsx +149 -0
- package/src/charts/private/chart-layout/chart-layout.tsx +5 -1
- package/src/components/tooltip/accessible-tooltip.tsx +40 -4
- package/src/components/tooltip/test/accessible-tooltip.test.tsx +82 -5
- package/src/providers/chart-context/global-charts-provider.tsx +57 -41
- package/src/providers/chart-context/private/catalog-pointers.ts +9 -1
- package/src/providers/chart-context/private/palette-generator.ts +305 -0
- package/src/providers/chart-context/private/perceptual-color.ts +219 -0
- package/src/providers/chart-context/private/series-palette.ts +1 -1
- package/src/providers/chart-context/test/chart-context.test.tsx +88 -8
- package/src/providers/chart-context/test/global-charts-provider-first-render.test.tsx +27 -0
- package/src/providers/chart-context/test/palette-generator.test.ts +181 -0
- package/src/providers/chart-context/test/perceptual-color.test.ts +75 -0
- package/src/test-utils/zoned-parsing-suite.ts +1 -1
- package/src/utils/color-utils.ts +5 -3
- package/src/utils/test/date-formatting.test.ts +1 -1
- package/src/providers/chart-context/private/get-chart-color.ts +0 -223
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import { hsl as d3Hsl } from '@visx/vendor/d3-color';
|
|
2
|
-
import { getColorDistance } from '../../../utils';
|
|
3
|
-
|
|
4
|
-
export interface ColorCache {
|
|
5
|
-
colors: string[];
|
|
6
|
-
hues: number[];
|
|
7
|
-
existingHslColors: Array< [ number, number, number ] >;
|
|
8
|
-
minHue: number;
|
|
9
|
-
maxHue: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Golden ratio for mathematically pleasing color distribution
|
|
14
|
-
* Used to generate evenly spaced hues that are visually distinct
|
|
15
|
-
*/
|
|
16
|
-
const GOLDEN_RATIO = 0.618033988749;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Minimum perceptual distance between colors to ensure visual distinction
|
|
20
|
-
* Based on weighted HSL distance calculation optimized for chart readability
|
|
21
|
-
*/
|
|
22
|
-
const MIN_COLOR_DISTANCE = 25;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Maximum attempts to find a sufficiently different color
|
|
26
|
-
* Prevents infinite loops while allowing reasonable search space
|
|
27
|
-
*/
|
|
28
|
-
const MAX_COLOR_GENERATION_ATTEMPTS = 50;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Color variation attempt offset
|
|
32
|
-
* Small increment to explore slightly different color variations per attempt
|
|
33
|
-
*/
|
|
34
|
-
const VARIATION_ATTEMPT_OFFSET = 0.1;
|
|
35
|
-
|
|
36
|
-
// Saturation configuration for generated colors
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Base saturation percentage for generated colors
|
|
40
|
-
* 45% provides muted, professional colors without being washed out
|
|
41
|
-
*/
|
|
42
|
-
const BASE_SATURATION = 45;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Number of saturation variation steps
|
|
46
|
-
* Creates 3 different saturation levels for variety
|
|
47
|
-
*/
|
|
48
|
-
const SATURATION_VARIATION_STEPS = 3;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Saturation increment per variation step
|
|
52
|
-
* 10% increments provide subtle variation while keeping colors muted
|
|
53
|
-
* Results in saturation levels: 45%, 55%, 65%
|
|
54
|
-
*/
|
|
55
|
-
const SATURATION_INCREMENT = 10;
|
|
56
|
-
|
|
57
|
-
// Lightness configuration for WCAG AA accessibility compliance
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Base lightness percentage for generated colors
|
|
61
|
-
* 35% ensures sufficient contrast against white backgrounds for WCAG AA compliance
|
|
62
|
-
* WCAG AA requires 4.5:1 contrast ratio for normal text
|
|
63
|
-
*/
|
|
64
|
-
const BASE_LIGHTNESS = 35;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Number of lightness variation steps
|
|
68
|
-
* Creates 4 different lightness levels for variety
|
|
69
|
-
*/
|
|
70
|
-
const LIGHTNESS_VARIATION_STEPS = 4;
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Lightness increment per variation step
|
|
74
|
-
* 8% increments provide subtle lightness variation while maintaining accessibility
|
|
75
|
-
* Results in lightness levels: 35%, 43%, 51%, 59%
|
|
76
|
-
* All levels maintain WCAG AA compliance against white backgrounds
|
|
77
|
-
*/
|
|
78
|
-
const LIGHTNESS_INCREMENT = 8;
|
|
79
|
-
|
|
80
|
-
// Hue range expansion and constraints
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Minimum hue range in degrees to ensure sufficient color variety
|
|
84
|
-
* 60 degrees provides reasonable color spread even for narrow palettes
|
|
85
|
-
*/
|
|
86
|
-
const MIN_HUE_RANGE_DEGREES = 60;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Hue range expansion factor
|
|
90
|
-
* 1.3x expansion provides slightly more variety than the original palette
|
|
91
|
-
*/
|
|
92
|
-
const HUE_RANGE_EXPANSION_FACTOR = 1.3;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Threshold for detecting hue wrap-around (color wheel boundary crossing)
|
|
96
|
-
* 180 degrees indicates the colors span more than half the color wheel
|
|
97
|
-
*/
|
|
98
|
-
const HUE_WRAP_THRESHOLD_DEGREES = 180;
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Full color wheel rotation in degrees
|
|
102
|
-
*/
|
|
103
|
-
const FULL_HUE_ROTATION_DEGREES = 360;
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Factor for single color hue range
|
|
107
|
-
*/
|
|
108
|
-
const SINGLE_COLOR_HUE_RANGE_FACTOR = 0.33;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get a color from the colors array or generate a new color using the golden ratio
|
|
112
|
-
*
|
|
113
|
-
* @param index - the index of the color to get
|
|
114
|
-
* @param colorCache - pre-computed color data for performance
|
|
115
|
-
* @return a color from the colors array or a new color using the golden ratio
|
|
116
|
-
*/
|
|
117
|
-
export const getChartColor = ( index: number, colorCache: ColorCache ): string => {
|
|
118
|
-
const {
|
|
119
|
-
colors,
|
|
120
|
-
hues,
|
|
121
|
-
existingHslColors,
|
|
122
|
-
minHue: cachedMinHue,
|
|
123
|
-
maxHue: cachedMaxHue,
|
|
124
|
-
} = colorCache;
|
|
125
|
-
|
|
126
|
-
if ( index < colors.length ) {
|
|
127
|
-
return colors[ index ];
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
let minHue = cachedMinHue;
|
|
131
|
-
let maxHue = cachedMaxHue;
|
|
132
|
-
|
|
133
|
-
// Generate additional colors using golden ratio, avoiding similar colors
|
|
134
|
-
for ( let attempt = 0; attempt < MAX_COLOR_GENERATION_ATTEMPTS; attempt++ ) {
|
|
135
|
-
// Calculate hue using golden ratio distribution with variation per attempt
|
|
136
|
-
// Formula: ((base_index + attempt_variation) * golden_ratio * 360°) mod 360°
|
|
137
|
-
// This ensures mathematically pleasing spacing while allowing slight shifts per attempt
|
|
138
|
-
let hue =
|
|
139
|
-
( ( index - colors.length + attempt * VARIATION_ATTEMPT_OFFSET ) *
|
|
140
|
-
GOLDEN_RATIO *
|
|
141
|
-
FULL_HUE_ROTATION_DEGREES ) %
|
|
142
|
-
FULL_HUE_ROTATION_DEGREES;
|
|
143
|
-
|
|
144
|
-
// If we have existing colors, constrain new colors to their hue range
|
|
145
|
-
if ( hues.length > 0 ) {
|
|
146
|
-
// Handle hue wrap-around (e.g., if colors span across 0 degrees)
|
|
147
|
-
let hueRange = maxHue - minHue;
|
|
148
|
-
|
|
149
|
-
// If there's only one color, use a set hue range for limited variety
|
|
150
|
-
if ( hues.length === 1 ) {
|
|
151
|
-
hueRange = FULL_HUE_ROTATION_DEGREES * SINGLE_COLOR_HUE_RANGE_FACTOR;
|
|
152
|
-
} else if ( hueRange > HUE_WRAP_THRESHOLD_DEGREES ) {
|
|
153
|
-
// If the range is very large, it might be wrapping around the color wheel
|
|
154
|
-
// Check if a smaller range exists when considering wrap-around
|
|
155
|
-
const altMinHue = Math.min( ...hues.filter( h => h > HUE_WRAP_THRESHOLD_DEGREES ) );
|
|
156
|
-
const altMaxHue =
|
|
157
|
-
Math.max( ...hues.filter( h => h < HUE_WRAP_THRESHOLD_DEGREES ) ) +
|
|
158
|
-
FULL_HUE_ROTATION_DEGREES;
|
|
159
|
-
const altRange = altMaxHue - altMinHue;
|
|
160
|
-
|
|
161
|
-
if ( altRange < hueRange ) {
|
|
162
|
-
minHue = altMinHue;
|
|
163
|
-
maxHue = altMaxHue;
|
|
164
|
-
hueRange = altRange;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Expand the range slightly to provide some variation
|
|
169
|
-
const expandedRange = Math.max(
|
|
170
|
-
hueRange * HUE_RANGE_EXPANSION_FACTOR,
|
|
171
|
-
MIN_HUE_RANGE_DEGREES
|
|
172
|
-
);
|
|
173
|
-
const rangeCenter = ( minHue + maxHue ) / 2;
|
|
174
|
-
const expandedMin = rangeCenter - expandedRange / 2;
|
|
175
|
-
|
|
176
|
-
// Map the generated hue to the expanded range
|
|
177
|
-
hue = expandedMin + ( hue / FULL_HUE_ROTATION_DEGREES ) * expandedRange;
|
|
178
|
-
|
|
179
|
-
// Normalize to 0-360 range
|
|
180
|
-
hue =
|
|
181
|
-
( ( hue % FULL_HUE_ROTATION_DEGREES ) + FULL_HUE_ROTATION_DEGREES ) %
|
|
182
|
-
FULL_HUE_ROTATION_DEGREES;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const saturation =
|
|
186
|
-
BASE_SATURATION + ( ( index + attempt ) % SATURATION_VARIATION_STEPS ) * SATURATION_INCREMENT;
|
|
187
|
-
const lightness =
|
|
188
|
-
BASE_LIGHTNESS + ( ( index + attempt ) % LIGHTNESS_VARIATION_STEPS ) * LIGHTNESS_INCREMENT;
|
|
189
|
-
|
|
190
|
-
const candidateHsl: [ number, number, number ] = [ hue, saturation, lightness ];
|
|
191
|
-
|
|
192
|
-
// Check if this color is sufficiently different from existing colors
|
|
193
|
-
let isSufficientlyDifferent = true;
|
|
194
|
-
for ( const existingHsl of existingHslColors ) {
|
|
195
|
-
if ( getColorDistance( candidateHsl, existingHsl ) < MIN_COLOR_DISTANCE ) {
|
|
196
|
-
isSufficientlyDifferent = false;
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if ( isSufficientlyDifferent ) {
|
|
202
|
-
// d3-color uses 0-1 scale for saturation and lightness
|
|
203
|
-
return d3Hsl( Math.round( hue ), saturation / 100, lightness / 100 ).formatHex();
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Fallback if we couldn't find a sufficiently different color
|
|
208
|
-
// Formula: ((base_index) * golden_ratio * 360°) mod 360°
|
|
209
|
-
// This ensures mathematically pleasing spacing while maintaining consistency
|
|
210
|
-
const fallbackHue =
|
|
211
|
-
( ( index - colors.length ) * GOLDEN_RATIO * FULL_HUE_ROTATION_DEGREES ) %
|
|
212
|
-
FULL_HUE_ROTATION_DEGREES;
|
|
213
|
-
const fallbackSaturation =
|
|
214
|
-
BASE_SATURATION + ( index % SATURATION_VARIATION_STEPS ) * SATURATION_INCREMENT;
|
|
215
|
-
const fallbackLightness =
|
|
216
|
-
BASE_LIGHTNESS + ( index % LIGHTNESS_VARIATION_STEPS ) * LIGHTNESS_INCREMENT;
|
|
217
|
-
// d3-color uses 0-1 scale for saturation and lightness
|
|
218
|
-
return d3Hsl(
|
|
219
|
-
Math.round( fallbackHue ),
|
|
220
|
-
fallbackSaturation / 100,
|
|
221
|
-
fallbackLightness / 100
|
|
222
|
-
).formatHex();
|
|
223
|
-
};
|