diff --git a/packages/editor/package.json b/packages/editor/package.json index bc91e55a..24db7fa7 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -45,13 +45,15 @@ "mobx": "^6.3.8", "mobx-react-lite": "^3.2.2", "react": "^17.0.2", - "react-dom": "^17.0.2" + "react-dom": "^17.0.2", + "tern": "^0.24.3" }, "devDependencies": { "@babel/preset-react": "^7.14.5", "@sunmao-ui/vite-plugins": "^1.0.2", "@types/codemirror": "^5.60.5", "@types/lodash-es": "^4.17.5", + "@types/tern": "^0.23.4", "@vitejs/plugin-react": "^1.0.1", "@vitejs/plugin-react-refresh": "^1.3.6", "babel-jest": "^27.2.1", diff --git a/packages/editor/src/components/CodeEditor/TernEditor.tsx b/packages/editor/src/components/CodeEditor/TernEditor.tsx new file mode 100644 index 00000000..3d573ee6 --- /dev/null +++ b/packages/editor/src/components/CodeEditor/TernEditor.tsx @@ -0,0 +1,84 @@ +import React, { useEffect, useRef } from 'react'; +import CodeMirror from 'codemirror'; +import { Box } from '@chakra-ui/react'; +import { css } from '@emotion/react'; +import 'codemirror/mode/javascript/javascript'; +import 'codemirror/addon/fold/brace-fold'; +import 'codemirror/addon/fold/foldgutter'; +import 'codemirror/addon/fold/foldgutter.css'; +import 'codemirror/lib/codemirror.css'; +import 'codemirror/theme/ayu-mirage.css'; +// tern +import 'codemirror/addon/tern/tern'; +import 'tern/plugin/doc_comment'; +import 'tern/plugin/complete_strings'; +import ecma from 'tern/defs/ecmascript.json'; +import { Def } from 'tern'; + +function installTern(cm: CodeMirror.Editor) { + const tern = new CodeMirror.TernServer({ defs: [ecma as unknown as Def] }); + cm.on('cursorActivity', cm => tern.updateArgHints(cm)); + cm.on('change', (_instance, change) => { + if (change.text.length === 1 && change.text[0] === '.') { + tern.complete(cm); + } + }); + return tern; +} + +export const TernEditor: React.FC<{ + defaultCode: string; + onChange?: (v: string) => void; + onBlur?: (v: string) => void; + lineNumbers?: boolean; +}> = ({ defaultCode, onChange, onBlur, lineNumbers = true }) => { + 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, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], + foldOptions: { + widget: () => { + return '\u002E\u002E\u002E'; + }, + }, + theme: 'ayu-mirage', + viewportMargin: Infinity, + }); + installTern(cm.current); + } + 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); + }; + }, [defaultCode]); + + return ; +}; diff --git a/packages/editor/src/components/CodeEditor/index.ts b/packages/editor/src/components/CodeEditor/index.ts index cf0e6eb2..5fa839fb 100644 --- a/packages/editor/src/components/CodeEditor/index.ts +++ b/packages/editor/src/components/CodeEditor/index.ts @@ -1,3 +1,4 @@ export * from './StateEditor'; export * from './SchemaEditor'; export * from './CssEditor'; +export * from './TernEditor'; diff --git a/packages/editor/src/components/ComponentForm/JsonSchemaForm/widgets/GeneralWidget.tsx b/packages/editor/src/components/ComponentForm/JsonSchemaForm/widgets/GeneralWidget.tsx index bfe3b4b1..63026007 100644 --- a/packages/editor/src/components/ComponentForm/JsonSchemaForm/widgets/GeneralWidget.tsx +++ b/packages/editor/src/components/ComponentForm/JsonSchemaForm/widgets/GeneralWidget.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { FieldProps } from '../fields'; -import { SchemaEditor } from 'components/CodeEditor'; +import { TernEditor } from 'components/CodeEditor'; type Props = FieldProps; @@ -8,7 +8,7 @@ const GeneralWidget: React.FC = props => { const { formData, onChange } = props; return ( - { diff --git a/packages/editor/src/components/ComponentForm/StyleTraitForm/index.tsx b/packages/editor/src/components/ComponentForm/StyleTraitForm/index.tsx index 4579f134..4ee9d2c7 100644 --- a/packages/editor/src/components/ComponentForm/StyleTraitForm/index.tsx +++ b/packages/editor/src/components/ComponentForm/StyleTraitForm/index.tsx @@ -32,25 +32,32 @@ export const StyleTraitForm: React.FC = props => { {styleSlots.map(styleSlot => { const styleTrait = styles.find(s => s.properties.styleSlot === styleSlot); - if (!styleTrait) { - return null; - } + const defaultCode = (styleTrait?.properties?.style as string) || ''; return ( {styleSlot} eventBus.send( 'operation', - genOperation('modifyTraitProperty', { - componentId: component.id, - traitIndex: component.traits.indexOf(styleTrait), - properties: { - styleSlot, - style: v, - }, - }) + styleTrait + ? genOperation('modifyTraitProperty', { + componentId: component.id, + traitIndex: component.traits.indexOf(styleTrait), + properties: { + styleSlot, + style: v, + }, + }) + : genOperation('createTrait', { + componentId: component.id, + traitType: 'core/v1/style', + properties: { + styleSlot, + style: v, + }, + }) ) } /> diff --git a/yarn.lock b/yarn.lock index e0ebdb0a..7a2a9217 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3122,6 +3122,13 @@ dependencies: "@types/estree" "*" +"@types/tern@^0.23.4": + version "0.23.4" + resolved "http://192.168.26.29:7001/@types/tern/download/@types/tern-0.23.4.tgz#03926eb13dbeaf3ae0d390caf706b2643a0127fb" + integrity sha1-A5JusT2+rzrg05DK9wayZDoBJ/s= + dependencies: + "@types/estree" "*" + "@types/unist@*", "@types/unist@^2.0.0": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" @@ -3289,11 +3296,28 @@ acorn-jsx@^5.3.1: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== +acorn-loose@^6.0.0: + version "6.1.0" + resolved "http://192.168.26.29:7001/acorn-loose/download/acorn-loose-6.1.0.tgz#3b2de5b3fc64f811c7b6c07cd9128d1476817f94" + integrity sha1-Oy3ls/xk+BHHtsB82RKNFHaBf5Q= + dependencies: + acorn "^6.2.0" + +acorn-walk@^6.0.0: + version "6.2.0" + resolved "http://192.168.26.29:7001/acorn-walk/download/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" + integrity sha1-Ejy487hMIXHx9/slJhWxx4prGow= + acorn-walk@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn@^6.0.0, acorn@^6.2.0: + version "6.4.2" + resolved "http://192.168.26.29:7001/acorn/download/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha1-NYZv1xBSjpLeEM8GAWSY5H454eY= + acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" @@ -4505,6 +4529,16 @@ encoding@^0.1.12: dependencies: iconv-lite "^0.6.2" +enhanced-resolve@^2.2.2: + version "2.3.0" + resolved "http://192.168.26.29:7001/enhanced-resolve/download/enhanced-resolve-2.3.0.tgz#a115c32504b6302e85a76269d7a57ccdd962e359" + integrity sha1-oRXDJQS2MC6Fp2Jp16V8zdli41k= + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.3.0" + object-assign "^4.0.1" + tapable "^0.2.3" + enquirer@^2.3.5, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" @@ -4527,6 +4561,13 @@ err-code@^2.0.2: resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== +errno@^0.1.3: + version "0.1.8" + resolved "http://192.168.26.29:7001/errno/download/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha1-i7Ppx9Rjvkl2/4iPdrSAnrwugR8= + dependencies: + prr "~1.0.1" + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -6915,6 +6956,14 @@ memoize-one@^5.0.0: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q== +memory-fs@^0.3.0: + version "0.3.0" + resolved "http://192.168.26.29:7001/memory-fs/download/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20" + integrity sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA= + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + meow@^8.0.0: version "8.1.2" resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" @@ -7167,7 +7216,7 @@ min-indent@^1.0.0: resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== -minimatch@^3.0.4: +minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -8109,6 +8158,11 @@ protocols@^1.1.0, protocols@^1.4.0: resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.8.tgz#48eea2d8f58d9644a4a32caae5d5db290a075ce8" integrity sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg== +prr@~1.0.1: + version "1.0.1" + resolved "http://192.168.26.29:7001/prr/download/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -8441,7 +8495,7 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.6, readable-stream@~2.3.6: +readable-stream@^2.0.1, readable-stream@^2.0.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -8617,6 +8671,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@2.0.0: + version "2.0.0" + resolved "http://192.168.26.29:7001/resolve-from/download/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" + integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c= + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -9215,6 +9274,11 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" +tapable@^0.2.3: + version "0.2.9" + resolved "http://192.168.26.29:7001/tapable/download/tapable-0.2.9.tgz#af2d8bbc9b04f74ee17af2b4d9048f807acd18a8" + integrity sha1-ry2LvJsE907hevK02QSPgHrNGKg= + tar@^4.4.12: version "4.4.19" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" @@ -9264,6 +9328,19 @@ terminal-link@^2.0.0: ansi-escapes "^4.2.1" supports-hyperlinks "^2.0.0" +tern@^0.24.3: + version "0.24.3" + resolved "http://192.168.26.29:7001/tern/download/tern-0.24.3.tgz#da0d9118490c15af4b1c94553d5f0fafc77a0977" + integrity sha1-2g2RGEkMFa9LHJRVPV8Pr8d6CXc= + dependencies: + acorn "^6.0.0" + acorn-loose "^6.0.0" + acorn-walk "^6.0.0" + enhanced-resolve "^2.2.2" + glob "^7.1.1" + minimatch "^3.0.3" + resolve-from "2.0.0" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"