Skip to content

Commit

Permalink
Merge pull request #732 from buildo/BEN-131
Browse files Browse the repository at this point in the history
Implement HomePage and MyTheme page
  • Loading branch information
veej authored Sep 27, 2023
2 parents e34b7a9 + 3deb943 commit f1d055a
Show file tree
Hide file tree
Showing 33 changed files with 1,084 additions and 101 deletions.
11 changes: 9 additions & 2 deletions packages/configuration-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@
"@react-aria/i18n": "3.8.1",
"@react-aria/numberfield": "3.7.0",
"@react-stately/numberfield": "3.6.0",
"@vanilla-extract/css": "1.12.0",
"@vanilla-extract/recipes": "0.5.0",
"i18next": "22.5.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-i18next": "12.3.1",
"ts-pattern": "5.0.5"
"react-router-dom": "^6.15.0",
"ts-pattern": "3.3.5"
},
"devDependencies": {
"@react-types/numberfield": "3.5.0",
Expand All @@ -32,11 +35,15 @@
"@types/react-dom": "18.2.7",
"@typescript-eslint/eslint-plugin": "6.5.0",
"@typescript-eslint/parser": "6.5.0",
"@vanilla-extract/dynamic": "2.0.3",
"@vanilla-extract/sprinkles": "1.6.1",
"@vanilla-extract/vite-plugin": "3.8.2",
"@vitejs/plugin-react": "4.0.4",
"eslint": "8.48.0",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-react-refresh": "0.4.3",
"typescript": "5.1.3",
"vite": "4.4.9"
"vite": "4.4.9",
"vite-plugin-checker": "^0.6.2"
}
}
20 changes: 12 additions & 8 deletions packages/configuration-builder/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { Stack } from "@buildo/bento-design-system";
import { Box, Divider } from "@buildo/bento-design-system";
import { Header } from "./Header/Header";
import { ThemeConfigurator } from "./ThemeConfigurator/ThemeConfigurator";
import { RouterProvider } from "react-router-dom";
import { router } from "./router";

function App() {
export function App() {
return (
<Stack space={0}>
<Box display="flex" flexDirection="column" height="full">
<Header />
<ThemeConfigurator />
</Stack>
<Box flexShrink={0}>
<Divider />
</Box>
<Box display="flex" flexGrow={1} flexDirection="column" overflow="hidden">
<RouterProvider router={router} />
</Box>
</Box>
);
}

export default App;
2 changes: 1 addition & 1 deletion packages/configuration-builder/src/ColorEditor/Palette.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Box } from "@buildo/bento-design-system";
import { LightnessInterpolation } from "./ColorEditor";
import { useState } from "react";
import { IconEyedropper } from "../Icons/IconEyedropper";
import { IconEyedropper } from "../PhosphorIcons";
import { HSLToHex, HexColor } from "../utils/colorUtils";

type Props = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Button, Headline, Inline, Stack, Actions } from "@buildo/bento-design-s
import { useTranslation } from "react-i18next";
import { ColorConfig, ColorEditor } from "../ColorEditor/ColorEditor";
import { HexColor } from "../utils/colorUtils";
import { ThemeConfig } from "../ThemeConfigurator/ThemeConfigurator";
import { defaultColorConfig } from "./defaultColor";
import { ThemeConfig } from "../ConfiguratorStatusContext";

type BrandColors = ThemeConfig["colors"]["brand"];

Expand Down
48 changes: 23 additions & 25 deletions packages/configuration-builder/src/ColorsSection/ColorsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import { useTranslation } from "react-i18next";
import { ConfiguratorSection } from "../ConfiguratorSection/ConfiguratorSection";
import { BrandColors } from "./BrandColors";
import { ThemeConfig } from "../ThemeConfigurator/ThemeConfigurator";
import { useState } from "react";
import { match } from "ts-pattern";
import { InteractiveColor } from "./InteractiveColor";
import { NeutralColor } from "./NeutralColor";
import { SemanticColors } from "./SemanticColors";
import { DataVizColors } from "./DataVizColors";
import { SectionCompleted } from "./SectionCompleted";
import { ThemeConfig, useConfiguratorStatusContext } from "../ConfiguratorStatusContext";

type ColorsConfig = ThemeConfig["colors"];

type Props = {
value: ColorsConfig;
onChange: (value: ColorsConfig) => void;
onComplete: () => void;
};

