Skip to content

Commit

Permalink
Implement ColorPickerField
Browse files Browse the repository at this point in the history
  • Loading branch information
veej committed Sep 25, 2023
1 parent bc95424 commit 2813ff9
Show file tree
Hide file tree
Showing 7 changed files with 711 additions and 49 deletions.
4 changes: 4 additions & 0 deletions packages/bento-design-system/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export * from "./util/NonEmptyArray";
export * from "./util/Omit";
export * from "./util/align";
export * from "./util/atoms";
export * from "./util/bentoTokens";
export * from "./util/breakpoints";
export * from "./util/conditions";
export * from "./util/link";
Expand All @@ -112,11 +113,14 @@ export { titleRecipe } from "./Typography/Title/Title.css";
export { headlineRecipe } from "./Typography/Headline/Headline.css";
export { displayRecipe } from "./Typography/Display/Display.css";
export { inputRecipe } from "./Field/Field.css";
export { control } from "./SelectField/SelectField.css";
export * from "./vars.css";
export { defaultTheme } from "./defaultThemeClass.css";
export { defaultTokens } from "./util/defaultTokens";

export type { BentoConfig, PartialBentoConfig } from "./BentoConfig";
export { useBentoConfig } from "./BentoConfigContext";
export { getRadiusPropsFromConfig } from "./util/BorderRadiusConfig";
export { defaultConfigs };
export { BentoConfigProvider } from "./BentoConfigContext";

