mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2025-02-23 17:49:49 +08:00
refactor(editor): replace codemirror with react-codemirror2
This commit is contained in:
parent
ab60a14972
commit
373cfb9ba9
@ -57,6 +57,7 @@
|
||||
"mobx-react-lite": "^3.2.2",
|
||||
"re-resizable": "^6.9.5",
|
||||
"react": "^17.0.2",
|
||||
"react-codemirror2": "^7.2.1",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-json-tree": "^0.16.1",
|
||||
"scroll-into-view": "^1.16.2",
|
||||
|
53
packages/editor/src/components/CodeEditor/CodeEditor.tsx
Normal file
53
packages/editor/src/components/CodeEditor/CodeEditor.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import React, { useState } from 'react';
|
||||
import { UnControlled as CodeMirror } from 'react-codemirror2';
|
||||
import 'codemirror/mode/css/css';
|
||||
import 'codemirror/addon/fold/brace-fold';
|
||||
import 'codemirror/addon/fold/foldgutter';
|
||||
import 'codemirror/addon/display/autorefresh';
|
||||
import type { ModeSpec, ModeSpecOptions } from 'codemirror';
|
||||
|
||||
// Result
|
||||
import 'codemirror/mode/javascript/javascript';
|
||||
import { css, cx } from '@emotion/css';
|
||||
|
||||
export const CodeEditor: React.FC<{
|
||||
defaultCode: string;
|
||||
className?: string;
|
||||
mode?: string | ModeSpec<ModeSpecOptions>;
|
||||
onChange?: (v: string) => void;
|
||||
onBlur?: (v: string) => void;
|
||||
}> = ({ defaultCode, mode, className, onChange, onBlur }) => {
|
||||
const [value, setValue] = useState(defaultCode);
|
||||
const style = css`
|
||||
.CodeMirror {
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
return (
|
||||
<CodeMirror
|
||||
className={cx([style, className])}
|
||||
value={value}
|
||||
onChange={(_x, _y, v: string) => {
|
||||
setValue(v);
|
||||
onChange?.(v);
|
||||
}}
|
||||
onBlur={() => {
|
||||
setValue(value);
|
||||
onBlur?.(value);
|
||||
}}
|
||||
options={{
|
||||
mode: mode || 'css',
|
||||
foldGutter: true,
|
||||
lineWrapping: true,
|
||||
lineNumbers: false,
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
||||
foldOptions: {
|
||||
widget: () => {
|
||||
return '\u002E\u002E\u002E';
|
||||
},
|
||||
},
|
||||
theme: 'ayu-mirage',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,65 +0,0 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import CodeMirror from 'codemirror';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { css } from '@emotion/css';
|
||||
import 'codemirror/mode/css/css';
|
||||
import 'codemirror/addon/fold/brace-fold';
|
||||
import 'codemirror/addon/fold/foldgutter';
|
||||
import 'codemirror/addon/display/autorefresh';
|
||||
|
||||
export const CssEditor: React.FC<{
|
||||
defaultCode: string;
|
||||
onChange?: (v: string) => void;
|
||||
onBlur?: (v: string) => void;
|
||||
}> = ({ defaultCode, onChange, onBlur }) => {
|
||||
const style = css`
|
||||
width: 100%;
|
||||
.CodeMirror {
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
}
|
||||
`;
|
||||
|
||||
const wrapperEl = useRef<HTMLDivElement>(null);
|
||||
const cm = useRef<CodeMirror.Editor | null>(null);
|
||||
useEffect(() => {
|
||||
if (!wrapperEl.current) {
|
||||
return;
|
||||
}
|
||||
if (!cm.current) {
|
||||
cm.current = CodeMirror(wrapperEl.current, {
|
||||
value: defaultCode,
|
||||
mode: {
|
||||
name: 'css',
|
||||
},
|
||||
foldGutter: true,
|
||||
lineWrapping: true,
|
||||
lineNumbers: false,
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
||||
foldOptions: {
|
||||
widget: () => {
|
||||
return '\u002E\u002E\u002E';
|
||||
},
|
||||
},
|
||||
theme: 'ayu-mirage',
|
||||
autoRefresh: { delay: 50 },
|
||||
});
|
||||
} else {
|
||||
cm.current.setValue(defaultCode);
|
||||
}
|
||||
const changeHandler = (instance: CodeMirror.Editor) => {
|
||||
onChange?.(instance.getValue());
|
||||
};
|
||||
const blurHandler = (instance: CodeMirror.Editor) => {
|
||||
onBlur?.(instance.getValue());
|
||||
};
|
||||
cm.current.on('change', changeHandler);
|
||||
cm.current.on('blur', blurHandler);
|
||||
return () => {
|
||||
cm.current?.off('change', changeHandler);
|
||||
cm.current?.off('blur', blurHandler);
|
||||
};
|
||||
}, [onBlur, onChange, defaultCode]);
|
||||
|
||||
return <Box className={style} ref={wrapperEl} />;
|
||||
};
|
@ -1,67 +0,0 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import CodeMirror from 'codemirror';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { css } from '@emotion/css';
|
||||
import 'codemirror/mode/javascript/javascript';
|
||||
import 'codemirror/addon/fold/brace-fold';
|
||||
import 'codemirror/addon/fold/foldgutter';
|
||||
import 'codemirror/addon/display/autorefresh';
|
||||
|
||||
export const SchemaEditor: React.FC<{
|
||||
defaultCode: string;
|
||||
onChange: (v: string) => void;
|
||||
}> = ({ defaultCode, onChange }) => {
|
||||
const style = css`
|
||||
.CodeMirror {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const wrapperEl = useRef<HTMLDivElement>(null);
|
||||
const cm = useRef<CodeMirror.Editor | null>(null);
|
||||
useEffect(() => {
|
||||
if (!wrapperEl.current) {
|
||||
return;
|
||||
}
|
||||
if (!cm.current) {
|
||||
cm.current = CodeMirror(wrapperEl.current, {
|
||||
value: defaultCode,
|
||||
mode: {
|
||||
name: 'javascript',
|
||||
json: true,
|
||||
},
|
||||
foldGutter: true,
|
||||
lineWrapping: true,
|
||||
lineNumbers: true,
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
||||
foldOptions: {
|
||||
widget: () => {
|
||||
return '\u002E\u002E\u002E';
|
||||
},
|
||||
},
|
||||
theme: 'ayu-mirage',
|
||||
/**
|
||||
* Codemirror has a serach addon which can search all the content
|
||||
* without render all.
|
||||
* But it's search behavior is differnet with popular code editors
|
||||
* and the native UX of the browser:
|
||||
* https://github.com/codemirror/CodeMirror/issues/4491#issuecomment-284741358
|
||||
* So since our schema is not that large, currently we will render
|
||||
* all content to support native search.
|
||||
*/
|
||||
viewportMargin: Infinity,
|
||||
autoRefresh: { delay: 50 },
|
||||
});
|
||||
}
|
||||
const handler = (instance: CodeMirror.Editor) => {
|
||||
onChange(instance.getValue());
|
||||
};
|
||||
cm.current.on('change', handler);
|
||||
return () => {
|
||||
cm.current?.off('change', handler);
|
||||
};
|
||||
}, [defaultCode, onChange]);
|
||||
|
||||
return <Box className={style} ref={wrapperEl} height="100%" width="100%" />;
|
||||
};
|
@ -1,3 +1,2 @@
|
||||
export * from './StateViewer';
|
||||
export * from './SchemaEditor';
|
||||
export * from './CssEditor';
|
||||
export * from './CodeEditor';
|
||||
|
@ -13,9 +13,10 @@ import {
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { genOperation } from '../operations';
|
||||
import { AppModel } from '../AppModel/AppModel';
|
||||
import { SchemaEditor } from './CodeEditor';
|
||||
import { EditorServices } from '../types';
|
||||
import { Application } from '@sunmao-ui/core';
|
||||
import { CodeEditor } from './CodeEditor/CodeEditor';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
type Props = {
|
||||
app: Application;
|
||||
@ -46,8 +47,18 @@ export const CodeModeModal: React.FC<Props> = observer(({ app, onClose, services
|
||||
<ModalHeader>Code Mode</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Box overflow="auto" minHeight="600px" maxHeight="1000px">
|
||||
<SchemaEditor defaultCode={JSON.stringify(app, null, 2)} onChange={setCode} />
|
||||
<Box overflow="auto" height="calc(100vh - 270px)">
|
||||
<CodeEditor
|
||||
className={css`
|
||||
height: 100%;
|
||||
`}
|
||||
mode={{
|
||||
name: 'javascript',
|
||||
json: true,
|
||||
}}
|
||||
defaultCode={JSON.stringify(app, null, 2)}
|
||||
onChange={setCode}
|
||||
/>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
|
@ -21,10 +21,11 @@ import { ComponentSchema } from '@sunmao-ui/core';
|
||||
import { CORE_VERSION, CoreTraitName } from '@sunmao-ui/shared';
|
||||
import { FontWidget, SizeWidget, ColorWidget, SpaceWidget } from '@sunmao-ui/editor-sdk';
|
||||
import { capitalize } from 'lodash';
|
||||
import { CssEditor } from '../../../components/CodeEditor';
|
||||
import { genOperation } from '../../../operations';
|
||||
import { formWrapperCSS } from '../style';
|
||||
import { EditorServices } from '../../../types';
|
||||
import { CodeEditor } from '../../CodeEditor';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
type PartialCSSProperties = Partial<Record<keyof React.CSSProperties, any>>;
|
||||
|
||||
@ -258,7 +259,15 @@ export const StyleTraitForm: React.FC<Props> = props => {
|
||||
</Box>
|
||||
</CollapsibleFormControl>
|
||||
<CollapsibleFormControl label="CSS">
|
||||
<CssEditor defaultCode={style} onBlur={v => changeStyleContent(i, v)} />
|
||||
<CodeEditor
|
||||
className={css`
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
`}
|
||||
mode="css"
|
||||
defaultCode={style}
|
||||
onBlur={v => changeStyleContent(i, v)}
|
||||
/>
|
||||
</CollapsibleFormControl>
|
||||
</VStack>
|
||||
</AccordionPanel>
|
||||
|
@ -10,7 +10,8 @@ import {
|
||||
Spinner,
|
||||
Tag,
|
||||
} from '@chakra-ui/react';
|
||||
import { Result } from './Result';
|
||||
import { CodeEditor } from '../../CodeEditor';
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
interface Props {
|
||||
data?: unknown;
|
||||
@ -56,7 +57,21 @@ export const Response: React.FC<Props> = props => {
|
||||
</h2>
|
||||
<AccordionPanel pb={4} padding={0} height="250px">
|
||||
<Flex alignItems="center" justifyContent="center" height="100%">
|
||||
{props.loading ? <Spinner /> : <Result defaultCode={error || data} />}
|
||||
{props.loading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<CodeEditor
|
||||
className={css`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
`}
|
||||
mode={{
|
||||
name: 'javascript',
|
||||
json: true,
|
||||
}}
|
||||
defaultCode={error || data}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
|
@ -1,54 +0,0 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import CodeMirror from 'codemirror';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { css } from '@emotion/css';
|
||||
import 'codemirror/mode/javascript/javascript';
|
||||
import 'codemirror/addon/fold/brace-fold';
|
||||
import 'codemirror/addon/fold/foldgutter';
|
||||
import 'codemirror/addon/display/autorefresh';
|
||||
|
||||
export const Result: React.FC<{
|
||||
defaultCode: string;
|
||||
}> = ({ defaultCode }) => {
|
||||
const style = css`
|
||||
.CodeMirror {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const wrapperEl = useRef<HTMLDivElement>(null);
|
||||
const cm = useRef<CodeMirror.Editor | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!wrapperEl.current) {
|
||||
return;
|
||||
}
|
||||
if (!cm.current) {
|
||||
cm.current = CodeMirror(wrapperEl.current, {
|
||||
value: defaultCode,
|
||||
mode: {
|
||||
name: 'javascript',
|
||||
json: true,
|
||||
},
|
||||
foldGutter: true,
|
||||
lineWrapping: true,
|
||||
lineNumbers: true,
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
|
||||
foldOptions: {
|
||||
widget: () => {
|
||||
return '\u002E\u002E\u002E';
|
||||
},
|
||||
},
|
||||
viewportMargin: Infinity,
|
||||
readOnly: true,
|
||||
autoRefresh: { delay: 50 },
|
||||
});
|
||||
}
|
||||
setTimeout(() => {
|
||||
cm.current?.refresh();
|
||||
});
|
||||
}, [defaultCode]);
|
||||
|
||||
return <Box className={style} ref={wrapperEl} height="100%" width="100%" />;
|
||||
};
|
@ -15,6 +15,10 @@ export default defineConfig({
|
||||
esbuild: {
|
||||
logOverride: { 'this-is-undefined-in-esm': 'silent' },
|
||||
},
|
||||
define: {
|
||||
// react-codemirror2 need this
|
||||
global: 'globalThis',
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: {
|
||||
|
@ -10846,6 +10846,11 @@ react-clientside-effect@^1.2.5:
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.13"
|
||||
|
||||
react-codemirror2@^7.2.1:
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/react-codemirror2/-/react-codemirror2-7.2.1.tgz#38dab492fcbe5fb8ebf5630e5bb7922db8d3a10c"
|
||||
integrity sha512-t7YFmz1AXdlImgHXA9Ja0T6AWuopilub24jRaQdPVbzUJVNKIYuy3uCFZYa7CE5S3UW6SrSa5nAqVQvtzRF9gw==
|
||||
|
||||
react-color@^2.19.3:
|
||||
version "2.19.3"
|
||||
resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.19.3.tgz#ec6c6b4568312a3c6a18420ab0472e146aa5683d"
|
||||
|
Loading…
Reference in New Issue
Block a user