Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurator #784

Open
wants to merge 44 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
f893491
Implement ColorPickerField
veej Sep 25, 2023
29e2485
Implement brand tokens section
veej Sep 25, 2023
7b91603
Extract brand tokens step
veej Sep 25, 2023
d33a781
Refactor ColorSelector and add Alpha selector
veej Oct 2, 2023
8385396
add state to example slider
veej Oct 2, 2023
fb10a60
Implement ColorPickerField
veej Sep 25, 2023
fa547a0
text&icons tokens section
veej Sep 26, 2023
4de6eb9
Interactive tokens section (simplified)
veej Oct 4, 2023
09fae73
set interactive outline
veej Oct 4, 2023
ed00f4f
secondary dark vs light
veej Oct 9, 2023
c8ffcd2
add outline/transparent tokens in guided mode, fix secondary buttons
veej Oct 9, 2023
78cf26c
Semantic section
veej Nov 4, 2023
ba13b34
Add semantic section to wizard
veej Nov 4, 2023
8c22652
Add ContentBlock around sections
veej Nov 4, 2023
2a7a572
Refactor text&icons tokens section
veej Nov 4, 2023
739c659
CategoricalPalette tokens section
veej Nov 8, 2023
68adec3
Input tokens section
veej Nov 8, 2023
d709c32
Refactor steps template
veej Nov 17, 2023
078f6e7
Other tokens section (no modal)
veej Nov 17, 2023
15b4a57
Restore scrolling position on top when navigating between steps
veej Nov 24, 2023
f6cf30d
OtherTokens section improvements
veej Nov 24, 2023
1b25fba
Make Playgrounds inert
veej Nov 24, 2023
16a48ea
add BrandSecondary and BrandTertiary palettes to the colors dropdown
veej Nov 24, 2023
f710021
fix inverse link tokens
veej Dec 7, 2023
0ee6f7f
export TS theme
veej Nov 17, 2023
35e765a
Avoid converting hex colors to rgb if alpha=1
veej Nov 25, 2023
bae3a42
Save TS config on file
veej Dec 1, 2023
56d0295
Format exported code using prettier
veej Dec 7, 2023
34236ee
elevations section
veej Dec 1, 2023
137ec13
export elevations (fix indentation)
veej Dec 1, 2023
8ef27cc
Fix default theme values
veej Dec 7, 2023
0c9ce78
Export boxShadows for outlineColors + fix export
veej Dec 7, 2023
4d2fb93
Add Typography section and refactor completed step
veej Dec 8, 2023
dcc8b35
Add typography config
veej Dec 8, 2023
d4d0d56
Merge pull request #812 from buildo/export-ts
veej Dec 22, 2023
944f22f
Merge pull request #819 from buildo/config_elevations
veej Dec 22, 2023
c381b0b
export typography
veej Dec 22, 2023
db1ce6d
refactor TS export
veej Dec 22, 2023
01fc73d
Merge pull request #821 from buildo/config_fonts
veej Dec 22, 2023
170a8ac
useReferenceAsKeyColor true by default
veej Jan 5, 2024
d229b74
Add tooltip to palette colors
veej Jan 5, 2024
c0ff007
Add space in palette tokens names
veej Jan 5, 2024
ecdb39d
Make getRelativeStep work also with reference colors
veej Jan 5, 2024
c27d4a4
add missing react keys
veej Jan 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions packages/bento-design-system/src/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,21 @@ type Props = {
size?: ModalSize;
kind?: ModalKind;
autoFocus?: boolean;
portalContainer?: HTMLElement;
};

