From 7af90e88c6b7df64f532700983d6256573cb932b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Lago=C3=A1?= Date: Fri, 16 Jun 2023 11:59:00 +0100 Subject: [PATCH] chore: allow manually editing the code in the theme creator (#3431) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paulo Lagoá --- .../CodeEditor/CodeEditor.styles.tsx | 3 + app/src/generator/CodeEditor/CodeEditor.tsx | 69 +++++++++++---- app/src/generator/Content.tsx | 2 +- app/src/generator/GeneratorContext.tsx | 72 ++++++++++++---- app/src/generator/Sidebar/Sidebar.styles.tsx | 9 +- app/src/generator/Sidebar/Sidebar.tsx | 83 +++++++------------ app/src/generator/utils.ts | 5 +- 7 files changed, 144 insertions(+), 99 deletions(-) diff --git a/app/src/generator/CodeEditor/CodeEditor.styles.tsx b/app/src/generator/CodeEditor/CodeEditor.styles.tsx index b478e4148d..3f7465aed9 100644 --- a/app/src/generator/CodeEditor/CodeEditor.styles.tsx +++ b/app/src/generator/CodeEditor/CodeEditor.styles.tsx @@ -12,6 +12,9 @@ export const styles = { top: "2px!important", }, }, + ".suggest-widget": { + display: "none!important", + }, }), codeEditorTools: css({ display: "flex", diff --git a/app/src/generator/CodeEditor/CodeEditor.tsx b/app/src/generator/CodeEditor/CodeEditor.tsx index 4284a06a5f..d36588f23f 100644 --- a/app/src/generator/CodeEditor/CodeEditor.tsx +++ b/app/src/generator/CodeEditor/CodeEditor.tsx @@ -8,7 +8,8 @@ import { useTheme, } from "@hitachivantara/uikit-react-core"; import { GeneratorContext } from "generator/GeneratorContext"; -import { useContext } from "react"; +import { useContext, useEffect, useState } from "react"; +import debounce from "lodash/debounce"; import { Download, Undo, @@ -21,26 +22,33 @@ import { HvCodeEditor } from "@hitachivantara/uikit-react-code-editor"; import { IconButton } from "components/common/IconButton"; import { styles } from "./CodeEditor.styles"; -export const groupsToShow = ["acce", "atmo", "base", "sema"] as const; // "sup", "cat" - const CodeEditor = ({ themeName, setCopied }): JSX.Element => { - const { activeTheme, selectedTheme } = useTheme(); + const { selectedTheme, selectedMode, changeTheme } = useTheme(); - const { customTheme, updateCustomTheme, undo, redo, canUndo, canRedo } = - useContext(GeneratorContext); + const { + customTheme, + updateCustomTheme, + undo, + redo, + canUndo, + canRedo, + themeChanges, + } = useContext(GeneratorContext); const fileName = `${themeName}.ts`; - const fullCode = getThemeCode( - themeName, - selectedTheme, - activeTheme, - customTheme - ); + const fullCode = getThemeCode(themeName, selectedTheme, themeChanges); const encodedCode = encodeURIComponent(fullCode); + const [value, setValue] = useState(fullCode); + + useEffect(() => { + const code = getThemeCode(themeName, selectedTheme, themeChanges); + setValue(code); + }, [customTheme]); + const onCopyHandler = () => { - navigator.clipboard.writeText(fullCode); + navigator.clipboard.writeText(value); setCopied(true); }; @@ -52,6 +60,34 @@ const CodeEditor = ({ themeName, setCopied }): JSX.Element => { updateCustomTheme(newTheme); }; + const codeChangedHandler = (code?: string) => { + if (!code) return; + + const snippet = code.substring( + code.indexOf("({") + 1, + code.indexOf("});") + 1 + ); + + const themeJson = snippet.replace( + /(['"])?([a-zA-Z0-9_]+)(['"])?:/g, + '"$2": ' + ); + + try { + const parsed = JSON.parse(themeJson); + if (customTheme.base !== parsed.base) { + changeTheme(parsed.base, selectedMode); + } else { + const newTheme = createTheme(parsed); + updateCustomTheme(newTheme); + } + } catch { + console.log("error processing theme JSON"); + } + }; + + const debouncedHandler = debounce(codeChangedHandler, 1000); + return ( @@ -95,15 +131,16 @@ const CodeEditor = ({ themeName, setCopied }): JSX.Element => { ); diff --git a/app/src/generator/Content.tsx b/app/src/generator/Content.tsx index be861ceacc..2ab956a996 100644 --- a/app/src/generator/Content.tsx +++ b/app/src/generator/Content.tsx @@ -25,7 +25,7 @@ const Content = () => { style={{ width: open ? "calc(100% - 390px)" : "100%", backgroundColor: - customTheme?.colors?.modes[selectedMode]?.backgroundColor, + customTheme?.colors?.modes?.[selectedMode]?.backgroundColor, }} > diff --git a/app/src/generator/GeneratorContext.tsx b/app/src/generator/GeneratorContext.tsx index 98317933e9..d04febcc0d 100644 --- a/app/src/generator/GeneratorContext.tsx +++ b/app/src/generator/GeneratorContext.tsx @@ -6,13 +6,17 @@ import { Dispatch, SetStateAction, useEffect, + useMemo, } from "react"; +import merge from "lodash/merge"; +import { themeDiff } from "./utils"; type GeneratorContextProp = { customTheme: HvTheme | HvThemeStructure; updateCustomTheme: ( newTheme: HvThemeStructure | HvTheme, - addToHistory?: boolean + addToHistory?: boolean, + updateThemeChanges?: boolean ) => void; open?: boolean; @@ -28,6 +32,8 @@ type GeneratorContextProp = { redo?: () => void; canUndo?: boolean; canRedo?: boolean; + + themeChanges?: Partial; }; export const GeneratorContext = createContext({ @@ -46,8 +52,13 @@ const GeneratorProvider = ({ children }) => { const [historyStep, setHistoryStep] = useState(-1); const [canUndo, setCanUndo] = useState(false); const [canRedo, setCanRedo] = useState(false); + const [themeChanges, setThemeChanges] = useState({}); - const updateCustomTheme = (newTheme, addToHistory = true) => { + const updateCustomTheme = ( + newTheme, + addToHistory = true, + updateThemeChanges = true + ) => { if (addToHistory) { let newHistory: HvTheme[] = history; if (history.length - historyStep > 1) { @@ -59,6 +70,13 @@ const GeneratorProvider = ({ children }) => { setHistory([...newHistory, newTheme]); setHistoryStep((prev) => prev + 1); } + + if (updateThemeChanges) { + setThemeChanges((prev) => { + const diff = themeDiff(customTheme, newTheme); + return merge({}, prev, diff); + }); + } setCustomTheme(newTheme); }; @@ -88,23 +106,41 @@ const GeneratorProvider = ({ children }) => { setHistoryStep((prev) => prev + 1); }; + const value = useMemo( + () => ({ + customTheme, + updateCustomTheme, + open, + setOpen, + tutorialOpen, + setTutorialOpen, + currentStep, + setCurrentStep, + undo, + redo, + canUndo, + canRedo, + themeChanges, + }), + [ + customTheme, + updateCustomTheme, + open, + setOpen, + tutorialOpen, + setTutorialOpen, + currentStep, + setCurrentStep, + undo, + redo, + canUndo, + canRedo, + themeChanges, + ] + ); + return ( - + {children} ); diff --git a/app/src/generator/Sidebar/Sidebar.styles.tsx b/app/src/generator/Sidebar/Sidebar.styles.tsx index d170875b2b..8e00afed15 100644 --- a/app/src/generator/Sidebar/Sidebar.styles.tsx +++ b/app/src/generator/Sidebar/Sidebar.styles.tsx @@ -23,16 +23,9 @@ export const styles = { ...theme.typography.sectionTitle, textTransform: "uppercase", }), - themeName: css({ - display: "flex", - gap: 10, - alignItems: "center", - }), - themeNameInput: css({ - width: "100%", - }), themeBase: css({ display: "flex", + flexDirection: "row", justifyContent: "space-between", gap: 20, alignItems: "center", diff --git a/app/src/generator/Sidebar/Sidebar.tsx b/app/src/generator/Sidebar/Sidebar.tsx index 0f536c51f4..9277c4f43a 100644 --- a/app/src/generator/Sidebar/Sidebar.tsx +++ b/app/src/generator/Sidebar/Sidebar.tsx @@ -3,7 +3,6 @@ import { HvBaseTheme, HvBox, HvDropdown, - HvInput, HvListValue, HvLoading, HvSnackbar, @@ -15,7 +14,6 @@ import { } from "@hitachivantara/uikit-react-core"; import { lazy, Suspense, useContext, useEffect, useState } from "react"; import { GeneratorContext } from "generator/GeneratorContext"; -import debounce from "lodash/debounce"; import CodeEditor from "generator/CodeEditor"; import { Bold, @@ -36,28 +34,23 @@ const Zindices = lazy(() => import("generator/Zindices")); const Sizes = lazy(() => import("generator/Sizes")); const Sidebar = () => { - const { selectedTheme, selectedMode, colorModes, themes, changeTheme } = + const { selectedTheme, selectedMode, colorModes, changeTheme, themes } = useTheme(); - const { updateCustomTheme, open } = useContext(GeneratorContext); + const { customTheme, updateCustomTheme, open, themeChanges } = + useContext(GeneratorContext); - const [themeName, setThemeName] = useState("customTheme"); const [copied, setCopied] = useState(false); const [tab, setTab] = useState(0); - const nameChangeHandler = (name) => { - setThemeName(name); - }; - useEffect(() => { const newTheme = createTheme({ - name: themeName, + name: customTheme.name, base: selectedTheme as HvBaseTheme, + ...themeChanges, }); - updateCustomTheme(newTheme, false); - }, [themeName, selectedTheme]); - - const debouncedNameChangeHandler = debounce(nameChangeHandler, 250); + updateCustomTheme(newTheme, false, false); + }, [customTheme.name, selectedTheme]); const handleClose = (event, reason) => { if (reason === "clickaway") return; @@ -80,48 +73,32 @@ const Sidebar = () => { Theme Creator - - Name: - - debouncedNameChangeHandler(value)} - placeholder={themeName} - /> - - - - Theme: - ({ - value: name, - label: name, - selected: name === selectedTheme, - }))} - onChange={(t) => { - // console.log("new theme is", (t as HvListValue)?.value) - changeTheme((t as HvListValue)?.value, selectedMode); - }} - /> - - - Mode: - ({ - value: name, - label: name, - selected: name === selectedMode, - }))} - onChange={(mode) => - changeTheme(selectedTheme, (mode as HvListValue)?.value) - } - /> - + Base: + ({ + value: name, + label: name, + selected: name === selectedTheme, + }))} + onChange={(base) => { + changeTheme((base as HvListValue)?.value, selectedMode); + }} + /> + Mode: + ({ + value: name, + label: name, + selected: name === selectedMode, + }))} + onChange={(mode) => + changeTheme(selectedTheme, (mode as HvListValue)?.value) + } + /> - + { export const getThemeCode = ( themeName: string, selectedTheme: string, - activeTheme, - customTheme + themeChanges ) => { const final = { name: themeName, base: selectedTheme, - ...themeDiff(activeTheme, customTheme), + ...themeChanges, }; // the `replace` bit below is just a regex to remove the quotes from