From 087430f08e895615df16d341870de55d39a0de59 Mon Sep 17 00:00:00 2001 From: Vlad Date: Mon, 4 Dec 2023 14:39:00 +0100 Subject: [PATCH] fix: code editor (#740) * fix: code editor layout and logic --- .../components/CodeEditor/CodeEditor.scss | 8 +--- .../components/CodeEditor/CodeEditor.tsx | 28 ++++++------ src/editor/containers/Editor/Editor.tsx | 40 ++++++++++++----- src/editor/containers/Form/Form.tsx | 5 ++- src/editor/containers/Form/hooks.ts | 45 +++++++++++++++++++ src/editor/context.ts | 2 + src/editor/types/index.ts | 2 + 7 files changed, 98 insertions(+), 32 deletions(-) create mode 100644 src/editor/containers/Form/hooks.ts diff --git a/src/editor/components/CodeEditor/CodeEditor.scss b/src/editor/components/CodeEditor/CodeEditor.scss index dc2c459a3..b5507b467 100644 --- a/src/editor/components/CodeEditor/CodeEditor.scss +++ b/src/editor/components/CodeEditor/CodeEditor.scss @@ -8,17 +8,13 @@ $block: '.#{$ns}code-editor'; overflow: hidden; &_fullscreen { - position: fixed; + position: absolute; top: 0; left: 0; width: 100%; - height: 100vh; + height: 100%; z-index: 1000; background: var(--g-color-base-background); - - #{$block}__header { - margin-top: var(--pc-editor-header-height); - } } &__code { diff --git a/src/editor/components/CodeEditor/CodeEditor.tsx b/src/editor/components/CodeEditor/CodeEditor.tsx index ee658c5de..2f5def0fb 100644 --- a/src/editor/components/CodeEditor/CodeEditor.tsx +++ b/src/editor/components/CodeEditor/CodeEditor.tsx @@ -1,12 +1,13 @@ -import React, {useCallback, useMemo, useState} from 'react'; +import React, {useCallback, useContext, useState} from 'react'; import {ChevronsCollapseUpRight, ChevronsExpandUpRight} from '@gravity-ui/icons'; import {Button, Icon} from '@gravity-ui/uikit'; -import yaml from 'js-yaml'; +import debounce from 'lodash/debounce'; import MonacoEditor from 'react-monaco-editor'; -import {PageContent} from '../../../models'; +import {PageContent, Theme} from '../../../models'; import {block} from '../../../utils'; +import {EditorContext} from '../../context'; import {parseCode} from '../../utils/code'; import {CodeEditorMessageProps} from '../../utils/validation'; @@ -17,7 +18,7 @@ import './CodeEditor.scss'; const b = block('code-editor'); interface CodeEditorProps { - content: PageContent; + code: string; fullscreenModeOn: boolean; validator: (code: string) => CodeEditorMessageProps; onFullscreenModeOnUpdate: (fullscreenModeOn: boolean) => void; @@ -26,22 +27,23 @@ interface CodeEditorProps { } export const CodeEditor = ({ - content, onChange, validator, fullscreenModeOn, onFullscreenModeOnUpdate, + code, }: CodeEditorProps) => { - const value = useMemo(() => yaml.dump(content), [content]); - const [message, setMessage] = useState(() => validator(value)); + const [message, setMessage] = useState(() => validator(code)); + const {theme = Theme.Light} = useContext(EditorContext); + // eslint-disable-next-line react-hooks/exhaustive-deps const onChangeWithValidation = useCallback( - (code: string) => { - const validationResult = validator(code); + debounce((newCode: string) => { + const validationResult = validator(newCode); setMessage(validationResult); - onChange(parseCode(code)); - }, + onChange(parseCode(newCode)); + }, 200), [onChange, validator], ); @@ -61,11 +63,11 @@ export const CodeEditor = ({
diff --git a/src/editor/containers/Editor/Editor.tsx b/src/editor/containers/Editor/Editor.tsx index c53879378..7ba73d190 100644 --- a/src/editor/containers/Editor/Editor.tsx +++ b/src/editor/containers/Editor/Editor.tsx @@ -13,6 +13,7 @@ import {useCodeValidator} from '../../hooks/useCodeValidator'; import {useMainState} from '../../store/main'; import {useSettingsState} from '../../store/settings'; import {EditorProps, ViewModeItem} from '../../types'; +import {FormTab} from '../../types/index'; import {addCustomDecorator, checkIsMobile, getBlockId} from '../../utils'; import {Form} from '../Form/Form'; @@ -22,6 +23,7 @@ export const Editor = ({ providerProps, transformContent, deviceEmulationSettings, + theme: editorTheme, ...rest }: EditorProps) => { const { @@ -35,7 +37,7 @@ export const Editor = ({ } = useMainState(rest); const { viewMode, - theme, + theme: constructorTheme, onViewModeUpdate, onThemeUpdate, formTab, @@ -45,6 +47,9 @@ export const Editor = ({ } = useSettingsState(); const isEditingMode = viewMode === ViewModeItem.Edititng; + const isCodeOnlyMode = + codeFullscreeModeOn && formTab === FormTab.Code && viewMode === ViewModeItem.Edititng; + const transformedContent = useMemo( () => (transformContent ? transformContent(content, {viewMode}) : content), [content, transformContent, viewMode], @@ -95,11 +100,20 @@ export const Editor = ({ providerProps: { ...providerProps, isMobile: checkIsMobile(viewMode), - theme, + theme: constructorTheme, }, deviceEmulationSettings, + theme: editorTheme, }), - [providerProps, rest.custom, viewMode, transformedContent, deviceEmulationSettings, theme], + [ + providerProps, + rest.custom, + viewMode, + transformedContent, + deviceEmulationSettings, + constructorTheme, + editorTheme, + ], ); useEffect(() => { @@ -111,7 +125,7 @@ export const Editor = ({ {isEditingMode && ( @@ -130,14 +144,16 @@ export const Editor = ({ /> )} - - - - - - - {isEditingMode && } - + {!isCodeOnlyMode && ( + + + + + + + {isEditingMode && } + + )} ); diff --git a/src/editor/containers/Form/Form.tsx b/src/editor/containers/Form/Form.tsx index 5226cb9bc..65c1dbe90 100644 --- a/src/editor/containers/Form/Form.tsx +++ b/src/editor/containers/Form/Form.tsx @@ -12,6 +12,8 @@ import useFormSpec from '../../hooks/useFormSpec'; import {FormTab} from '../../types'; import {CodeEditorMessageProps} from '../../utils/validation'; +import {useCode} from './hooks'; + import './Form.scss'; const b = block('editor-form'); @@ -48,6 +50,7 @@ export const Form = memo( onCodeFullscreeModeOnUpdate, }: FormProps) => { const {blocks, ...page} = content || {}; + const code = useCode({activeTab, content, codeFullscreeModeOn}); const spec = useFormSpec(schema); const {blocks: blocksSpec, page: pageSpec} = spec || {}; @@ -104,7 +107,7 @@ export const Form = memo( case FormTab.Code: { form = ( ) { + const [code, setCode] = useState(''); + + const prevTab = usePreviousValue(activeTab); + const prevContentLength = usePreviousValue(content.blocks?.length); + const prevCodeFullscreeModeOn = usePreviousValue(codeFullscreeModeOn); + + useEffect(() => { + const switchedToCodeEditing = activeTab !== prevTab && activeTab === FormTab.Code; + const blocksCountChanged = prevContentLength !== content.blocks?.length; + const codeModeSwitched = codeFullscreeModeOn !== prevCodeFullscreeModeOn; + + if (blocksCountChanged || switchedToCodeEditing || codeModeSwitched) { + setCode(yaml.dump(content, {lineWidth: -1})); + } + }, [ + activeTab, + prevTab, + content, + prevContentLength, + codeFullscreeModeOn, + prevCodeFullscreeModeOn, + ]); + + return code; +} diff --git a/src/editor/context.ts b/src/editor/context.ts index c69d3ae82..81bc77521 100644 --- a/src/editor/context.ts +++ b/src/editor/context.ts @@ -1,6 +1,7 @@ import React from 'react'; import {PageConstructorProps, PageConstructorProviderProps} from '../containers/PageConstructor'; +import {Theme} from '../models/common'; import {EditorProps} from './types'; @@ -8,6 +9,7 @@ export interface EditorContextType { constructorProps?: PageConstructorProps; providerProps?: PageConstructorProviderProps; deviceEmulationSettings?: EditorProps['deviceEmulationSettings']; + theme?: Theme; } export const EditorContext = React.createContext>({}); diff --git a/src/editor/types/index.ts b/src/editor/types/index.ts index 56702bc5a..302b2b18c 100644 --- a/src/editor/types/index.ts +++ b/src/editor/types/index.ts @@ -1,5 +1,6 @@ import {PageConstructorProps, PageConstructorProviderProps} from '../../containers/PageConstructor'; import {BlockDecorationProps, PageContent} from '../../models'; +import {Theme} from '../../models/common'; import {SchemaCustomConfig} from '../../schema'; import {EditBlockActions} from '../components/EditBlock/EditBlock'; @@ -27,6 +28,7 @@ export interface EditorProps transformContent?: ContentTransformer; customSchema?: SchemaCustomConfig; deviceEmulationSettings?: DeviceEmulationSettings; + theme?: Theme; } export interface EditBlockEditorProps {