Skip to content

Commit 89de08a

Browse files
committed
Refactor ColorSelector and add Alpha selector
1 parent 1c8f20f commit 89de08a

17 files changed

+430
-228
lines changed

Diff for: packages/configuration-builder/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@react-aria/listbox": "^3.10.2",
3636
"@react-stately/collections": "^3.10.1",
3737
"@react-types/numberfield": "3.5.0",
38+
"@react-types/shared": "3.19.0",
3839
"@tsconfig/vite-react": "2.0.0",
3940
"@types/react": "18.2.21",
4041
"@types/react-dom": "18.2.7",

Diff for: packages/configuration-builder/src/ColorEditor/Palette.tsx

+3-19
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,15 @@ import { Box } from "@buildo/bento-design-system";
22
import { LightnessInterpolation } from "./ColorEditor";
33
import { useState } from "react";
44
import { IconEyedropper } from "../PhosphorIcons";
5-
import { HSLToHex, HexColor } from "../utils/colorUtils";
5+
import { HexColor } from "../utils/colorUtils";
6+
import { getPalette } from "../utils/paletteUtils";
67

78
type Props = {
89
hue: number;
910
saturation: number;
1011
lightnessInterpolation: LightnessInterpolation;
1112
};
1213

13-
const interpolations: Record<LightnessInterpolation, number[]> = {
14-
Linear: [97, 91, 82, 73, 64, 55, 46, 37, 28, 19, 10],
15-
EaseIn: [97, 93, 88, 82, 75, 65, 54, 43, 32, 21, 10],
16-
EaseOut: [97, 86, 75, 64, 53, 42, 32, 25, 19, 14, 10],
17-
EaseInOut: [97, 93, 87, 79, 67, 54, 41, 28, 20, 14, 10],
18-
};
19-
20-
export function getPalette(props: Props) {
21-
const { hue, saturation, lightnessInterpolation } = props;
22-
const lightnesses = interpolations[lightnessInterpolation];
23-
24-
return lightnesses.map((lightness) => {
25-
const color = HSLToHex({ h: hue, s: saturation, l: lightness });
26-
return color;
27-
});
28-
}
29-
3014
export function PaletteColorBox(props: { color: HexColor }) {
3115
const [isHovered, setIsHovered] = useState(false);
3216

@@ -74,7 +58,7 @@ export function Palette(props: Props) {
7458
>
7559
<Box display="flex" gap={4} flexGrow={1}>
7660
{colors.map((color) => {
77-
return <PaletteColorBox color={color} key={color} />;
61+
return <PaletteColorBox color={color.value} key={color.value} />;
7862
})}
7963
</Box>
8064
</Box>

Diff for: packages/configuration-builder/src/ColorPickerField/ColorPickerField.tsx

+30-16
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,33 @@ import { useSelect, HiddenSelect } from "@react-aria/select";
1515
import { PalettesDropdown } from "./PalettesDropdown";
1616
import { ThemeConfig } from "../ConfiguratorStatusContext";
1717
import { useButton } from "@react-aria/button";
18-
import { getPalette } from "../ColorEditor/Palette";
1918
import { ColorConfig } from "../ColorEditor/ColorEditor";
19+
import { stepNames } from "../utils/paletteUtils";
2020

2121
type Props = FieldProps<string> & { colors: ThemeConfig["colors"] };
2222

2323
function getPaletteItemsSection(key: string, colorConfig: ColorConfig) {
24-
const paletteItems = getPalette(colorConfig).map((color, index) => (
25-
<Item key={`${key}-${index}`}>{color}</Item>
26-
));
24+
const paletteItems = [...Array(stepNames.length)].map((_, index) => {
25+
const stepName = stepNames[index];
26+
const colorKey = `${key}-${stepName}`;
27+
const colorText = `${key}${stepName}`;
28+
return (
29+
<Item key={colorKey} textValue={colorKey}>
30+
{colorText}
31+
</Item>
32+
);
33+
});
2734
return (
2835
<Section
2936
key={key}
3037
children={
3138
colorConfig.useReferenceAsKeyColor
32-
? [...paletteItems, <Item key={`${key}-reference`}>{colorConfig.referenceColor}</Item>]
39+
? [
40+
...paletteItems,
41+
<Item key={`${key}-ref`} textValue={`${key}-ref`}>
42+
{`${key}Reference`}
43+
</Item>,
44+
]
3345
: paletteItems
3446
}
3547
/>
@@ -39,8 +51,12 @@ function getPaletteItemsSection(key: string, colorConfig: ColorConfig) {
3951
function getColorItems(colors: ThemeConfig["colors"]) {
4052
return [
4153
<Section key="General">
42-
<Item key="white">white</Item>
43-
<Item key="black">black</Item>
54+
<Item key="white" textValue="white">
55+
white
56+
</Item>
57+
<Item key="black" textValue="black">
58+
black
59+
</Item>
4460
</Section>,
4561
getPaletteItemsSection("BrandPrimary", colors.brand[0]),
4662
getPaletteItemsSection("Interactive", colors.interactive),
@@ -70,10 +86,14 @@ export function ColorPickerField(props: Props) {
7086

7187
const state = useSelectState({
7288
...props,
89+
selectedKey: props.value,
7390
isDisabled: props.disabled,
7491
children: getColorItems(props.colors),
7592
onSelectionChange: (key) => {
76-
props.onChange(state.collection.getItem(key)!.textValue);
93+
const item = state.collection.getItem(key);
94+
if (item) {
95+
props.onChange(item.textValue);
96+
}
7797
},
7898
});
7999

@@ -125,7 +145,7 @@ export function ColorPickerField(props: Props) {
125145
>
126146
<Box {...valueProps} flexGrow={1} textAlign="left">
127147
<Body size={inputConfig.fontSize} color={props.disabled ? "disabled" : "primary"}>
128-
{props.value}
148+
{state.selectedItem ? state.selectedItem.rendered : ""}
129149
</Body>
130150
</Box>
131151
<Box paddingLeft={16} aria-hidden="true">
@@ -137,13 +157,7 @@ export function ColorPickerField(props: Props) {
137157
</Box>
138158
{state.isOpen && (
139159
<Popover triggerRef={ref} onClose={state.close}>
140-
<PalettesDropdown
141-
colors={props.colors}
142-
value={props.value}
143-
onChange={props.onChange}
144-
state={state}
145-
menuProps={menuProps}
146-
/>
160+
<PalettesDropdown colors={props.colors} state={state} menuProps={menuProps} />
147161
</Popover>
148162
)}
149163
</Field>

Diff for: packages/configuration-builder/src/ColorPickerField/PalettesDropdown.tsx

+54-74
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
import { Box, Card, Divider, Inline, Label, Stack } from "@buildo/bento-design-system";
2-
import { ThemeConfig } from "../ConfiguratorStatusContext";
2+
import { ThemeConfig, useConfiguratorStatusContext } from "../ConfiguratorStatusContext";
33
import { colorBoxRecipe } from "./PalettesDropdown.css";
44
import { SelectState } from "@react-stately/select";
55
import { AriaListBoxOptions, useListBox, useOption } from "@react-aria/listbox";
6-
import { Key, useRef } from "react";
6+
import { useRef } from "react";
77
import { useTranslation } from "react-i18next";
8+
import { Node } from "@react-types/shared";
9+
import { PaletteName, getPalette, getPaletteKeyColor } from "../utils/paletteUtils";
810

911
type Props = {
1012
colors: ThemeConfig["colors"];
11-
value: string | null;
12-
onChange: (value: string) => void;
1313
state: SelectState<object>;
1414
menuProps: AriaListBoxOptions<object>;
1515
};
1616

1717
function ColorBox({
18-
color,
19-
colorKey,
18+
item,
2019
state,
20+
color,
2121
}: {
22-
color: string;
23-
colorKey: Key;
22+
item: Node<object>;
2423
state: SelectState<object>;
24+
color: string;
2525
}) {
2626
const ref = useRef(null);
27-
const { optionProps, isSelected } = useOption({ key: colorKey }, state, ref);
27+
const { optionProps, isSelected } = useOption({ key: item.key }, state, ref);
28+
2829
return (
2930
<Box
3031
ref={ref}
@@ -36,27 +37,27 @@ function ColorBox({
3637
);
3738
}
3839

39-
function Palette(props: {
40-
name: string;
41-
onChange: (value: string) => void;
42-
value: string | null;
43-
state: SelectState<object>;
44-
}) {
45-
const colors = [...props.state.collection.getChildren!(props.name)];
40+
function Palette(props: { name: PaletteName; state: SelectState<object> }) {
41+
const colors = useConfiguratorStatusContext().theme.colors;
42+
const colorItems = [...props.state.collection.getChildren!(props.name)];
43+
const keyColor = getPaletteKeyColor(props.name, colors);
44+
if (keyColor) {
45+
const palette = getPalette(keyColor);
4646

47-
return (
48-
<Inline space={4} alignY="stretch">
49-
{colors.map((color) => {
50-
const colorBox = (
51-
<ColorBox colorKey={color.key} state={props.state} color={color.textValue} />
52-
);
53-
if (color.key.toString().endsWith("reference")) {
54-
return [<Divider orientation="vertical" />, colorBox];
55-
}
56-
return colorBox;
57-
})}
58-
</Inline>
59-
);
47+
return (
48+
<Inline space={4} alignY="stretch">
49+
{colorItems.map((colorItem, index) => {
50+
if (colorItem.key.toString().endsWith("ref")) {
51+
return [
52+
<Divider orientation="vertical" />,
53+
<ColorBox item={colorItem} state={props.state} color={keyColor.referenceColor} />,
54+
];
55+
}
56+
return <ColorBox item={colorItem} state={props.state} color={palette[index].value} />;
57+
})}
58+
</Inline>
59+
);
60+
}
6061
}
6162

6263
export function PalettesDropdown(props: Props) {
@@ -72,69 +73,48 @@ export function PalettesDropdown(props: Props) {
7273
<Stack space={16}>
7374
<Inline space={4}>
7475
{[...props.state.collection.getChildren!("General")].map((color) => (
75-
<ColorBox color={color.key as string} colorKey={color.key} state={props.state} />
76+
<ColorBox item={color} color={color.textValue} state={props.state} />
7677
))}
7778
</Inline>
7879
<Stack space={4}>
7980
<Label size="small">{t("ColorsSection.Step.brand")}</Label>
80-
<Palette
81-
name="BrandPrimary"
82-
onChange={props.onChange}
83-
value={props.value}
84-
state={props.state}
85-
/>
81+
<Palette name="BrandPrimary" state={props.state} />
8682
</Stack>
8783
<Stack space={4}>
8884
<Label size="small">{t("ColorsSection.Step.interactive")}</Label>
8985
<Inline space={4}>
90-
<Palette
91-
name="Interactive"
92-
onChange={props.onChange}
93-
value={props.value}
94-
state={props.state}
95-
/>
86+
<Palette name="Interactive" state={props.state} />
9687
</Inline>
9788
</Stack>
9889
<Stack space={4}>
9990
<Label size="small">{t("ColorsSection.Step.neutral")}</Label>
100-
<Palette
101-
name="Neutral"
102-
onChange={props.onChange}
103-
value={props.value}
104-
state={props.state}
105-
/>
91+
<Palette name="Neutral" state={props.state} />
10692
</Stack>
10793
<Stack space={4}>
10894
<Label size="small">{t("ColorsSection.Step.semantic")}</Label>
109-
{["Informative", "Positive", "Warning", "Negative"].map((semanticColor) => (
110-
<Palette
111-
name={semanticColor}
112-
onChange={props.onChange}
113-
value={props.value}
114-
state={props.state}
115-
/>
116-
))}
95+
{(["Informative", "Positive", "Warning", "Negative"] as PaletteName[]).map(
96+
(semanticColor) => (
97+
<Palette name={semanticColor} state={props.state} />
98+
)
99+
)}
117100
</Stack>
118101
<Stack space={4}>
119102
<Label size="small">{t("ColorsSection.Step.dataVisualization")}</Label>
120-
{[
121-
"Grey",
122-
"Red",
123-
"Orange",
124-
"Yellow",
125-
"Green",
126-
"Jade",
127-
"Blue",
128-
"Indigo",
129-
"Violet",
130-
"Pink",
131-
].map((dataVizColor) => (
132-
<Palette
133-
name={dataVizColor}
134-
onChange={props.onChange}
135-
value={props.value}
136-
state={props.state}
137-
/>
103+
{(
104+
[
105+
"Grey",
106+
"Red",
107+
"Orange",
108+
"Yellow",
109+
"Green",
110+
"Jade",
111+
"Blue",
112+
"Indigo",
113+
"Violet",
114+
"Pink",
115+
] as PaletteName[]
116+
).map((dataVizColor) => (
117+
<Palette name={dataVizColor} state={props.state} />
138118
))}
139119
</Stack>
140120
</Stack>

0 commit comments

Comments
 (0)