Skip to content

Commit 27e9c46

Browse files
committed
ColorArea: add getChannels method to Color, returning Set of ColorChannels
1 parent b8ee45e commit 27e9c46

File tree

5 files changed

+65
-103
lines changed

5 files changed

+65
-103
lines changed

packages/@react-aria/color/src/useColorArea.ts

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {AriaColorAreaProps} from '@react-types/color';
13+
import {AriaColorAreaProps, ColorChannel} from '@react-types/color';
1414
import {ColorAreaState} from '@react-stately/color';
1515
import {focusWithoutScrolling, isAndroid, isIOS, mergeProps, useGlobalListeners, useLabels} from '@react-aria/utils';
1616
// @ts-ignore
@@ -299,27 +299,14 @@ export function useColorArea(props: AriaColorAreaProps, state: ColorAreaState, i
299299
let colorAriaLabellingProps = useLabels(props);
300300

301301
let getValueTitle = () => {
302-
switch (state.value.getColorSpace()) {
303-
case 'hsb':
304-
return [
305-
formatMessage('colorNameAndValue', {name: state.value.getChannelName('hue', locale), value: state.value.formatChannelValue('hue', locale)}),
306-
formatMessage('colorNameAndValue', {name: state.value.getChannelName('saturation', locale), value: state.value.formatChannelValue('saturation', locale)}),
307-
formatMessage('colorNameAndValue', {name: state.value.getChannelName('brightness', locale), value: state.value.formatChannelValue('brightness', locale)})
308-
].join(', ');
309-
case 'hsl':
310-
return [
311-
formatMessage('colorNameAndValue', {name: state.value.getChannelName('hue', locale), value: state.value.formatChannelValue('hue', locale)}),
312-
formatMessage('colorNameAndValue', {name: state.value.getChannelName('saturation', locale), value: state.value.formatChannelValue('saturation', locale)}),
313-
formatMessage('colorNameAndValue', {name: state.value.getChannelName('lightness', locale), value: state.value.formatChannelValue('lightness', locale)})
314-
].join(', ');
315-
case 'rgb':
316-
return [
317-
formatMessage('colorNameAndValue', {name: state.value.getChannelName('red', locale), value: state.value.formatChannelValue('red', locale)}),
318-
formatMessage('colorNameAndValue', {name: state.value.getChannelName('green', locale), value: state.value.formatChannelValue('green', locale)}),
319-
formatMessage('colorNameAndValue', {name: state.value.getChannelName('blue', locale), value: state.value.formatChannelValue('blue', locale)})
320-
].join(', ');
321-
}
322-
return null;
302+
const channels: Set<ColorChannel> = state.value.getColorChannels();
303+
const colorNamesAndValues = [];
304+
channels.forEach(channel =>
305+
colorNamesAndValues.push(
306+
formatMessage('colorNameAndValue', {name: state.value.getChannelName(channel, locale), value: state.value.formatChannelValue(channel, locale)})
307+
)
308+
);
309+
return colorNamesAndValues.length ? colorNamesAndValues.join(', ') : null;
323310
};
324311

325312
let ariaRoleDescription = isMobile ? null : formatMessage('twoDimensionalSlider');

packages/@react-spectrum/color/stories/ColorArea.stories.tsx

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,15 @@ const Template: Story<SpectrumColorAreaProps> = (args) => (
2929
<ColorAreaExample {...args} />
3030
);
3131

32-
let RGB: Set<ColorChannel> = new Set(['red', 'green', 'blue']);
33-
let HSL: Set<ColorChannel> = new Set(['hue', 'saturation', 'lightness']);
34-
let HSB: Set<ColorChannel> = new Set(['hue', 'saturation', 'brightness']);
3532
let difference = (a, b): Set<ColorChannel> => new Set([...a].filter(x => !b.has(x)));
3633

3734
function ColorAreaExample(props: SpectrumColorAreaProps) {
3835
let {xChannel, yChannel, isDisabled} = props;
3936
let defaultValue = typeof props.defaultValue === 'string' ? parseColor(props.defaultValue) : props.defaultValue;
4037
let [color, setColor] = useState(defaultValue || parseColor('#ff00ff'));
41-
let channels = new Set([xChannel, yChannel]);
42-
let colorSpace = color.getColorSpace();
43-
let colorSpaceSet = RGB;
44-
switch (colorSpace) {
45-
case 'hsl':
46-
colorSpaceSet = HSL;
47-
break;
48-
case 'hsb':
49-
colorSpaceSet = HSB;
50-
break;
51-
}
52-
let zChannel: ColorChannel = difference(colorSpaceSet, channels).keys().next().value;
38+
let xyChannels = new Set([xChannel, yChannel]);
39+
let colorSpace = color.getColorSpace();
40+
let zChannel: ColorChannel = difference(color.getColorChannels(), xyChannels).keys().next().value;
5341
let isHue = zChannel === 'hue';
5442
function onChange(e) {
5543
try {

packages/@react-stately/color/src/Color.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ abstract class Color implements IColor {
7171
}
7272

7373
abstract getColorSpace(): ColorFormat
74+
abstract getColorChannels(): Set<ColorChannel>
7475
}
7576

7677
const HEX_REGEX = /^#(?:([0-9a-f]{3})|([0-9a-f]{6}))$/i;
@@ -266,6 +267,11 @@ class RGBColor extends Color {
266267
getColorSpace(): ColorFormat {
267268
return 'rgb';
268269
}
270+
271+
private static colorChannels: Set<ColorChannel> = new Set(['red', 'green', 'blue']);
272+
getColorChannels(): Set<ColorChannel> {
273+
return RGBColor.colorChannels;
274+
}
269275
}
270276

271277
// X = <negative/positive number with/without decimal places>
@@ -399,6 +405,11 @@ class HSBColor extends Color {
399405
getColorSpace(): ColorFormat {
400406
return 'hsb';
401407
}
408+
409+
private static colorChannels: Set<ColorChannel> = new Set(['hue', 'saturation', 'brightness']);
410+
getColorChannels(): Set<ColorChannel> {
411+
return HSBColor.colorChannels;
412+
}
402413
}
403414

404415
// X = <negative/positive number with/without decimal places>
@@ -534,4 +545,9 @@ class HSLColor extends Color {
534545
getColorSpace(): ColorFormat {
535546
return 'hsl';
536547
}
548+
549+
private static colorChannels: Set<ColorChannel> = new Set(['hue', 'saturation', 'lightness']);
550+
getColorChannels(): Set<ColorChannel> {
551+
return HSLColor.colorChannels;
552+
}
537553
}

packages/@react-stately/color/src/useColorAreaState.ts

Lines changed: 32 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,6 @@ export interface ColorAreaState {
6464
}
6565

6666
const DEFAULT_COLOR = parseColor('#ffffff');
67-
const RGBSet: Set<ColorChannel> = new Set(['red', 'green', 'blue']);
68-
const HSLSet: Set<ColorChannel> = new Set(['hue', 'saturation', 'lightness']);
69-
const HSBSet: Set<ColorChannel> = new Set(['hue', 'saturation', 'brightness']);
7067
let difference = <T>(a: Set<T>, b: Set<T>): Set<T> => new Set([...a].filter(x => !b.has(x)));
7168

7269
/**
@@ -88,79 +85,21 @@ export function useColorAreaState(props: ColorAreaProps): ColorAreaState {
8885
let channels = useMemo(() => {
8986
// determine the color space from the color value
9087
let colorSpace = valueRef.current.getColorSpace();
91-
let colorSpaceSet = RGBSet;
9288

93-
if (colorSpace === 'hsb') {
94-
colorSpaceSet = HSBSet;
95-
if (!xChannel) {
96-
switch (yChannel) {
97-
case 'hue':
98-
// eslint-disable-next-line react-hooks/exhaustive-deps
99-
xChannel = 'brightness';
100-
break;
101-
case 'brightness':
102-
xChannel = 'saturation';
103-
break;
104-
default:
105-
xChannel = 'saturation';
106-
// eslint-disable-next-line react-hooks/exhaustive-deps
107-
yChannel = 'brightness';
108-
break;
109-
}
110-
} else if (!yChannel) {
111-
switch (xChannel) {
112-
case 'hue':
113-
yChannel = 'brightness';
114-
break;
115-
case 'brightness':
116-
yChannel = 'saturation';
117-
break;
118-
default:
119-
xChannel = 'saturation';
120-
yChannel = 'brightness';
121-
break;
122-
}
123-
}
124-
} else if (colorSpace === 'hsl') {
125-
colorSpaceSet = HSLSet;
126-
if (!xChannel) {
127-
switch (yChannel) {
128-
case 'hue':
129-
xChannel = 'lightness';
130-
break;
131-
case 'lightness':
132-
xChannel = 'saturation';
133-
default:
134-
xChannel = 'saturation';
135-
yChannel = 'lightness';
136-
break;
137-
}
138-
} else if (!yChannel) {
139-
switch (xChannel) {
140-
case 'hue':
141-
yChannel = 'lightness';
142-
break;
143-
case 'lightness':
144-
yChannel = 'saturation';
145-
default:
146-
xChannel = 'saturation';
147-
yChannel = 'lightness';
148-
break;
149-
}
150-
}
151-
} else if (colorSpace === 'rgb') {
152-
colorSpaceSet = RGBSet;
89+
if (colorSpace === 'rgb') {
15390
if (!xChannel) {
15491
switch (yChannel) {
15592
case 'red':
15693
case 'green':
94+
// eslint-disable-next-line react-hooks/exhaustive-deps
15795
xChannel = 'blue';
15896
break;
15997
case 'blue':
16098
xChannel = 'red';
16199
break;
162100
default:
163101
xChannel = 'blue';
102+
// eslint-disable-next-line react-hooks/exhaustive-deps
164103
yChannel = 'green';
165104
}
166105
} else if (!yChannel) {
@@ -176,10 +115,38 @@ export function useColorAreaState(props: ColorAreaProps): ColorAreaState {
176115
yChannel = 'green';
177116
}
178117
}
118+
} else if (!xChannel) {
119+
switch (yChannel) {
120+
case 'hue':
121+
xChannel = colorSpace === 'hsb' ? 'brightness' : 'lightness';
122+
break;
123+
case 'brightness':
124+
case 'lightness':
125+
xChannel = 'saturation';
126+
break;
127+
default:
128+
xChannel = 'saturation';
129+
yChannel = colorSpace === 'hsb' ? 'brightness' : 'lightness';
130+
break;
131+
}
132+
} else if (!yChannel) {
133+
switch (xChannel) {
134+
case 'hue':
135+
yChannel = colorSpace === 'hsb' ? 'brightness' : 'lightness';
136+
break;
137+
case 'brightness':
138+
case 'lightness':
139+
yChannel = 'saturation';
140+
break;
141+
default:
142+
xChannel = 'saturation';
143+
yChannel = colorSpace === 'hsb' ? 'brightness' : 'lightness';
144+
break;
145+
}
179146
}
180147

181148
let xyChannels: Set<ColorChannel> = new Set([xChannel, yChannel]);
182-
let zChannel = difference(colorSpaceSet, xyChannels).values().next().value as ColorChannel;
149+
let zChannel = difference(valueRef.current.getColorChannels(), xyChannels).values().next().value as ColorChannel;
183150

184151
return {xChannel, yChannel, zChannel};
185152
}, [xChannel, yChannel]);

packages/@react-types/color/src/index.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ export interface Color {
8181
/**
8282
* Returns the color space, 'rgb', 'hsb' or 'hsl', for the current color.
8383
*/
84-
getColorSpace(): ColorFormat
84+
getColorSpace(): ColorFormat,
85+
/**
86+
* Returns an array of the color channels within the current color space space.
87+
*/
88+
getColorChannels(): Set<ColorChannel>
8589
}
8690

8791
export interface ColorFieldProps extends Omit<ValueBase<string | Color>, 'onChange'>, InputBase, Validation, FocusableProps, TextInputBase, LabelableProps {

0 commit comments

Comments
 (0)