type CustomModalProps = Pick<Props, "children" | "isDestructive" | "size" | "autoFocus"> & {
type CustomModalProps = Pick<
Props,
"children" | "isDestructive" | "size" | "autoFocus" | "portalContainer"
> & {
["aria-label"]: string;
};

export function CustomModal(props: CustomModalProps) {
const config = useBentoConfig().modal;
const ref = useRef<HTMLDivElement>(null);
const { overlayProps, underlayProps } = useOverlay({ ...props, isOpen: true }, ref);
const createPortal = useCreatePortal();
const createPortal = useCreatePortal(props.portalContainer);

usePreventScroll();

Expand Down
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 @@ -95,6 +95,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 @@ -113,11 +114,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
6 changes: 4 additions & 2 deletions packages/bento-design-system/src/util/useCreatePortal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import { Children } from "./Children";
import { useIsSSR } from "@react-aria/ssr";
import { BentoThemePortalProvider } from "../BentoThemeContext";

export function useCreatePortal(): (children: Children) => ReactPortal | null {
export function useCreatePortal(
portalContainer?: HTMLElement
): (children: Children) => ReactPortal | null {
const isSSR = useIsSSR();

return (children) => {
const content = <BentoThemePortalProvider>{children}</BentoThemePortalProvider>;
return isSSR ? null : createReactPortal(content, document.body);
return isSSR ? null : createReactPortal(content, portalContainer ?? document.body);
};
}
9 changes: 9 additions & 0 deletions packages/configuration-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
"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",
"file-saver": "^2.0.5",
"i18next": "22.5.1",
"react": "18.2.0",
"react-dom": "18.2.0",
Expand All @@ -29,8 +33,12 @@
"ts-pattern": "3.3.5"
},
"devDependencies": {
"@react-aria/listbox": "^3.10.2",
"@react-stately/collections": "^3.10.1",
"@react-types/numberfield": "3.5.0",
"@react-types/shared": "3.19.0",
"@tsconfig/vite-react": "2.0.0",
"@types/file-saver": "^2.0.7",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
"@typescript-eslint/eslint-plugin": "6.5.0",
Expand All @@ -42,6 +50,7 @@
"eslint": "8.48.0",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-react-refresh": "0.4.3",
"prettier": "2.8.8",
"typescript": "5.1.3",
"vite": "4.4.9",
"vite-plugin-checker": "0.6.2"
Expand Down
15 changes: 3 additions & 12 deletions packages/configuration-builder/src/ColorEditor/ColorEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,15 @@ import { HexToHSL, HexColor } from "../utils/colorUtils";
import { useTranslation } from "react-i18next";
import { LightnessInterpolationSelector } from "./LightnessInterpolationSelector";
import { CounterField } from "./CounterField";

export type LightnessInterpolation = "Linear" | "EaseIn" | "EaseOut" | "EaseInOut";

export type ColorConfig = {
referenceColor: HexColor;
useReferenceAsKeyColor: boolean;
lightnessInterpolation: LightnessInterpolation;
hue: number;
saturation: number;
};
import { PaletteConfig } from "../utils/paletteUtils";

type Props = {
name: LocalizedString;
value: ColorConfig;
value: PaletteConfig;
} & (
| {
isReadOnly?: false;
onChange: (value: ColorConfig) => void;
onChange: (value: PaletteConfig) => void;
onDelete?: () => void;
}
| {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SelectField } from "@buildo/bento-design-system";
import { LightnessInterpolation } from "./ColorEditor";
import { useTranslation } from "react-i18next";
import { LightnessInterpolation } from "../utils/paletteUtils";

type Props = {
value: LightnessInterpolation;
Expand Down
20 changes: 5 additions & 15 deletions packages/configuration-builder/src/ColorEditor/Palette.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
import { Box } from "@buildo/bento-design-system";
import { LightnessInterpolation } from "./ColorEditor";
import { useState } from "react";
import { IconEyedropper } from "../PhosphorIcons";
import { HSLToHex, HexColor } from "../utils/colorUtils";
import { HexColor } from "../utils/colorUtils";
import { LightnessInterpolation, getPalette } from "../utils/paletteUtils";

type Props = {
hue: number;
saturation: number;
lightnessInterpolation: LightnessInterpolation;
};

const interpolations: Record<LightnessInterpolation, number[]> = {
Linear: [97, 91, 82, 73, 64, 55, 46, 37, 28, 19, 10],
EaseIn: [97, 93, 88, 82, 75, 65, 54, 43, 32, 21, 10],
EaseOut: [97, 86, 75, 64, 53, 42, 32, 25, 19, 14, 10],
EaseInOut: [97, 93, 87, 79, 67, 54, 41, 28, 20, 14, 10],
};

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

Expand Down Expand Up @@ -51,9 +44,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 +56,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.value} key={color.value} />;
})}
</Box>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
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 { ColorKey, PaletteConfig, stepNames } from "../utils/paletteUtils";

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

function getPaletteItemsSection(key: string, paletteConfig: PaletteConfig) {
const paletteItems = [...Array(stepNames.length)].map((_, index) => {
const stepName = stepNames[index];
const colorKey = `${key}-${stepName}`;
const colorText = `${key} ${stepName}`;
return (
<Item key={colorKey} textValue={colorKey}>
{colorText}
</Item>
);
});
return (
<Section
key={key}
children={
paletteConfig.useReferenceAsKeyColor
? [
...paletteItems,
<Item key={`${key}-ref`} textValue={`${key}-ref`}>
{`${key}Reference`}
</Item>,
]
: paletteItems
}
/>
);
}

function getColorItems(colors: ThemeConfig["colors"]) {
return [
<Section key="General">
<Item key="white" textValue="white">
White
</Item>
<Item key="black" textValue="black">
Black
</Item>
</Section>,
...colors.brand.map((brandColor, i) =>
getPaletteItemsSection(
i === 0 ? "BrandPrimary" : i === 1 ? "BrandSecondary" : "BrandTertiary",
brandColor
)
),
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,
selectedKey: props.value,
isDisabled: props.disabled,
children: getColorItems(props.colors),
onSelectionChange: (key) => {
const item = state.collection.getItem(key);
if (item) {
props.onChange(item.textValue as ColorKey);
}
},
});

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"}>
{state.selectedItem ? state.selectedItem.rendered : ""}
</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} 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