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] Add elevations configuration #819

Merged
merged 4 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function ConfiguratorSection<T extends string>(props: Props<T>) {
paddingTop={24}
flexGrow={1}
overflowY="auto"
key={props.endStep ? "endStep" : props.currentStep}
key={props.endStep ? "endStep" : props.singleStep ? "singleStep" : props.currentStep}
>
<ContentBlock maxWidth={1440} alignSelf="center">
<Stack space={40}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,41 +8,55 @@ import {
} from "@buildo/bento-design-system";
import { useTranslation } from "react-i18next";

export type Props<T extends string> = {
steps: readonly T[];
stepLabel: (step: T) => LocalizedString;
currentStep: T;
onStepChange: (step: T) => void;
onComplete: () => void;
onCancel: () => void;
children: Children;
};
export type Props<T extends string> = (
| {
singleStep?: never;
steps: readonly T[];
stepLabel: (step: T) => LocalizedString;
currentStep: T;
onStepChange: (step: T) => void;
}
| {
singleStep: true;
}
) & { onComplete: () => void; onCancel: () => void; children: Children };

export function ConfiguratorSectionSteps<T extends string>(props: Props<T>) {
const { t } = useTranslation();
const currentStepIndex = props.steps.indexOf(props.currentStep);
const currentStepIndex = props.singleStep ? 0 : props.steps.indexOf(props.currentStep);

const onNext = () => {
props.onStepChange(props.steps[currentStepIndex + 1]);
if (!props.singleStep) {
props.onStepChange(props.steps[currentStepIndex + 1]);
}
};
const onBack = () => {
props.onStepChange(props.steps[currentStepIndex - 1]);
if (!props.singleStep) {
props.onStepChange(props.steps[currentStepIndex - 1]);
}
};

return (
<Stack space={24}>
<Stepper
steps={props.steps.map((step) => ({ label: props.stepLabel(step) }))}
currentStep={currentStepIndex}
/>
{!props.singleStep && (
<Stepper
steps={props.steps.map((step) => ({ label: props.stepLabel(step) }))}
currentStep={currentStepIndex}
/>
)}

<Stack space={40}>
<Headline size="small">{props.stepLabel(props.currentStep)}</Headline>
{!props.singleStep && (
<Headline size="small">{props.stepLabel(props.currentStep)}</Headline>
)}
{props.children}
<Actions
primaryAction={{
label: t("Next"),
onPress: currentStepIndex === props.steps.length - 1 ? props.onComplete : onNext,
label: t(props.singleStep ? "Confirm" : "Next"),
onPress:
props.singleStep || currentStepIndex === props.steps.length - 1
? props.onComplete
: onNext,
}}
secondaryAction={
currentStepIndex === 0
Expand Down
45 changes: 37 additions & 8 deletions packages/configuration-builder/src/ConfiguratorStatusContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ type TokensConfig = MapLeafNodes<
ColorToken
>;

export type ElevationConfig = {
x: number;
y: number;
blur: number;
color: ColorToken;
};

export type ThemeConfig = {
colors: {
brand: BrandColors;
Expand All @@ -54,10 +61,11 @@ export type ThemeConfig = {
pink: ColorConfig;
};
};
elevations: Record<"small" | "medium" | "large", ElevationConfig>;
tokens: TokensConfig;
};

export type ThemeSection = "colors" | "tokens";
export type ThemeSection = "colors" | "tokens" | "elevations";

type ConfiguratorStatus = {
theme: ThemeConfig;
Expand All @@ -77,7 +85,7 @@ export const defaultTokens: TokensConfig = {
backgroundColor: {
backgroundPrimary: colorToken("white"),
backgroundSecondary: colorToken("Neutral-1"),
backgroundOverlay: colorToken("Neutral-20", 20),
backgroundOverlay: colorToken("Neutral-20", 40),
backgroundPrimaryInverse: colorToken("Neutral-90"),
backgroundSecondaryInverse: colorToken("Neutral-80"),
backgroundInteractive: colorToken("Interactive-40"),
Expand All @@ -92,7 +100,7 @@ export const defaultTokens: TokensConfig = {
foregroundColor: {
foregroundPrimary: colorToken("Neutral-90"),
foregroundSecondary: colorToken("Neutral-50"),
foregroundPrimaryInverse: colorToken("Neutral-1"),
foregroundPrimaryInverse: colorToken("white"),
foregroundSecondaryInverse: colorToken("Neutral-30"),
foregroundInteractive: colorToken("Interactive-40"),
foregroundInformative: colorToken("Informative-30"),
Expand Down Expand Up @@ -121,17 +129,17 @@ export const defaultTokens: TokensConfig = {
primaryTransparentHoverBackground: colorToken("Interactive-10", 40),
primaryTransparentFocusBackground: colorToken("Interactive-10", 40),
dangerSolidEnabledBackground: colorToken("Negative-40"),
dangerSolidHoverBackground: colorToken("Negative-40"),
dangerSolidFocusBackground: colorToken("Negative-40"),
dangerSolidHoverBackground: colorToken("Negative-60"),
dangerSolidFocusBackground: colorToken("Negative-60"),
dangerTransparentEnabledBackground: colorToken("white", 0),
dangerTransparentHoverBackground: colorToken("Negative-40", 10),
dangerTransparentFocusBackground: colorToken("Negative-40", 10),
secondarySolidEnabledBackground: colorToken("Neutral-5"),
secondarySolidHoverBackground: colorToken("Neutral-20"),
secondarySolidFocusBackground: colorToken("Neutral-20"),
secondaryTransparentEnabledBackground: colorToken("white", 0),
secondaryTransparentHoverBackground: colorToken("Neutral-10", 40),
secondaryTransparentFocusBackground: colorToken("Neutral-10", 40),
secondaryTransparentHoverBackground: colorToken("Neutral-20", 40),
secondaryTransparentFocusBackground: colorToken("Neutral-20", 40),
disabledSolidBackground: colorToken("Neutral-20", 20),
disabledTransparentBackground: colorToken("white", 0),
},
Expand Down Expand Up @@ -168,7 +176,7 @@ export const defaultTokens: TokensConfig = {
outlineColor: {
outlineInteractive: colorToken("Interactive-40"),
outlineDecorative: colorToken("Neutral-20"),
outlineContainer: colorToken("Neutral-40", 20),
outlineContainer: colorToken("Neutral-5"),
outlineInputEnabled: colorToken("Neutral-40"),
outlineInputHover: colorToken("Neutral-60"),
outlineInputFocus: colorToken("Interactive-40"),
Expand Down Expand Up @@ -258,11 +266,32 @@ export function ConfiguratorStatusProvider(props: { children: Children }) {
},
},
tokens: defaultTokens,
elevations: {
small: {
x: 0,
y: 4,
blur: 8,
color: { colorKey: "black", alpha: 16 },
},
medium: {
x: 0,
y: 8,
blur: 16,
color: { colorKey: "black", alpha: 16 },
},
large: {
x: 0,
y: 16,
blur: 32,
color: { colorKey: "black", alpha: 16 },
},
},
});

const [sections, setSections] = useState<ConfiguratorStatus["sections"]>({
colors: false,
tokens: false,
elevations: false,
});

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Body, Card, FormRow, Inset, NumberField, Stack, Title } from "@buildo/bento-design-system";
import { ElevationConfig } from "../ConfiguratorStatusContext";
import { Playground } from "../TokensSection/Playground";
import { Form } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { ColorSelector } from "../TokensSection/ColorSelector";

type Props = {
elevation: "small" | "medium" | "large";
config: ElevationConfig;
onChange: (elevation: ElevationConfig) => void;
};

export function ElevationCard(props: Props) {
const { t } = useTranslation();
return (
<Card padding={0} borderRadius={40}>
<Stack space={0}>
<Playground>
<Inset spaceY={40}>
<Card elevation={props.elevation}>
<></>
</Card>
</Inset>
</Playground>
<Inset space={40}>
<Form>
<Stack space={24}>
<Stack space={8}>
<Title size="large">{t(`Elevation.${props.elevation}`)}</Title>
<Body size="medium">{t(`Elevation.${props.elevation}Description`)}</Body>
</Stack>
<FormRow>
<NumberField
label={t("Elevation.x")}
value={props.config.x}
onChange={(x) => props.onChange({ ...props.config, x })}
/>
<NumberField
label={t("Elevation.y")}
value={props.config.y}
onChange={(y) => props.onChange({ ...props.config, y })}
/>
<NumberField
label={t("Elevation.blur")}
value={props.config.blur}
onChange={(blur) => props.onChange({ ...props.config, blur })}
/>
</FormRow>
<FormRow>
<ColorSelector
label={t("Elevation.color")}
value={props.config.color}
onChange={(color) => props.onChange({ ...props.config, color })}
/>
</FormRow>
</Stack>
</Form>
</Inset>
</Stack>
</Card>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ConfiguratorSection } from "../ConfiguratorSection/ConfiguratorSection";
import { useConfiguratorStatusContext } from "../ConfiguratorStatusContext";
import { useTranslation } from "react-i18next";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { SectionCompleted } from "./SectionCompleted";
import { Columns } from "@buildo/bento-design-system";
import { ElevationCard } from "./ElevationCard";

export function ElevationsSection() {
const { theme, setTheme, completeSection } = useConfiguratorStatusContext();
const [completed, setCompleted] = useState(false);
const { t } = useTranslation();
const navigate = useNavigate();

if (completed) {
return (
<ConfiguratorSection title={t("ElevationsSection.title")} endStep>
<SectionCompleted />
</ConfiguratorSection>
);
}

return (
<ConfiguratorSection
title={t("ElevationsSection.title")}
singleStep
onCancel={() => navigate("/theme")}
onComplete={() => {
setCompleted(true);
completeSection("elevations");
}}
>
<Columns space={24}>
{(Object.keys(theme.elevations) as (keyof typeof theme.elevations)[]).map((elevation) => (
<ElevationCard
elevation={elevation}
config={theme.elevations[elevation]}
onChange={(elevationConfig) => {
setTheme({
...theme,
elevations: { ...theme.elevations, [elevation]: elevationConfig },
});
}}
/>
))}
</Columns>
</ConfiguratorSection>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ButtonLink, Feedback, Inline, Stack } from "@buildo/bento-design-system";
import { useTranslation } from "react-i18next";
import { IconConfetti } from "../PhosphorIcons";

export function SectionCompleted() {
const { t } = useTranslation();
return (
<Stack space={40} align="center">
<Feedback size="large" title={t("ElevationsSection.completed")} icon={IconConfetti} />
<Inline space={16}>
<ButtonLink
size="large"
kind="solid"
hierarchy="secondary"
label={t("ElevationsSection.returnToMyTheme")}
href="/theme"
/>
<ButtonLink
size="large"
kind="solid"
hierarchy="primary"
label={t("ElevationsSection.goToTokens")}
href="/theme/tokens"
/>
</Inline>
</Stack>
);
}
3 changes: 2 additions & 1 deletion packages/configuration-builder/src/MyTheme.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ export function MyTheme() {
name={t("Theme.Foundations.Elevations.title")}
description={t("Theme.Foundations.Elevations.description")}
icon={IconSubtract}
disabled
kind={sections.elevations ? "done" : "todo"}
onClick={() => navigate("/theme/elevations")}
/>
<SectionCard
name={t("Theme.Foundations.Tokens.title")}
Expand Down
17 changes: 16 additions & 1 deletion packages/configuration-builder/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"Next": "Next",
"Back": "Back",
"Cancel": "Cancel",
"Confirm": "Confirm",
"App.name": "Design System Configurator",
"App.description": "In three simple steps, the configurator will guide you through the customization of your new design system.",
"App.bentoDS": "Bento Design System",
Expand Down Expand Up @@ -176,6 +177,20 @@
"TokensSection.returnToMyTheme": "Return to My Theme",
"LoremIpsum.title": "The quick brown fox jumps over the lazy dog.",
"LoremIpsum.text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
"LoremIpsum.longText": "Lorem ipsum dolor sit amet consectetur. Nisl porttitor mi convallis cras sed enim. Eu commodo ac eget purus lacinia eu massa proin in. Cras bibendum malesuada lacus amet sagittis molestie mi nec aenean. Tortor sagittis blandit justo at platea ultrices egestas. Congue nec donec adipiscing elementum nunc elementum lectus egestas. Non morbi a facilisis urna aliquam. Et urna posuere sit sociis adipiscing facilisis id. Cras pharetra ac mattis vitae ultricies ac dictum turpis. Fermentum quam viverra aliquet adipiscing est sollicitudin in. Rhoncus odio tellus amet facilisi felis ullamcorper. Convallis auctor a vitae quam pharetra tristique. Eget molestie gravida proin sed sapien euismod dignissim. Diam massa fringilla vitae leo. Ut feugiat convallis in sem magnis. Non nascetur cursus in fringilla nunc aenean lorem non. In mollis vitae in curabitur a integer sit lacus eros. Velit facilisis ornare ultricies aliquet vivamus a. Amet aliquam venenatis fusce senectus nullam. Et sit fermentum neque ornare ut sit dignissim leo turpis. Pharetra quam risus dolor vel egestas massa. Tincidunt habitant habitant imperdiet pellentesque sed lectus pharetra facilisi tempus. Convallis vitae eu vivamus a faucibus. Eget sed tristique ut tincidunt nulla cras est leo. Fames senectus amet neque felis ut bibendum pellentesque non. Nisl facilisis consequat fermentum amet aliquet vitae. Hac tristique etiam id suscipit viverra rhoncus neque. Tincidunt ullamcorper aliquam sed magna eget tellus sodales. Libero volutpat lectus vel erat id bibendum quis nam in. Ut ipsum sagittis augue elit libero. Orci massa faucibus amet sem."
"LoremIpsum.longText": "Lorem ipsum dolor sit amet consectetur. Nisl porttitor mi convallis cras sed enim. Eu commodo ac eget purus lacinia eu massa proin in. Cras bibendum malesuada lacus amet sagittis molestie mi nec aenean. Tortor sagittis blandit justo at platea ultrices egestas. Congue nec donec adipiscing elementum nunc elementum lectus egestas. Non morbi a facilisis urna aliquam. Et urna posuere sit sociis adipiscing facilisis id. Cras pharetra ac mattis vitae ultricies ac dictum turpis. Fermentum quam viverra aliquet adipiscing est sollicitudin in. Rhoncus odio tellus amet facilisi felis ullamcorper. Convallis auctor a vitae quam pharetra tristique. Eget molestie gravida proin sed sapien euismod dignissim. Diam massa fringilla vitae leo. Ut feugiat convallis in sem magnis. Non nascetur cursus in fringilla nunc aenean lorem non. In mollis vitae in curabitur a integer sit lacus eros. Velit facilisis ornare ultricies aliquet vivamus a. Amet aliquam venenatis fusce senectus nullam. Et sit fermentum neque ornare ut sit dignissim leo turpis. Pharetra quam risus dolor vel egestas massa. Tincidunt habitant habitant imperdiet pellentesque sed lectus pharetra facilisi tempus. Convallis vitae eu vivamus a faucibus. Eget sed tristique ut tincidunt nulla cras est leo. Fames senectus amet neque felis ut bibendum pellentesque non. Nisl facilisis consequat fermentum amet aliquet vitae. Hac tristique etiam id suscipit viverra rhoncus neque. Tincidunt ullamcorper aliquam sed magna eget tellus sodales. Libero volutpat lectus vel erat id bibendum quis nam in. Ut ipsum sagittis augue elit libero. Orci massa faucibus amet sem.",
"ElevationsSection.title": "Elevations",
"ElevationsSection.completed": "Elevations section completed",
"ElevationsSection.returnToMyTheme": "Return to My Theme",
"ElevationsSection.goToTokens": "Go to Tokens",
"Elevation.small": "Small",
"Elevation.medium": "Medium",
"Elevation.large": "Large",
"Elevation.smallDescription": "Use Small elevation to slightly detach interactive elements from the page layout, such as Cards.",
"Elevation.mediumDescription": "Use Medium elevation for elements that are integral to the page layout, such as Headers.",
"Elevation.largeDescription": "Use Large elevation for non-layout elements, such as Modals.",
"Elevation.x": "X",
"Elevation.y": "Y",
"Elevation.blur": "Blur",
"Elevation.color": "Color"
}
}
5 changes: 5 additions & 0 deletions packages/configuration-builder/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { MyTheme } from "./MyTheme";
import { Home } from "./Home";
import { ColorsSection } from "./ColorsSection/ColorsSection";
import { TokensSection } from "./TokensSection/TokensSection";
import { ElevationsSection } from "./ElevationsSection/ElevationsSection";

export const router = createBrowserRouter([
{
Expand All @@ -21,6 +22,10 @@ export const router = createBrowserRouter([
path: "/theme/typography",
element: null,
},
{
path: "/theme/elevations",
element: <ElevationsSection />,
},
{
path: "/theme/tokens",
element: <TokensSection />,
Expand Down
Loading