diff --git a/packages/editor/package.json b/packages/editor/package.json index 6a955261..358ba974 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -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", diff --git a/packages/editor/src/components/CodeEditor/CodeEditor.tsx b/packages/editor/src/components/CodeEditor/CodeEditor.tsx new file mode 100644 index 00000000..77d8069d --- /dev/null +++ b/packages/editor/src/components/CodeEditor/CodeEditor.tsx @@ -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; + 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 ( + { + 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', + }} + /> + ); +}; diff --git a/packages/editor/src/components/CodeEditor/CssEditor.tsx b/packages/editor/src/components/CodeEditor/CssEditor.tsx deleted file mode 100644 index 6d021135..00000000 --- a/packages/editor/src/components/CodeEditor/CssEditor.tsx +++ /dev/null @@ -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(null); - const cm = useRef(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 ; -}; diff --git a/packages/editor/src/components/CodeEditor/SchemaEditor.tsx b/packages/editor/src/components/CodeEditor/SchemaEditor.tsx deleted file mode 100644 index da8e8e99..00000000 --- a/packages/editor/src/components/CodeEditor/SchemaEditor.tsx +++ /dev/null @@ -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(null); - const cm = useRef(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 ; -}; diff --git a/packages/editor/src/components/CodeEditor/index.ts b/packages/editor/src/components/CodeEditor/index.ts index 5c232548..b1a09781 100644 --- a/packages/editor/src/components/CodeEditor/index.ts +++ b/packages/editor/src/components/CodeEditor/index.ts @@ -1,3 +1,2 @@ export * from './StateViewer'; -export * from './SchemaEditor'; -export * from './CssEditor'; +export * from './CodeEditor'; diff --git a/packages/editor/src/components/CodeModeModal.tsx b/packages/editor/src/components/CodeModeModal.tsx index 1d53d201..058aa771 100644 --- a/packages/editor/src/components/CodeModeModal.tsx +++ b/packages/editor/src/components/CodeModeModal.tsx @@ -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 = observer(({ app, onClose, services Code Mode - - + + diff --git a/packages/editor/src/components/ComponentForm/StyleTraitForm/StyleTraitForm.tsx b/packages/editor/src/components/ComponentForm/StyleTraitForm/StyleTraitForm.tsx index 8ec75f5a..e4fe3512 100644 --- a/packages/editor/src/components/ComponentForm/StyleTraitForm/StyleTraitForm.tsx +++ b/packages/editor/src/components/ComponentForm/StyleTraitForm/StyleTraitForm.tsx @@ -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>; @@ -258,7 +259,15 @@ export const StyleTraitForm: React.FC = props => { - changeStyleContent(i, v)} /> + changeStyleContent(i, v)} + /> diff --git a/packages/editor/src/components/DataSource/ApiForm/Response.tsx b/packages/editor/src/components/DataSource/ApiForm/Response.tsx index 312f1e66..70b79b05 100644 --- a/packages/editor/src/components/DataSource/ApiForm/Response.tsx +++ b/packages/editor/src/components/DataSource/ApiForm/Response.tsx @@ -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.loading ? : } + {props.loading ? ( + + ) : ( + + )} diff --git a/packages/editor/src/components/DataSource/ApiForm/Result.tsx b/packages/editor/src/components/DataSource/ApiForm/Result.tsx deleted file mode 100644 index 8c58dd59..00000000 --- a/packages/editor/src/components/DataSource/ApiForm/Result.tsx +++ /dev/null @@ -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(null); - const cm = useRef(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 ; -}; diff --git a/packages/editor/vite.config.ts b/packages/editor/vite.config.ts index cd7dc906..5c6a5533 100644 --- a/packages/editor/vite.config.ts +++ b/packages/editor/vite.config.ts @@ -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: { diff --git a/yarn.lock b/yarn.lock index 5855ce47..220bcc67 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"