Skip to content

Commit 2af189f

Browse files
committed
Implement ColorPickerField
1 parent 3deb943 commit 2af189f

File tree

7 files changed

+710
-48
lines changed

7 files changed

+710
-48
lines changed

Diff for: packages/bento-design-system/src/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export * from "./util/NonEmptyArray";
9494
export * from "./util/Omit";
9595
export * from "./util/align";
9696
export * from "./util/atoms";
97+
export * from "./util/bentoTokens";
9798
export * from "./util/breakpoints";
9899
export * from "./util/conditions";
99100
export * from "./util/link";
@@ -112,11 +113,14 @@ export { titleRecipe } from "./Typography/Title/Title.css";
112113
export { headlineRecipe } from "./Typography/Headline/Headline.css";
113114
export { displayRecipe } from "./Typography/Display/Display.css";
114115
export { inputRecipe } from "./Field/Field.css";
116+
export { control } from "./SelectField/SelectField.css";
115117
export * from "./vars.css";
116118
export { defaultTheme } from "./defaultThemeClass.css";
117119
export { defaultTokens } from "./util/defaultTokens";
118120

119121
export type { BentoConfig, PartialBentoConfig } from "./BentoConfig";
122+
export { useBentoConfig } from "./BentoConfigContext";
123+
export { getRadiusPropsFromConfig } from "./util/BorderRadiusConfig";
120124
export { defaultConfigs };
121125
export { BentoConfigProvider } from "./BentoConfigContext";
122126

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

