Skip to content

Commit 087430f

Browse files
authored
fix: code editor (#740)
* fix: code editor layout and logic
1 parent 7171df4 commit 087430f

File tree

7 files changed

+98
-32
lines changed

7 files changed

+98
-32
lines changed

src/editor/components/CodeEditor/CodeEditor.scss

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,13 @@ $block: '.#{$ns}code-editor';
88
overflow: hidden;
99

1010
&_fullscreen {
11-
position: fixed;
11+
position: absolute;
1212
top: 0;
1313
left: 0;
1414
width: 100%;
15-
height: 100vh;
15+
height: 100%;
1616
z-index: 1000;
1717
background: var(--g-color-base-background);
18-
19-
#{$block}__header {
20-
margin-top: var(--pc-editor-header-height);
21-
}
2218
}
2319

2420
&__code {

src/editor/components/CodeEditor/CodeEditor.tsx

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import React, {useCallback, useMemo, useState} from 'react';
1+
import React, {useCallback, useContext, useState} from 'react';
22

33
import {ChevronsCollapseUpRight, ChevronsExpandUpRight} from '@gravity-ui/icons';
44
import {Button, Icon} from '@gravity-ui/uikit';
5-
import yaml from 'js-yaml';
5+
import debounce from 'lodash/debounce';
66
import MonacoEditor from 'react-monaco-editor';
77

8-
import {PageContent} from '../../../models';
8+
import {PageContent, Theme} from '../../../models';
99
import {block} from '../../../utils';
10+
import {EditorContext} from '../../context';
1011
import {parseCode} from '../../utils/code';
1112
import {CodeEditorMessageProps} from '../../utils/validation';
1213

@@ -17,7 +18,7 @@ import './CodeEditor.scss';
1718
const b = block('code-editor');
1819

1920
interface CodeEditorProps {
20-
content: PageContent;
21+
code: string;
2122
fullscreenModeOn: boolean;
2223
validator: (code: string) => CodeEditorMessageProps;
2324
onFullscreenModeOnUpdate: (fullscreenModeOn: boolean) => void;
@@ -26,22 +27,23 @@ interface CodeEditorProps {
2627
}
2728

2829
export const CodeEditor = ({
29-
content,
3030
onChange,
3131
validator,
3232
fullscreenModeOn,
3333
onFullscreenModeOnUpdate,
34+
code,
3435
}: CodeEditorProps) => {
35-
const value = useMemo(() => yaml.dump(content), [content]);
36-
const [message, setMessage] = useState(() => validator(value));
36+
const [message, setMessage] = useState(() => validator(code));
37+
const {theme = Theme.Light} = useContext(EditorContext);
3738

39+
// eslint-disable-next-line react-hooks/exhaustive-deps
3840
const onChangeWithValidation = useCallback(
39-
(code: string) => {
40-
const validationResult = validator(code);
41+
debounce((newCode: string) => {
42+
const validationResult = validator(newCode);
4143

4244
setMessage(validationResult);
43-
onChange(parseCode(code));
44-
},
45+
onChange(parseCode(newCode));
46+
}, 200),
4547
[onChange, validator],
4648
);
4749

@@ -61,11 +63,11 @@ export const CodeEditor = ({
6163
<div className={b('code')}>
6264
<MonacoEditor
6365
key={String(fullscreenModeOn)}
64-
value={value}
66+
value={code}
6567
language="yaml"
6668
options={options}
6769
onChange={onChangeWithValidation}
68-
theme="vs"
70+
theme={theme === Theme.Dark ? 'vs-dark' : 'vs'}
6971
/>
7072
</div>
7173
<div className={b('footer')}>

src/editor/containers/Editor/Editor.tsx

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {useCodeValidator} from '../../hooks/useCodeValidator';
1313
import {useMainState} from '../../store/main';
1414
import {useSettingsState} from '../../store/settings';
1515
import {EditorProps, ViewModeItem} from '../../types';
16+
import {FormTab} from '../../types/index';
1617
import {addCustomDecorator, checkIsMobile, getBlockId} from '../../utils';
1718
import {Form} from '../Form/Form';
1819

@@ -22,6 +23,7 @@ export const Editor = ({
2223
providerProps,
2324
transformContent,
2425
deviceEmulationSettings,
26+
theme: editorTheme,
2527
...rest
2628
}: EditorProps) => {
2729
const {
@@ -35,7 +37,7 @@ export const Editor = ({
3537
} = useMainState(rest);
3638
const {
3739
viewMode,
38-
theme,
40+
theme: constructorTheme,
3941
onViewModeUpdate,
4042
onThemeUpdate,
4143
formTab,
@@ -45,6 +47,9 @@ export const Editor = ({
4547
} = useSettingsState();
4648

4749
const isEditingMode = viewMode === ViewModeItem.Edititng;
50+
const isCodeOnlyMode =
51+
codeFullscreeModeOn && formTab === FormTab.Code && viewMode === ViewModeItem.Edititng;
52+
4853
const transformedContent = useMemo(
4954
() => (transformContent ? transformContent(content, {viewMode}) : content),
5055
[content, transformContent, viewMode],
@@ -95,11 +100,20 @@ export const Editor = ({
95100
providerProps: {
96101
...providerProps,
97102
isMobile: checkIsMobile(viewMode),
98-
theme,
103+
theme: constructorTheme,
99104
},
100105
deviceEmulationSettings,
106+
theme: editorTheme,
101107
}),
102-
[providerProps, rest.custom, viewMode, transformedContent, deviceEmulationSettings, theme],
108+
[
109+
providerProps,
110+
rest.custom,
111+
viewMode,
112+
transformedContent,
113+
deviceEmulationSettings,
114+
constructorTheme,
115+
editorTheme,
116+
],
103117
);
104118

105119
useEffect(() => {
@@ -111,7 +125,7 @@ export const Editor = ({
111125
<Layout
112126
mode={viewMode}
113127
onModeChange={onViewModeUpdate}
114-
theme={theme}
128+
theme={constructorTheme}
115129
onThemeChange={onThemeUpdate}
116130
>
117131
{isEditingMode && (
@@ -130,14 +144,16 @@ export const Editor = ({
130144
/>
131145
</Layout.Left>
132146
)}
133-
<Layout.Right>
134-
<ErrorBoundary key={errorBoundaryState}>
135-
<PageConstructorProvider {...providerProps} theme={theme}>
136-
<PageConstructor {...outgoingProps} />
137-
</PageConstructorProvider>
138-
</ErrorBoundary>
139-
{isEditingMode && <AddBlock onAdd={onAdd} />}
140-
</Layout.Right>
147+
{!isCodeOnlyMode && (
148+
<Layout.Right>
149+
<ErrorBoundary key={errorBoundaryState}>
150+
<PageConstructorProvider {...providerProps} theme={constructorTheme}>
151+
<PageConstructor {...outgoingProps} />
152+
</PageConstructorProvider>
153+
</ErrorBoundary>
154+
{isEditingMode && <AddBlock onAdd={onAdd} />}
155+
</Layout.Right>
156+
)}
141157
</Layout>
142158
</EditorContext.Provider>
143159
);

src/editor/containers/Form/Form.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import useFormSpec from '../../hooks/useFormSpec';
1212
import {FormTab} from '../../types';
1313
import {CodeEditorMessageProps} from '../../utils/validation';
1414

15+
import {useCode} from './hooks';
16+
1517
import './Form.scss';
1618

1719
const b = block('editor-form');
@@ -48,6 +50,7 @@ export const Form = memo(
4850
onCodeFullscreeModeOnUpdate,
4951
}: FormProps) => {
5052
const {blocks, ...page} = content || {};
53+
const code = useCode({activeTab, content, codeFullscreeModeOn});
5154
const spec = useFormSpec(schema);
5255
const {blocks: blocksSpec, page: pageSpec} = spec || {};
5356

@@ -104,7 +107,7 @@ export const Form = memo(
104107
case FormTab.Code: {
105108
form = (
106109
<CodeEditor
107-
content={content}
110+
code={code}
108111
onChange={onChange}
109112
validator={codeValidator}
110113
fullscreenModeOn={codeFullscreeModeOn}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import {useEffect, useState} from 'react';
2+
3+
import yaml from 'js-yaml';
4+
5+
import usePreviousValue from '../../hooks/usePreviousValue';
6+
import {FormTab} from '../../types';
7+
8+
import {FormProps} from './Form';
9+
10+
/**
11+
* Transorms PageConstructor content in JSON to YAML on code editor mode switching
12+
*
13+
* @param {Object} props - props parent from form
14+
* @returns {string} - updated code
15+
*/
16+
export function useCode({
17+
activeTab,
18+
content,
19+
codeFullscreeModeOn,
20+
}: Pick<FormProps, 'activeTab' | 'content' | 'codeFullscreeModeOn'>) {
21+
const [code, setCode] = useState('');
22+
23+
const prevTab = usePreviousValue(activeTab);
24+
const prevContentLength = usePreviousValue(content.blocks?.length);
25+
const prevCodeFullscreeModeOn = usePreviousValue(codeFullscreeModeOn);
26+
27+
useEffect(() => {
28+
const switchedToCodeEditing = activeTab !== prevTab && activeTab === FormTab.Code;
29+
const blocksCountChanged = prevContentLength !== content.blocks?.length;
30+
const codeModeSwitched = codeFullscreeModeOn !== prevCodeFullscreeModeOn;
31+
32+
if (blocksCountChanged || switchedToCodeEditing || codeModeSwitched) {
33+
setCode(yaml.dump(content, {lineWidth: -1}));
34+
}
35+
}, [
36+
activeTab,
37+
prevTab,
38+
content,
39+
prevContentLength,
40+
codeFullscreeModeOn,
41+
prevCodeFullscreeModeOn,
42+
]);
43+
44+
return code;
45+
}

src/editor/context.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import React from 'react';
22

33
import {PageConstructorProps, PageConstructorProviderProps} from '../containers/PageConstructor';
4+
import {Theme} from '../models/common';
45

56
import {EditorProps} from './types';
67

78
export interface EditorContextType {
89
constructorProps?: PageConstructorProps;
910
providerProps?: PageConstructorProviderProps;
1011
deviceEmulationSettings?: EditorProps['deviceEmulationSettings'];
12+
theme?: Theme;
1113
}
1214

1315
export const EditorContext = React.createContext<Partial<EditorContextType>>({});

src/editor/types/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {PageConstructorProps, PageConstructorProviderProps} from '../../containers/PageConstructor';
22
import {BlockDecorationProps, PageContent} from '../../models';
3+
import {Theme} from '../../models/common';
34
import {SchemaCustomConfig} from '../../schema';
45
import {EditBlockActions} from '../components/EditBlock/EditBlock';
56

@@ -27,6 +28,7 @@ export interface EditorProps
2728
transformContent?: ContentTransformer;
2829
customSchema?: SchemaCustomConfig;
2930
deviceEmulationSettings?: DeviceEmulationSettings;
31+
theme?: Theme;
3032
}
3133

3234
export interface EditBlockEditorProps {

0 commit comments

Comments
 (0)