Skip to content

Commit 95cc3b7

Browse files
authored
fix(Slider): Added support for Black & White colors, plus new customColorShades prop (#182)
* added support for black and white, removing hsl conversion and instead using rgba, plus new customColorShades prop * added support for black and white, removing hsl conversion and instead using rgba, plus new customColorShades prop * added support for black and white, removing hsl conversion and instead using rgba, plus new customColorShades prop -- fixed hsva match bug
1 parent 55f98f4 commit 95cc3b7

File tree

1 file changed

+65
-14
lines changed

1 file changed

+65
-14
lines changed

packages/color-slider/src/index.tsx

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,101 @@ import {
33
ColorResult,
44
color as handleColor,
55
hexToHsva,
6-
hsvaToHsla,
7-
hsvaToHslString,
6+
hsvaToRgbaString,
87
validHex,
8+
hsvaToRgba,
99
HsvaColor,
10-
hslStringToHsva,
10+
rgbaStringToHsva,
1111
} from '@uiw/color-convert';
1212

1313
export interface SliderProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'color'> {
1414
prefixCls?: string;
1515
color?: string | HsvaColor;
1616
lightness?: number[];
17+
customColorShades?: { color: string | HsvaColor; lightness: number[] }[];
1718
onChange?: (color: ColorResult, evn: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
1819
}
1920

21+
const hsvaCheck = (color?: string | HsvaColor): HsvaColor => {
22+
return (typeof color === 'string' && validHex(color) ? hexToHsva(color) : color || {}) as HsvaColor;
23+
};
24+
25+
// Check if values are within specified units of each other
26+
const withinRange = (val1: number, val2: number, tolerance: number = 2): boolean => Math.abs(val1 - val2) <= tolerance;
27+
28+
const hsvaEqual = (c1: HsvaColor, c2: HsvaColor, lightnessArray?: number[]): boolean => {
29+
// Check for match within 2 units of all properties
30+
const baseMatch = withinRange(c1.h, c2.h) && withinRange(c1.s, c2.s) && withinRange(c1.a, c2.a);
31+
const exactMatch = baseMatch && withinRange(c1.v, c2.v);
32+
33+
// If there's a match within range, return true
34+
if (exactMatch) return true;
35+
36+
// If no exact match and a lightness array exists,
37+
// check if value is within range of any of the lightness array values
38+
if (lightnessArray) {
39+
return baseMatch && lightnessArray.some((lightness) => withinRange(c2.v, lightness));
40+
}
41+
42+
return false;
43+
};
44+
2045
const Slider = React.forwardRef<HTMLDivElement, SliderProps>((props, ref) => {
21-
const { prefixCls = 'w-color-slider', className, style, onChange, color, lightness = [80, 65, 50, 35, 20], ...other } = props;
22-
const hsva = (typeof color === 'string' && validHex(color) ? hexToHsva(color) : color || {}) as HsvaColor;
23-
const handleClick = (hslStr: string, evn: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
24-
onChange && onChange(handleColor(hslStringToHsva(hslStr)), evn);
46+
const {
47+
prefixCls = 'w-color-slider',
48+
className,
49+
style,
50+
onChange,
51+
color,
52+
customColorShades = [
53+
{ color: '#000000', lightness: [50, 40, 30, 20, 10] },
54+
{ color: '#ffffff', lightness: [95, 90, 80, 70, 60] },
55+
],
56+
lightness = [80, 65, 50, 35, 20],
57+
...other
58+
} = props;
59+
const hsva = hsvaCheck(color);
60+
61+
// Find matching custom color and its lightness array
62+
const matchingCustomColor = customColorShades.find((shade) => {
63+
const customHsva = hsvaCheck(shade.color);
64+
const isMatch = hsvaEqual(customHsva, hsva, shade.lightness);
65+
return isMatch;
66+
});
67+
68+
// Determine which lightness array to use
69+
const activeLightness = matchingCustomColor ? matchingCustomColor.lightness : lightness;
70+
71+
const handleClick = (rgbaStr: string, evn: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
72+
const newHsva = rgbaStringToHsva(rgbaStr);
73+
onChange && onChange(handleColor(newHsva), evn);
2574
};
75+
2676
return (
2777
<div
2878
ref={ref}
2979
style={{ display: 'flex', ...style }}
3080
className={[prefixCls, className || ''].filter(Boolean).join(' ')}
3181
{...other}
3282
>
33-
{lightness.map((num, idx) => {
34-
const hsl = hsvaToHsla(hsva);
35-
const hslStr = `hsl(${hsl.h}, 50%, ${num}%)`;
36-
const checked = hslStr === hsvaToHslString(hsva);
83+
{activeLightness.map((num: number, idx: number) => {
84+
const newHsva = { ...hsva, v: num };
85+
const rgba = hsvaToRgba(newHsva);
86+
const rgbaStr = `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${rgba.a})`;
87+
const checked = rgbaStr === hsvaToRgbaString(hsva);
3788
return (
3889
<div
3990
key={idx}
4091
style={{
4192
paddingLeft: 1,
42-
width: `${100 / lightness.length}%`,
93+
width: `${100 / activeLightness.length}%`,
4394
boxSizing: 'border-box',
4495
}}
4596
>
4697
<div
47-
onClick={(evn) => handleClick(hslStr, evn)}
98+
onClick={(evn) => handleClick(rgbaStr, evn)}
4899
style={{
49-
backgroundColor: hslStr,
100+
backgroundColor: rgbaStr,
50101
height: 12,
51102
cursor: 'pointer',
52103
...(checked

0 commit comments

Comments
 (0)