+5
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
"dependencies": {
1717
"@buildo/bento-design-system": "workspace:*",
1818
"@phosphor-icons/react": "2.0.13",
19+
"@react-aria/button": "3.8.1",
1920
"@react-aria/i18n": "3.8.1",
2021
"@react-aria/numberfield": "3.7.0",
22+
"@react-aria/select": "3.12.1",
2123
"@react-stately/numberfield": "3.6.0",
24+
"@react-stately/select": "3.5.4",
2225
"@vanilla-extract/css": "1.12.0",
2326
"@vanilla-extract/recipes": "0.5.0",
2427
"i18next": "22.5.1",
@@ -29,6 +32,8 @@
2932
"ts-pattern": "3.3.5"
3033
},
3134
"devDependencies": {
35+
"@react-aria/listbox": "^3.10.2",
36+
"@react-stately/collections": "^3.10.1",
3237
"@react-types/numberfield": "3.5.0",
3338
"@tsconfig/vite-react": "2.0.0",
3439
"@types/react": "18.2.21",

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

+13-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ const interpolations: Record<LightnessInterpolation, number[]> = {
1717
EaseInOut: [97, 93, 87, 79, 67, 54, 41, 28, 20, 14, 10],
1818
};
1919

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+
2030
export function PaletteColorBox(props: { color: HexColor }) {
2131
const [isHovered, setIsHovered] = useState(false);
2232

@@ -51,9 +61,7 @@ export function PaletteColorBox(props: { color: HexColor }) {
5161
}
5262

5363
export function Palette(props: Props) {
54-
const { hue, saturation, lightnessInterpolation } = props;
55-
56-
const lightnesses = interpolations[lightnessInterpolation];
64+
const colors = getPalette(props);
5765

5866
return (
5967
<Box
@@ -65,9 +73,8 @@ export function Palette(props: Props) {
6573
style={{ height: 160 }}
6674
>
6775
<Box display="flex" gap={4} flexGrow={1}>
68-
{lightnesses.map((lightness) => {
69-
const color = HSLToHex({ h: hue, s: saturation, l: lightness });
70-
return <PaletteColorBox color={color} key={lightness} />;
76+
{colors.map((color) => {
77+
return <PaletteColorBox color={color} key={color} />;
7178
})}
7279
</Box>
7380
</Box>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import {
2+
Box,
3+
FieldProps,
4+
control,
5+
useBentoConfig,
6+
getRadiusPropsFromConfig,
7+
Body,
8+
Field,
9+
Popover,
10+
} from "@buildo/bento-design-system";
11+
import { useRef } from "react";
12+
import { useSelectState } from "@react-stately/select";
13+
import { Section, Item } from "@react-stately/collections";
14+
import { useSelect, HiddenSelect } from "@react-aria/select";
15+
import { PalettesDropdown } from "./PalettesDropdown";
16+
import { ThemeConfig } from "../ConfiguratorStatusContext";
17+
import { useButton } from "@react-aria/button";
18+
import { getPalette } from "../ColorEditor/Palette";
19+
import { ColorConfig } from "../ColorEditor/ColorEditor";
20+
21+
type Props = FieldProps<string> & { colors: ThemeConfig["colors"] };
22+
23+
function getPaletteItemsSection(key: string, colorConfig: ColorConfig) {
24+
const paletteItems = getPalette(colorConfig).map((color, index) => (
25+
<Item key={`${key}-${index}`}>{color}</Item>
26+
));
27+
return (
28+
<Section
29+
key={key}
30+
children={
31+
colorConfig.useReferenceAsKeyColor
32+
? [...paletteItems, <Item key={`${key}-reference`}>{colorConfig.referenceColor}</Item>]
33+
: paletteItems
34+
}
35+
/>
36+
);
37+
}
38+
39+
function getColorItems(colors: ThemeConfig["colors"]) {
40+
return [
41+
<Section key="General">
42+
<Item key="white">white</Item>
43+
<Item key="black">black</Item>
44+
</Section>,
45+
getPaletteItemsSection("BrandPrimary", colors.brand[0]),
46+
getPaletteItemsSection("Interactive", colors.interactive),
47+
getPaletteItemsSection("Neutral", colors.neutral),
48+
getPaletteItemsSection("Informative", colors.semantic.informative),
49+
getPaletteItemsSection("Positive", colors.semantic.positive),
50+
getPaletteItemsSection("Warning", colors.semantic.warning),
51+
getPaletteItemsSection("Negative", colors.semantic.negative),
52+
getPaletteItemsSection("Grey", colors.dataVisualization.grey),
53+
getPaletteItemsSection("Red", colors.dataVisualization.red),
54+
getPaletteItemsSection("Orange", colors.dataVisualization.orange),
55+
getPaletteItemsSection("Yellow", colors.dataVisualization.yellow),
56+
getPaletteItemsSection("Green", colors.dataVisualization.green),
57+
getPaletteItemsSection("Jade", colors.dataVisualization.jade),
58+
getPaletteItemsSection("Blue", colors.dataVisualization.blue),
59+
getPaletteItemsSection("Indigo", colors.dataVisualization.indigo),
60+
getPaletteItemsSection("Violet", colors.dataVisualization.violet),
61+
getPaletteItemsSection("Pink", colors.dataVisualization.pink),
62+
];
63+
}
64+
65+
export function ColorPickerField(props: Props) {
66+
const inputConfig = useBentoConfig().input;
67+
const dropdownConfig = useBentoConfig().dropdown;
68+
69+
const ref = useRef(null);
70+
71+
const state = useSelectState({
72+
...props,
73+
isDisabled: props.disabled,
74+
children: getColorItems(props.colors),
75+
onSelectionChange: (key) => {
76+
props.onChange(state.collection.getItem(key)!.textValue);
77+
},
78+
});
79+
80+
const { labelProps, errorMessageProps, descriptionProps, triggerProps, valueProps, menuProps } =
81+
useSelect(
82+
{
83+
...props,
84+
isDisabled: props.disabled,
85+
},
86+
state,
87+
ref
88+
);
89+
90+
const { buttonProps } = useButton(
91+
{
92+
...triggerProps,
93+
elementType: "div",
94+
},
95+
ref
96+
);
97+
98+
return (
99+
<Field
100+
{...props}
101+
labelProps={labelProps}
102+
assistiveTextProps={descriptionProps}
103+
errorMessageProps={errorMessageProps}
104+
>
105+
<HiddenSelect
106+
isDisabled={props.disabled}
107+
state={state}
108+
triggerRef={ref}
109+
label={props.label}
110+
name={props.name}
111+
/>
112+
<Box
113+
as="button"
114+
{...buttonProps}
115+
color={undefined}
116+
display="flex"
117+
cursor="pointer"
118+
outline="none"
119+
className={control({ validation: "valid", menuIsOpen: state.isOpen, isReadOnly: false })}
120+
disabled={props.disabled}
121+
{...getRadiusPropsFromConfig(inputConfig.radius)}
122+
paddingX={inputConfig.paddingX}
123+
paddingY={inputConfig.paddingY}
124+
ref={ref}
125+
>
126+
<Box {...valueProps} flexGrow={1} textAlign="left">
127+
<Body size={inputConfig.fontSize} color={props.disabled ? "disabled" : "primary"}>
128+
{props.value}
129+
</Body>
130+
</Box>
131+
<Box paddingLeft={16} aria-hidden="true">
132+
{dropdownConfig.openIndicatorIcon({
133+
size: dropdownConfig.openIndicatorIconSize,
134+
color: props.disabled ? "disabled" : "default",
135+
})}
136+
</Box>
137+
</Box>
138+
{state.isOpen && (
139+
<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+
/>
147+
</Popover>
148+
)}
149+
</Field>
150+
);
151+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { sprinkles } from "../sprinkles.css";
2+
import { strictRecipe } from "@buildo/bento-design-system";
3+
4+
export const colorBoxRecipe = strictRecipe({
5+
base: [
6+
{
7+
width: 28,
8+
height: 28,
9+
},
10+
sprinkles({
11+
borderWidth: 1,
12+
borderStyle: "solid",
13+
borderColor: {
14+
default: "outlineContainer",
15+
hover: "outlineInputHover",
16+
focus: "outlineInputHover",
17+
},
18+
cursor: "pointer",
19+
outline: "none",
20+
}),
21+
],
22+
variants: {
23+
isSelected: {
24+
true: sprinkles({ borderRadius: "circled" }),
25+
false: {},
26+
},
27+
},
28+
});

0 commit comments

Comments
 (0)