diff --git a/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditor.module.scss b/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditor.module.scss index 891dda43b..29c11e012 100644 --- a/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditor.module.scss +++ b/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditor.module.scss @@ -1,4 +1,5 @@ .wrapper { + min-height: 500px; max-height: 500px; overflow-y: auto; } diff --git a/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditor.tsx b/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditor.tsx index c9639a0db..a175ca43e 100644 --- a/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditor.tsx +++ b/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditor.tsx @@ -1,71 +1,38 @@ import Prism from 'virtual:prismjs'; -import { forwardRef, memo, useEffect, useImperativeHandle, useState } from 'react'; +import { memo } from 'react'; import Editor from 'react-simple-code-editor'; import 'prismjs/components/prism-css'; import 'prismjs/themes/prism-tomorrow.min.css'; + import style from './StyleEditor.module.scss'; interface CodeEditorProps { language: string; - initialValue: string; - isDirty: boolean; - setIsDirty: (value: boolean) => void; + value: string; + onChange: (value: string) => void; } -const CodeEditor = forwardRef((props: CodeEditorProps, cssRef) => { - const { language, initialValue, isDirty, setIsDirty } = props; - - const [code, setCode] = useState(initialValue); - +export default memo(CodeEditor); +function CodeEditor({ language, onChange, value }: CodeEditorProps) { const highlight = (code: string) => { const grammar = Prism.languages[language]; return grammar ? Prism.highlight(code, grammar, language) : code; }; - const handleChange = (newCode: string) => { - setCode(newCode); - }; - - useImperativeHandle(cssRef, () => { - return { - getCss: () => code, - }; - }); - - // add contents to editor on mount and any change in initialValue - useEffect(() => { - setCode(initialValue); - }, [initialValue]); - - // handle dirty state on change - useEffect(() => { - if (initialValue.trim() !== code.trim() && !isDirty && code.length !== 0) { - setIsDirty(true); - } - - if (initialValue.trim() === code.trim() && isDirty) { - setIsDirty(false); - } - }, [initialValue, code, isDirty, setIsDirty]); - return (
); -}); - -CodeEditor.displayName = 'StyleEditor'; - -export default memo(CodeEditor); +} diff --git a/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditorModal.module.scss b/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditorModal.module.scss index 084608cc4..4418f2a34 100644 --- a/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditorModal.module.scss +++ b/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditorModal.module.scss @@ -13,3 +13,12 @@ gap: 1rem; flex-direction: column; } + +.editorBody { + position: relative; + min-height: 500px; + background-color: $gray-1350; + border: 1px solid $gray-1100; + border-radius: 3px; + overflow: hidden; +} diff --git a/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditorModal.tsx b/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditorModal.tsx index c91c5c20b..58023911f 100644 --- a/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditorModal.tsx +++ b/apps/client/src/features/app-settings/panel/settings-panel/composite/StyleEditorModal.tsx @@ -1,4 +1,4 @@ -import { Suspense, lazy, useEffect, useRef, useState } from 'react'; +import { Suspense, lazy, useEffect, useState } from 'react'; import { getCSSContents, postCSSContents, restoreCSSContents } from '../../../../../common/api/assets'; import Button from '../../../../../common/components/buttons/Button'; @@ -15,24 +15,21 @@ interface CodeEditorModalProps { onClose: () => void; } -interface CSSRef { - getCss: () => string; -} - export default function CodeEditorModal({ isOpen, onClose }: CodeEditorModalProps) { - const [css, setCSS] = useState(''); - const [isDirty, setIsDirty] = useState(false); + const [savedCss, setSavedCss] = useState(''); + const [draftCss, setDraftCss] = useState(''); + const [isLoadingCss, setIsLoadingCss] = useState(false); const [saveLoading, setSaveLoading] = useState(false); const [resetLoading, setResetLoading] = useState(false); const [error, setError] = useState(null); - const cssRef = useRef(null); + const isDirty = savedCss.trim() !== draftCss.trim(); const handleRestore = async () => { try { setResetLoading(true); const defaultCss = await restoreCSSContents(); - setCSS(defaultCss); + setDraftCss(defaultCss); } catch (_error) { /** no error handling for now */ } finally { @@ -43,11 +40,8 @@ export default function CodeEditorModal({ isOpen, onClose }: CodeEditorModalProp const handleSave = async () => { try { setSaveLoading(true); - if (cssRef.current) { - await postCSSContents(cssRef.current.getCss()); - setCSS(cssRef.current.getCss()); - setIsDirty(false); - } + await postCSSContents(draftCss); + setSavedCss(draftCss); } catch (_error) { /** no error handling for now */ } finally { @@ -55,22 +49,43 @@ export default function CodeEditorModal({ isOpen, onClose }: CodeEditorModalProp } }; - const clear = () => setCSS(''); + const clear = () => setDraftCss(''); useEffect(() => { + let isCancelled = false; + async function fetchServerCSS() { // check for isOpen to fetch recent css if (isOpen) { try { + setError(null); + setIsLoadingCss(true); const css = await getCSSContents(); - setCSS(css); + if (isCancelled) { + return; + } + setSavedCss(css); + setDraftCss(css); } catch (_error) { + if (isCancelled) { + return; + } + setSavedCss(''); + setDraftCss(''); setError('Failed to load CSS from server'); /** no error handling for now */ + } finally { + if (!isCancelled) { + setIsLoadingCss(false); + } } } } fetchServerCSS(); + + return () => { + isCancelled = true; + }; }, [isOpen]); return ( @@ -81,9 +96,14 @@ export default function CodeEditorModal({ isOpen, onClose }: CodeEditorModalProp showCloseButton showBackdrop bodyElements={ - - - +
+ {!isLoadingCss && ( + }> + + + )} + +
} footerElements={