diff --git a/apps/website/screens/theme-generator/ThemeGeneratorConfigPage.tsx b/apps/website/screens/theme-generator/ThemeGeneratorConfigPage.tsx index 599561a72..9f810c991 100644 --- a/apps/website/screens/theme-generator/ThemeGeneratorConfigPage.tsx +++ b/apps/website/screens/theme-generator/ThemeGeneratorConfigPage.tsx @@ -5,6 +5,7 @@ import BottomButtons from "./components/BottomButtons"; import { BrandingDetails } from "./steps/BrandingDetails"; import { generateTokens, handleExport } from "./utils"; import { Colors, FileData, Step } from "./types"; +import ReviewDetails from "./steps/ReviewDetails"; const steps = [ { @@ -89,7 +90,7 @@ const ThemeGeneratorConfigPage = () => { case 1: return <>; case 2: - return <>; + return ; } }; diff --git a/apps/website/screens/theme-generator/components/review/ReviewBrandingAssets.tsx b/apps/website/screens/theme-generator/components/review/ReviewBrandingAssets.tsx new file mode 100644 index 000000000..1c1a32e5e --- /dev/null +++ b/apps/website/screens/theme-generator/components/review/ReviewBrandingAssets.tsx @@ -0,0 +1,45 @@ +import { DxcContainer, DxcFlex, DxcGrid, DxcImage, DxcTypography } from "@dxc-technology/halstack-react"; +import { useMemo } from "react"; +import { Logos } from "../../types"; + +const BrandingAsset = ({ label, logo }: { label: string; logo?: string }) => { + if (!logo) return null; + return ( + + + {label} + + + + + + + + ); +}; + +const ReviewBrandingAssets = ({ logos }: { logos: Logos }) => { + const brandingAssets = useMemo(() => { + return [ + { label: "Main logo", logo: logos.mainLogo?.[0]?.preview }, + { label: "Footer logo", logo: logos.footerLogo?.[0]?.preview }, + { label: "Reduced footer logo", logo: logos.footerReducedLogo?.[0]?.preview }, + { label: "Favicon", logo: logos.favicon?.[0]?.preview }, + ]; + }, [logos]); + + return ( + + {brandingAssets.length && + brandingAssets.map(({ label, logo }) => )} + + ); +}; + +export default ReviewBrandingAssets; diff --git a/apps/website/screens/theme-generator/components/review/ReviewSectionContainer.tsx b/apps/website/screens/theme-generator/components/review/ReviewSectionContainer.tsx new file mode 100644 index 000000000..a15502c24 --- /dev/null +++ b/apps/website/screens/theme-generator/components/review/ReviewSectionContainer.tsx @@ -0,0 +1,22 @@ +import { DxcContainer, DxcFlex } from "@dxc-technology/halstack-react"; + +const ReviewSectionContainer = ({ title, children }: { title: React.ReactNode; children: React.ReactNode }) => { + return ( + + + {title} + {children} + + + ); +}; + +export default ReviewSectionContainer; diff --git a/apps/website/screens/theme-generator/components/review/ReviewTokensGrid.tsx b/apps/website/screens/theme-generator/components/review/ReviewTokensGrid.tsx new file mode 100644 index 000000000..639f62244 --- /dev/null +++ b/apps/website/screens/theme-generator/components/review/ReviewTokensGrid.tsx @@ -0,0 +1,60 @@ +import { DxcFlex, DxcGrid, DxcTypography } from "@dxc-technology/halstack-react"; +import { Tokens } from "../../types"; +import React, { useMemo } from "react"; +import { divideColorTokens, SHADE_VALUES } from "../../utils"; + +const ReviewTokensGrid = ({ generatedTokens }: { generatedTokens: Tokens }) => { + const tokenGroups = useMemo(() => divideColorTokens(generatedTokens), [generatedTokens]); + return ( + + {SHADE_VALUES.map((value, index) => + index === 0 ? ( + + + {value} + + + ) : ( + + {value} + + ) + )} + {Object.entries(tokenGroups).map(([group, colors]) => ( + + + + {group.charAt(0).toUpperCase() + group.slice(1)} + + + {colors.map((color: string, index: number) => ( +
+ ))} + + ))} + + ); +}; + +export default ReviewTokensGrid; diff --git a/apps/website/screens/theme-generator/components/review/ReviewTokensList.tsx b/apps/website/screens/theme-generator/components/review/ReviewTokensList.tsx new file mode 100644 index 000000000..59a414aea --- /dev/null +++ b/apps/website/screens/theme-generator/components/review/ReviewTokensList.tsx @@ -0,0 +1,13 @@ +import { DxcContainer, DxcTypography } from "@dxc-technology/halstack-react"; + +const ReviewTokensList = ({ themeJson }: { themeJson: string }) => { + return ( + + + {themeJson} + + + ); +}; + +export default ReviewTokensList; diff --git a/apps/website/screens/theme-generator/steps/ReviewDetails.tsx b/apps/website/screens/theme-generator/steps/ReviewDetails.tsx new file mode 100644 index 000000000..904ad6455 --- /dev/null +++ b/apps/website/screens/theme-generator/steps/ReviewDetails.tsx @@ -0,0 +1,68 @@ +import { DxcButton, DxcFlex, DxcGrid, DxcTypography, useToast } from "@dxc-technology/halstack-react"; +import { Logos, Tokens } from "../types"; +import ReviewTokensGrid from "../components/review/ReviewTokensGrid"; +import ReviewTokensList from "../components/review/ReviewTokensList"; +import ReviewBrandingAssets from "../components/review/ReviewBrandingAssets"; +import { copyToClipboard } from "../utils"; +import { useMemo } from "react"; +import ReviewSectionContainer from "../components/review/ReviewSectionContainer"; + +const ReviewDetails = ({ generatedTokens, logos }: { generatedTokens: Tokens; logos: Logos }) => { + const toast = useToast(); + + const themeJson = useMemo(() => { + const themeObject = { + tokens: generatedTokens, + logos: { + mainLogo: "", + footerLogo: "", + footerReducedLogo: "", + favicon: "", + }, + }; + return JSON.stringify(themeObject, null, 2); + }, [generatedTokens]); + + const handleCopy = () => { + copyToClipboard(themeJson) + .then(() => { + toast.success({ message: "Copied to clipboard" }); + }) + .catch(() => { + toast.warning({ message: "Failed to copy to clipboard" }); + }); + }; + + return ( + <> + + + Color palette & theme + + + + } + > + + + + + + + + + Branding assets + + } + > + + + + ); +}; + +export default ReviewDetails; diff --git a/apps/website/screens/theme-generator/types.ts b/apps/website/screens/theme-generator/types.ts index 0a444a9f1..0b82e2375 100644 --- a/apps/website/screens/theme-generator/types.ts +++ b/apps/website/screens/theme-generator/types.ts @@ -5,6 +5,7 @@ export type Tokens = Record; export type BaseColors = Record; export type Step = 0 | 1 | 2; + export type Colors = { primary: CssColor; secondary: CssColor; @@ -15,22 +16,38 @@ export type Colors = { error: CssColor; warning: CssColor; }; + +export type TokenGroups = { + primary: string[]; + secondary: string[]; + tertiary: string[]; + semantic01: string[]; + semantic02: string[]; + semantic03: string[]; + semantic04: string[]; + neutral: string[]; + alpha: string[]; +}; + export type FileData = { error?: string; file: File; preview?: string; }; + export type Logos = { mainLogo: FileData[]; footerLogo: FileData[]; footerReducedLogo: FileData[]; favicon: FileData[]; }; + export type Field = { id: string; label: string; helperText: string; }; + export type BrandingDetailsSection = { id: string; title: string; diff --git a/apps/website/screens/theme-generator/utils.ts b/apps/website/screens/theme-generator/utils.ts index 25cf9062a..2f08c97df 100644 --- a/apps/website/screens/theme-generator/utils.ts +++ b/apps/website/screens/theme-generator/utils.ts @@ -1,5 +1,5 @@ import { Color, BackgroundColor, Theme, type CssColor } from "@adobe/leonardo-contrast-colors"; -import { BaseColors, Tokens } from "./types"; +import { BaseColors, TokenGroups, Tokens } from "./types"; /** * Contrast ratios for generating color shades @@ -106,3 +106,30 @@ export const handleExport = (tokens: Tokens) => { export const copyToClipboard = (value: string) => { return navigator.clipboard.writeText(value); }; + +export const divideColorTokens = (tokens: Tokens) => { + const groups: TokenGroups = { + primary: [], + secondary: [], + tertiary: [], + semantic01: [], + semantic02: [], + semantic03: [], + semantic04: [], + neutral: [], + alpha: [], + }; + + for (const [key, value] of Object.entries(tokens)) { + // divides tokens names into 3 sections + const sections = key.match(/^--color-([a-z0-9]+)-(.+)$/); + if (sections) { + // the second section indicates the group (primary, secondary, etc.) + const group = sections[1]; + if (group && group in groups) { + groups[group as keyof TokenGroups].push(value); + } + } + } + return groups; +};