Skip to content
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
20 changes: 20 additions & 0 deletions apps/website/hooks/useCopyToClipboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useToast } from "@dxc-technology/halstack-react";

const useCopyToClipboard = () => {
const toast = useToast();

const handleCopy = (text: string) => {
navigator.clipboard
.writeText(text)
.then(() => {
toast.success({ message: "Code copied to the clipboard." });
})
.catch(() => {
toast.warning({ message: "Failed to copy the text to the clipboard." });
});
};

return handleCopy;
};

export default useCopyToClipboard;
16 changes: 9 additions & 7 deletions apps/website/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ReactElement, ReactNode, useMemo, useState } from "react";
import type { NextPage } from "next";
import type { AppProps } from "next/app";
import Head from "next/head";
import { DxcApplicationLayout, DxcLink, DxcToastsQueue } from "@dxc-technology/halstack-react";
import { DxcApplicationLayout, DxcInset, DxcLink, DxcToastsQueue } from "@dxc-technology/halstack-react";
import MainContent from "@/common/MainContent";
import { useRouter } from "next/router";
import { LinkDetails, LinksSectionDetails, LinksSections } from "@/common/pagesList";
Expand Down Expand Up @@ -116,11 +116,13 @@ export default function App({ Component, pageProps, emotionCache = clientSideEmo
appTitle="Theme Generator"
sideContent={
!currentPath.includes("/theme-generator/user-guide") && (
<Link href="/theme-generator/user-guide" legacyBehavior passHref>
<DxcLink newWindow icon="description">
User guide
</DxcLink>
</Link>
<DxcInset horizontal="var(--spacing-padding-xs)">
<Link href="/theme-generator/user-guide" legacyBehavior passHref>
<DxcLink newWindow icon="description">
User guide
</DxcLink>
</Link>
</DxcInset>
)
}
/>
Expand All @@ -137,7 +139,7 @@ export default function App({ Component, pageProps, emotionCache = clientSideEmo
/>
)
}
footer={isThemeGenerator && <DxcApplicationLayout.Footer mode="reduced" />}
footer={isThemeGenerator ? <DxcApplicationLayout.Footer mode="reduced" /> : undefined}
>
<DxcApplicationLayout.Main>
<DxcToastsQueue duration={7000}>
Expand Down
27 changes: 13 additions & 14 deletions apps/website/screens/common/example/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { useState } from "react";
import styled from "@emotion/styled";
import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live";
import theme from "./liveEditorTheme";
import { DxcButton, DxcFlex, useToast } from "@dxc-technology/halstack-react";
import { DxcButton, DxcFlex } from "@dxc-technology/halstack-react";
import useCopyToClipboard from "hooks/useCopyToClipboard";

const StyledPreview = styled.div`
background-color: var(--color-bg-neutral-lightest);
Expand Down Expand Up @@ -49,24 +50,13 @@ type ExamplePropTypes = {
};
//
const Example = ({ actionsVisible = true, defaultIsVisible = false, example }: ExamplePropTypes) => {
const toast = useToast();
const [isCodeVisible, changeIsCodeVisible] = useState(defaultIsVisible);
const [liveCode, setLiveCode] = useState(example.code);

const handleCodeOnClick = () => {
changeIsCodeVisible(!isCodeVisible);
};

const handleCopy = () => {
navigator.clipboard
.writeText(liveCode)
.then(() => {
toast.success({ message: "Code copied to the clipboard." });
})
.catch(() => {
toast.warning({ message: "Failed to copy the text to the clipboard." });
});
};
const handleCopy = useCopyToClipboard();

return (
<DxcFlex direction="column" gap="var(--spacing-gap-m)">
Expand All @@ -79,7 +69,16 @@ const Example = ({ actionsVisible = true, defaultIsVisible = false, example }: E
</StyledPreview>
{actionsVisible && (
<DxcFlex gap="var(--spacing-gap-s)" justifyContent="flex-end">
{isCodeVisible && <DxcButton icon="content_copy" label="Copy code" mode="tertiary" onClick={handleCopy} />}
{isCodeVisible && (
<DxcButton
icon="content_copy"
label="Copy code"
mode="tertiary"
onClick={() => {
handleCopy(liveCode);
}}
/>
)}
<DxcButton
icon={isCodeVisible ? "code_off" : "code"}
label={isCodeVisible ? "Hide code" : "View code"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
{
Expand Down Expand Up @@ -89,7 +90,7 @@ const ThemeGeneratorConfigPage = () => {
case 1:
return <></>;
case 2:
return <></>;
return <ReviewDetails generatedTokens={tokens} logos={logos} />;
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const BottomButtons = ({
}) => (
<DxcContainer
width="100%"
padding="var(--spacing-padding-s) var(--spacing-padding-l)"
padding="var(--spacing-padding-s) var(--spacing-padding-xl)"
background={{ color: "var(--color-bg-neutral-lightest)" }}
boxSizing="border-box"
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { useState } from "react";
import { DxcContainer, DxcFlex, DxcPopover, DxcTextInput, useToast } from "@dxc-technology/halstack-react";
import { DxcContainer, DxcFlex, DxcPopover, DxcTextInput } from "@dxc-technology/halstack-react";
import styled from "@emotion/styled";
import { SketchPicker } from "react-color";
import { copyToClipboard } from "../../utils";
import useCopyToClipboard from "hooks/useCopyToClipboard";

const ColorBox = styled.button<{ color: string }>`
aspect-ratio: 1;
Expand Down Expand Up @@ -36,7 +36,6 @@ export const ColorCard = ({
const [isOpen, setIsOpen] = useState(false);
const [inputValue, setInputValue] = useState(color);
const [error, setError] = useState<string>();
const toast = useToast();

const handleBlur = ({ value, error }: { value: string; error?: string }) => {
setError(error);
Expand All @@ -45,15 +44,7 @@ export const ColorCard = ({
}
};

const handleCopy = (value: string) => {
copyToClipboard(value)
.then(() => {
toast.success({ message: "Copied to clipboard" });
})
.catch(() => {
toast.warning({ message: "Failed to copy to clipboard" });
});
};
const handleCopy = useCopyToClipboard();

return (
<DxcContainer
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { DxcContainer, DxcFlex, DxcGrid, DxcImage, DxcParagraph, DxcTypography } from "@dxc-technology/halstack-react";
import { useMemo } from "react";
import { Logos } from "../../types";

const BrandingAsset = ({ label, logo }: { label: string; logo?: string }) => {
return (
<DxcContainer width="250px" height="100%">
<DxcFlex direction="column" gap="var(--spacing-gap-xs)" fullHeight>
<DxcTypography fontWeight="var(--typography-label-semibold)">{label}</DxcTypography>
<DxcContainer
width="100%"
maxHeight="calc(100% - 28px)"
height="130px"
background={{ color: "var(--color-bg-neutral-light)" }}
borderRadius="var(--border-radius-m)"
padding="var(--spacing-padding-m)"
boxSizing="border-box"
>
<DxcFlex alignItems="center" justifyContent="center" fullHeight>
{logo ? (
<DxcImage width="100%" height="100%" objectFit="contain" src={logo} alt={label} />
) : (
<DxcParagraph>No asset selected.</DxcParagraph>
)}
</DxcFlex>
</DxcContainer>
</DxcFlex>
</DxcContainer>
);
};

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 (
<DxcGrid templateColumns={["repeat(4, 1fr)"]} templateRows={["1fr"]} gap="var(--spacing-gap-ml)">
{brandingAssets.some((asset) => asset.logo) ? (
brandingAssets.map((asset) => <BrandingAsset key={asset.label} label={asset.label} logo={asset.logo} />)
) : (
<DxcParagraph>No branding assets selected.</DxcParagraph>
)}
</DxcGrid>
);
};

export default ReviewBrandingAssets;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { DxcContainer, DxcFlex } from "@dxc-technology/halstack-react";

const ReviewSectionContainer = ({ title, children }: { title: React.ReactNode; children: React.ReactNode }) => {
return (
<DxcContainer
boxSizing="border-box"
width="100%"
maxWidth="1112px"
padding="var(--spacing-padding-ml)"
borderRadius="var(--border-radius-m)"
background={{ color: "var(--color-bg-neutral-lightest)" }}
boxShadow="var(--shadow-100)"
>
<DxcFlex direction="column" gap="var(--spacing-gap-ml)">
{title}
{children}
</DxcFlex>
</DxcContainer>
);
};

export default ReviewSectionContainer;
Original file line number Diff line number Diff line change
@@ -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 (
<DxcGrid
templateColumns={["100px", "repeat(10, 1fr)"]}
templateRows={["auto", "repeat(9, 1fr)"]}
gap="var(--spacing-gap-xs)"
>
{SHADE_VALUES.map((value, index) =>
index === 0 ? (
<DxcGrid.Item column={"2/3"} key={"shade_" + value}>
<DxcTypography
fontSize="var(--typography-body-s)"
color="var(--color-fg-neutral-strong)"
textAlign="center"
>
{value}
</DxcTypography>
</DxcGrid.Item>
) : (
<DxcTypography
key={"shade_" + value}
fontSize="var(--typography-body-s)"
color="var(--color-fg-neutral-strong)"
textAlign="center"
>
{value}
</DxcTypography>
)
)}
{Object.entries(tokenGroups).map(([group, colors]) => (
<React.Fragment key={group}>
<DxcFlex alignItems="center">
<DxcTypography fontWeight="var(--typography-label-semibold)">
{group.charAt(0).toUpperCase() + group.slice(1)}
</DxcTypography>
</DxcFlex>
{colors.map((color: string, index: number) => (
<div
key={group + "_" + SHADE_VALUES[index]}
style={{
backgroundColor: color,
width: "100%",
aspectRatio: "1 / 1",
borderRadius: "var(--border-radius-m)",
}}
/>
))}
</React.Fragment>
))}
</DxcGrid>
);
};

export default ReviewTokensGrid;
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { DxcContainer, DxcTypography } from "@dxc-technology/halstack-react";

const ReviewTokensList = ({ themeJson }: { themeJson: string }) => {
return (
<DxcContainer height="100%" overflow="auto" borderRadius="var(--border-radius-m)">
<DxcTypography as="pre" fontSize="var(--typography-body-xs)" whiteSpace="pre">
{themeJson}
</DxcTypography>
</DxcContainer>
);
};

export default ReviewTokensList;
63 changes: 63 additions & 0 deletions apps/website/screens/theme-generator/steps/ReviewDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { DxcButton, DxcFlex, DxcGrid, DxcTypography } 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 useCopyToClipboard from "hooks/useCopyToClipboard";
import { useMemo } from "react";
import ReviewSectionContainer from "../components/review/ReviewSectionContainer";

const ReviewDetails = ({ generatedTokens, logos }: { generatedTokens: Tokens; logos: Logos }) => {
const themeJson = useMemo(() => {
const themeObject = {
tokens: generatedTokens,
logos: {
mainLogo: "",
footerLogo: "",
footerReducedLogo: "",
favicon: "",
},
};
return JSON.stringify(themeObject, null, 2);
}, [generatedTokens]);

const handleCopy = useCopyToClipboard();

return (
<>
<ReviewSectionContainer
title={
<DxcFlex justifyContent="space-between" alignItems="center">
<DxcTypography fontSize="var(--typography-title-l)" fontWeight="var(--typography-title-bold)">
Color palette & theme
</DxcTypography>
<DxcButton
mode="secondary"
icon="content_copy"
size={{ height: "medium" }}
onClick={() => handleCopy(themeJson)}
/>
</DxcFlex>
}
>
<DxcGrid templateColumns={["2fr", "1fr"]} templateRows={["368px"]} gap="var(--spacing-gap-m)">
<DxcFlex justifyContent="center">
<ReviewTokensGrid generatedTokens={generatedTokens} />
</DxcFlex>
<ReviewTokensList themeJson={themeJson} />
</DxcGrid>
</ReviewSectionContainer>
<ReviewSectionContainer
title={
<DxcTypography fontSize="var(--typography-title-l)" fontWeight="var(--typography-title-bold)">
Branding assets
</DxcTypography>
}
>
<ReviewBrandingAssets logos={logos} />
</ReviewSectionContainer>
</>
);
};

export default ReviewDetails;
Loading