export function ColorsSection(props: Props) {
export function ColorsSection() {
const steps = [
"brand" as const,
"interactive" as const,
Expand All @@ -27,6 +19,13 @@ export function ColorsSection(props: Props) {
"dataVisualization" as const,
];
const { t } = useTranslation();
const { theme, setTheme, completeSection } = useConfiguratorStatusContext();

const colors = theme.colors;

const onChange = (newValue: ThemeConfig["colors"]) => {
setTheme({ ...theme, colors: newValue });
};

const [completed, setCompleted] = useState(false);
const [currentStep, setCurrentStep] = useState<(typeof steps)[0]>("brand");
Expand All @@ -42,59 +41,58 @@ export function ColorsSection(props: Props) {
return match(completed)
.with(true, () => (
<ConfiguratorSection title={t("ColorsSection.title")} endStep>
<SectionCompleted goToMyTheme={() => {}} goToTypography={() => {}} />
<SectionCompleted />
</ConfiguratorSection>
))
.with(false, () => (
<ConfiguratorSection
key={currentStep} // refresh component to restore scroll position at every step change
title={t("ColorsSection.title")}
steps={steps.map((step) => ({ label: t(`ColorsSection.Step.${step}`) }))}
currentStep={currentStepIndex}
>
{match(currentStep)
.with("brand", () => (
<BrandColors
value={props.value.brand}
onChange={(value) => props.onChange({ ...props.value, brand: value })}
value={colors.brand}
onChange={(value) => onChange({ ...colors, brand: value })}
onCancel={() => {}}
onNext={onNext}
/>
))
.with("interactive", () => (
<InteractiveColor
value={props.value.interactive}
onChange={(value) => props.onChange({ ...props.value, interactive: value })}
brandColors={props.value.brand}
value={theme.colors.interactive}
onChange={(value) => onChange({ ...colors, interactive: value })}
brandColors={colors.brand}
onBack={onBack}
onNext={onNext}
/>
))
.with("neutral", () => (
<NeutralColor
value={props.value.neutral}
onChange={(neutral) => props.onChange({ ...props.value, neutral })}
value={colors.neutral}
onChange={(neutral) => onChange({ ...colors, neutral })}
onBack={onBack}
onNext={onNext}
/>
))
.with("semantic", () => (
<SemanticColors
value={props.value.semantic}
onChange={(semantic) => props.onChange({ ...props.value, semantic })}
value={colors.semantic}
onChange={(semantic) => onChange({ ...colors, semantic })}
onBack={onBack}
onNext={onNext}
/>
))
.with("dataVisualization", () => (
<DataVizColors
value={props.value.dataVisualization}
onChange={(dataVisualization) =>
props.onChange({ ...props.value, dataVisualization })
}
value={colors.dataVisualization}
onChange={(dataVisualization) => onChange({ ...colors, dataVisualization })}
onBack={onBack}
onNext={() => {
setCompleted(true);
props.onComplete();
completeSection("colors");
}}
/>
))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useTranslation } from "react-i18next";
import { ThemeConfig } from "../ThemeConfigurator/ThemeConfigurator";
import { Actions, Headline, Stack } from "@buildo/bento-design-system";
import { ColorEditor } from "../ColorEditor/ColorEditor";
import { ThemeConfig } from "../ConfiguratorStatusContext";

type DataVizColors = ThemeConfig["colors"]["dataVisualization"];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import {
Stack,
unsafeLocalizedString,
} from "@buildo/bento-design-system";
import { ThemeConfig } from "../ThemeConfigurator/ThemeConfigurator";
import { ColorEditor } from "../ColorEditor/ColorEditor";
import { useTranslation } from "react-i18next";
import { useState } from "react";
import { ThemeConfig } from "../ConfiguratorStatusContext";

type InteractiveColor = ThemeConfig["colors"]["interactive"];
type BrandColors = ThemeConfig["colors"]["brand"];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useTranslation } from "react-i18next";
import { ThemeConfig } from "../ThemeConfigurator/ThemeConfigurator";
import { Actions, Headline, Stack } from "@buildo/bento-design-system";
import { ColorEditor } from "../ColorEditor/ColorEditor";
import { ColorPresets } from "./ColorPresets";
import { HexColor } from "../utils/colorUtils";
import { defaultColorConfig } from "./defaultColor";
import { ThemeConfig } from "../ConfiguratorStatusContext";

type NeutralColor = ThemeConfig["colors"]["neutral"];

Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,26 @@
import { Button, Feedback, Inline, Stack } from "@buildo/bento-design-system";
import { ButtonLink, Feedback, Inline, Stack } from "@buildo/bento-design-system";
import { useTranslation } from "react-i18next";
import { IconConfetti } from "../Icons/IconConfetti";
import { IconConfetti } from "../PhosphorIcons";

type Props = {
goToMyTheme: () => void;
goToTypography: () => void;
};

export function SectionCompleted(props: Props) {
export function SectionCompleted() {
const { t } = useTranslation();
return (
<Stack space={40} align="center">
<Feedback size="large" title={t("ColorsSection.completed")} icon={IconConfetti} />
<Inline space={16}>
<Button
<ButtonLink
size="large"
kind="solid"
hierarchy="secondary"
label={t("ColorsSection.returnToMyTheme")}
onPress={props.goToMyTheme}
href="/theme"
/>
<Button
<ButtonLink
size="large"
kind="solid"
hierarchy="primary"
label={t("ColorsSection.goToTypography")}
onPress={props.goToTypography}
href="/theme/typography"
/>
</Inline>
</Stack>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useTranslation } from "react-i18next";
import { ThemeConfig } from "../ThemeConfigurator/ThemeConfigurator";
import { Actions, Headline, Stack } from "@buildo/bento-design-system";
import { ColorEditor } from "../ColorEditor/ColorEditor";
import { ColorPresets } from "./ColorPresets";
import { HexColor } from "../utils/colorUtils";
import { defaultColorConfig } from "./defaultColor";
import { ThemeConfig } from "../ConfiguratorStatusContext";

type SemanticColors = ThemeConfig["colors"]["semantic"];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@ type Props = {
export function ConfiguratorSection(props: Props) {
const { t } = useTranslation();
return (
<Box padding={40} paddingTop={24}>
<Box padding={40} paddingTop={24} flexGrow={1} overflowY="auto">
<Stack space={40}>
<Stack space={24}>
<Breadcrumb
items={[{ label: t("ConfigurationSection.myTheme"), href: "" }, { label: props.title }]}
items={[
{ label: t("ConfigurationSection.myTheme"), href: "/theme" },
{ label: props.title },
]}
/>
<Headline size="medium">{props.title}</Headline>
{!props.endStep && <Stepper steps={props.steps} currentStep={props.currentStep} />}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { useState } from "react";
import { ColorConfig } from "../ColorEditor/ColorEditor";
import { ColorsSection } from "../ColorsSection/ColorsSection";
import { HexColor } from "../utils/colorUtils";
import { defaultTokens } from "@buildo/bento-design-system";
import { defaultColorConfig } from "../ColorsSection/defaultColor";
import { createContext, useContext, useState } from "react";
import { defaultColorConfig } from "./ColorsSection/defaultColor";
import { Children, defaultTokens } from "@buildo/bento-design-system";
import { HexColor } from "./utils/colorUtils";
import { ColorConfig } from "./ColorEditor/ColorEditor";

type BrandColors =
| [ColorConfig]
Expand Down Expand Up @@ -36,8 +35,19 @@ export type ThemeConfig = {
};
};

export function ThemeConfigurator() {
const [themeConfig, setThemeConfig] = useState<ThemeConfig>({
export type ThemeSection = "colors";

type ConfiguratorStatus = {
theme: ThemeConfig;
sections: { [key in ThemeSection]: boolean };
completeSection: (section: ThemeSection) => void;
setTheme: (newTheme: ThemeConfig) => void;
};

export const ConfiguratorStatusContext = createContext<ConfiguratorStatus | null>(null);

export function ConfiguratorStatusProvider(props: { children: Children }) {
const [theme, setTheme] = useState<ThemeConfig>({
colors: {
brand: [defaultColorConfig(defaultTokens.brandColor.brandPrimary as HexColor)],
interactive: defaultColorConfig(
Expand Down Expand Up @@ -67,11 +77,28 @@ export function ThemeConfigurator() {
},
});

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

return (
<ColorsSection
value={themeConfig.colors}
onChange={(colors) => setThemeConfig({ ...themeConfig, colors })}
onComplete={() => {}}
/>
<ConfiguratorStatusContext.Provider
value={{
theme,
setTheme: (newTheme) => setTheme(newTheme),
sections,
completeSection: (section) => setSections({ ...sections, [section]: true }),
}}
>
{props.children}
</ConfiguratorStatusContext.Provider>
);
}

export function useConfiguratorStatusContext() {
const value = useContext(ConfiguratorStatusContext);
if (!value) {
throw new Error("Missing ConfiguratorStatusContext.Provider");
}
return value;
}
Loading

0 comments on commit f1d055a

Please sign in to comment.