Expand Down
5 changes: 5 additions & 0 deletions packages/configuration-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
"dependencies": {
"@buildo/bento-design-system": "workspace:*",
"@phosphor-icons/react": "2.0.13",
"@react-aria/button": "3.8.1",
"@react-aria/i18n": "3.8.1",
"@react-aria/numberfield": "3.7.0",
"@react-aria/select": "3.12.1",
"@react-stately/numberfield": "3.6.0",
"@react-stately/select": "3.5.4",
"@vanilla-extract/css": "1.12.0",
"@vanilla-extract/recipes": "0.5.0",
"i18next": "22.5.1",
Expand All @@ -29,6 +32,8 @@
"ts-pattern": "3.3.5"
},
"devDependencies": {
"@react-aria/listbox": "^3.10.2",
"@react-stately/collections": "^3.10.1",
"@react-types/numberfield": "3.5.0",
"@tsconfig/vite-react": "2.0.0",
"@types/react": "18.2.21",
Expand Down
19 changes: 13 additions & 6 deletions packages/configuration-builder/src/ColorEditor/Palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ const interpolations: Record<LightnessInterpolation, number[]> = {
EaseInOut: [97, 93, 87, 79, 67, 54, 41, 28, 20, 14, 10],
};

export function getPalette(props: Props) {
const { hue, saturation, lightnessInterpolation } = props;
const lightnesses = interpolations[lightnessInterpolation];

return lightnesses.map((lightness) => {
const color = HSLToHex({ h: hue, s: saturation, l: lightness });
return color;
});
}

export function PaletteColorBox(props: { color: HexColor }) {
const [isHovered, setIsHovered] = useState(false);

Expand Down Expand Up @@ -51,9 +61,7 @@ export function PaletteColorBox(props: { color: HexColor }) {
}

export function Palette(props: Props) {
const { hue, saturation, lightnessInterpolation } = props;

const lightnesses = interpolations[lightnessInterpolation];
const colors = getPalette(props);

return (
<Box
Expand All @@ -65,9 +73,8 @@ export function Palette(props: Props) {
style={{ height: 160 }}
>
<Box display="flex" gap={4} flexGrow={1}>
{lightnesses.map((lightness) => {
const color = HSLToHex({ h: hue, s: saturation, l: lightness });
return <PaletteColorBox color={color} key={lightness} />;
{colors.map((color) => {
return <PaletteColorBox color={color} key={color} />;
})}
</Box>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import {
Box,
FieldProps,
control,
useBentoConfig,
getRadiusPropsFromConfig,
Body,
Field,
Popover,
} from "@buildo/bento-design-system";
import { useRef } from "react";
import { useSelectState } from "@react-stately/select";
import { Section, Item } from "@react-stately/collections";
import { useSelect, HiddenSelect } from "@react-aria/select";
import { PalettesDropdown } from "./PalettesDropdown";
import { ThemeConfig } from "../ConfiguratorStatusContext";
import { useButton } from "@react-aria/button";
import { getPalette } from "../ColorEditor/Palette";
import { ColorConfig } from "../ColorEditor/ColorEditor";

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

function getPaletteItemsSection(key: string, colorConfig: ColorConfig) {
const paletteItems = getPalette(colorConfig).map((color, index) => (
<Item key={`${key}-${index}`}>{color}</Item>
));
return (
<Section
key={key}
children={
colorConfig.useReferenceAsKeyColor
? [...paletteItems, <Item key={`${key}-reference`}>{colorConfig.referenceColor}</Item>]
: paletteItems
}
/>
);
}

function getColorItems(colors: ThemeConfig["colors"]) {
return [
<Section key="General">
<Item key="white">white</Item>
<Item key="black">black</Item>
</Section>,
getPaletteItemsSection("BrandPrimary", colors.brand[0]),
getPaletteItemsSection("Interactive", colors.interactive),
getPaletteItemsSection("Neutral", colors.neutral),
getPaletteItemsSection("Informative", colors.semantic.informative),
getPaletteItemsSection("Positive", colors.semantic.positive),
getPaletteItemsSection("Warning", colors.semantic.warning),
getPaletteItemsSection("Negative", colors.semantic.negative),
getPaletteItemsSection("Grey", colors.dataVisualization.grey),
getPaletteItemsSection("Red", colors.dataVisualization.red),
getPaletteItemsSection("Orange", colors.dataVisualization.orange),
getPaletteItemsSection("Yellow", colors.dataVisualization.yellow),
getPaletteItemsSection("Green", colors.dataVisualization.green),
getPaletteItemsSection("Jade", colors.dataVisualization.jade),
getPaletteItemsSection("Blue", colors.dataVisualization.blue),
getPaletteItemsSection("Indigo", colors.dataVisualization.indigo),
getPaletteItemsSection("Violet", colors.dataVisualization.violet),
getPaletteItemsSection("Pink", colors.dataVisualization.pink),
];
}

export function ColorPickerField(props: Props) {
const inputConfig = useBentoConfig().input;
const dropdownConfig = useBentoConfig().dropdown;

const ref = useRef(null);

const state = useSelectState({
...props,
isDisabled: props.disabled,
children: getColorItems(props.colors),
onSelectionChange: (key) => {
props.onChange(state.collection.getItem(key)!.textValue);
},
});

const { labelProps, errorMessageProps, descriptionProps, triggerProps, valueProps, menuProps } =
useSelect(
{
...props,
isDisabled: props.disabled,
},
state,
ref
);

const { buttonProps } = useButton(
{
...triggerProps,
elementType: "div",
},
ref
);

return (
<Field
{...props}
labelProps={labelProps}
assistiveTextProps={descriptionProps}
errorMessageProps={errorMessageProps}
>
<HiddenSelect
isDisabled={props.disabled}
state={state}
triggerRef={ref}
label={props.label}
name={props.name}
/>
<Box
as="button"
{...buttonProps}
color={undefined}
display="flex"
cursor="pointer"
outline="none"
className={control({ validation: "valid", menuIsOpen: state.isOpen, isReadOnly: false })}
disabled={props.disabled}
{...getRadiusPropsFromConfig(inputConfig.radius)}
paddingX={inputConfig.paddingX}
paddingY={inputConfig.paddingY}
ref={ref}
>
<Box {...valueProps} flexGrow={1} textAlign="left">
<Body size={inputConfig.fontSize} color={props.disabled ? "disabled" : "primary"}>
{props.value}
</Body>
</Box>
<Box paddingLeft={16} aria-hidden="true">
{dropdownConfig.openIndicatorIcon({
size: dropdownConfig.openIndicatorIconSize,
color: props.disabled ? "disabled" : "default",
})}
</Box>
</Box>
{state.isOpen && (
<Popover triggerRef={ref} onClose={state.close}>
<PalettesDropdown
colors={props.colors}
value={props.value}
onChange={props.onChange}
state={state}
menuProps={menuProps}
/>
</Popover>
)}
</Field>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { sprinkles } from "../sprinkles.css";
import { strictRecipe } from "@buildo/bento-design-system";

export const colorBoxRecipe = strictRecipe({
base: [
{
width: 28,
height: 28,
},
sprinkles({
borderWidth: 1,
borderStyle: "solid",
borderColor: {
default: "outlineContainer",
hover: "outlineInputHover",
focus: "outlineInputHover",
},
cursor: "pointer",
outline: "none",
}),
],
variants: {
isSelected: {
true: sprinkles({ borderRadius: "circled" }),
false: {},
},
},
});
Loading

0 comments on commit 2813ff9

Please sign in to comment.