From 9c390e2926a349e58b224978e4e224707cb4b970 Mon Sep 17 00:00:00 2001 From: Nut He <18328704+hetao92@users.noreply.github.com> Date: Fri, 18 Feb 2022 17:31:44 +0800 Subject: [PATCH] feat: add space create page mod: code review --- app-v2/common.less | 6 + app-v2/components/Breadcrumb/index.less | 3 +- app-v2/components/Breadcrumb/index.tsx | 22 ++- app-v2/components/CodeMirror/index.less | 9 + app-v2/components/CodeMirror/index.tsx | 195 +++++++++++++++++++++ app-v2/components/GQLCodeMirror/index.less | 16 ++ app-v2/components/GQLCodeMirror/index.tsx | 29 +++ app-v2/config/locale/en-US.json | 5 +- app-v2/config/locale/zh-CN.json | 5 +- app-v2/config/rules.ts | 2 +- app-v2/interfaces/schema.ts | 1 + app-v2/pages/Import/TaskCreate/index.less | 2 +- app-v2/pages/Import/TaskCreate/index.tsx | 2 +- app-v2/pages/Import/index.less | 2 +- app-v2/pages/Import/index.tsx | 2 +- app-v2/pages/MainPage/index.less | 5 + app-v2/pages/MainPage/routes.tsx | 6 + app-v2/pages/Schema/SpaceCreate/index.less | 34 ++++ app-v2/pages/Schema/SpaceCreate/index.tsx | 184 +++++++++++++++++++ app-v2/pages/Schema/index.less | 2 +- app-v2/pages/Schema/index.tsx | 5 +- app-v2/stores/schema.ts | 34 +++- app-v2/utils/gql.ts | 23 +-- app/components/CodeMirror.tsx | 12 +- config/webpack.base.ts | 3 +- 25 files changed, 555 insertions(+), 54 deletions(-) create mode 100644 app-v2/components/CodeMirror/index.less create mode 100644 app-v2/components/CodeMirror/index.tsx create mode 100644 app-v2/components/GQLCodeMirror/index.less create mode 100644 app-v2/components/GQLCodeMirror/index.tsx create mode 100644 app-v2/pages/Schema/SpaceCreate/index.less create mode 100644 app-v2/pages/Schema/SpaceCreate/index.tsx diff --git a/app-v2/common.less b/app-v2/common.less index db5582fe..4daa0751 100644 --- a/app-v2/common.less +++ b/app-v2/common.less @@ -1,5 +1,6 @@ @gray: #D5DDEB; @lightGray: #F8F8F8; +@containerWidth: 1180px; @font-face { font-family: 'Roboto-Black'; @@ -25,3 +26,8 @@ font-family: 'Roboto-Regular'; src: url(~@appv2/static/fonts/Roboto-Regular.ttf); } + +.center-layout { + width: @containerWidth; + margin: 0 auto; +} diff --git a/app-v2/components/Breadcrumb/index.less b/app-v2/components/Breadcrumb/index.less index 2fc9bba6..a9b0427e 100644 --- a/app-v2/components/Breadcrumb/index.less +++ b/app-v2/components/Breadcrumb/index.less @@ -5,7 +5,8 @@ font-size: 18px; align-items: center; display: flex; - padding-left: 40px; + padding-left: 0; + padding-right: 0; .arrow-icon { width: 23px; height: 23px; diff --git a/app-v2/components/Breadcrumb/index.tsx b/app-v2/components/Breadcrumb/index.tsx index 2c9848fa..44a2f2d3 100644 --- a/app-v2/components/Breadcrumb/index.tsx +++ b/app-v2/components/Breadcrumb/index.tsx @@ -1,4 +1,4 @@ -import { PageHeader } from 'antd'; +import { Breadcrumb, PageHeader } from 'antd'; import React from 'react'; import { Link } from 'react-router-dom'; @@ -9,7 +9,7 @@ interface IProps { path: string; breadcrumbName: string; }[]; - ExtraNode?: JSX.Element; + extraNode?: JSX.Element; } const itemRender = (route, _params, routes, _paths) => { @@ -31,16 +31,24 @@ const itemRender = (route, _params, routes, _paths) => { ); }; -const Breadcrumb: React.FC = (props: IProps) => { - const { routes, ExtraNode } = props; +const NebulaBreadcrumb: React.FC = (props: IProps) => { + const { routes, extraNode } = props; return ( { + return <> + + {extraNode} + ; + }} /> ); }; -export default Breadcrumb; +export default NebulaBreadcrumb; diff --git a/app-v2/components/CodeMirror/index.less b/app-v2/components/CodeMirror/index.less new file mode 100644 index 00000000..e97cce9b --- /dev/null +++ b/app-v2/components/CodeMirror/index.less @@ -0,0 +1,9 @@ +.CodeMirror-wrap pre.CodeMirror-line, +.CodeMirror-wrap pre.CodeMirror-line-like { + word-break: break-all !important; +} + +.CodeMirror { + resize: vertical; + overflow: auto !important; +} diff --git a/app-v2/components/CodeMirror/index.tsx b/app-v2/components/CodeMirror/index.tsx new file mode 100644 index 00000000..33266b98 --- /dev/null +++ b/app-v2/components/CodeMirror/index.tsx @@ -0,0 +1,195 @@ +import CodeMirror from 'codemirror'; +import 'codemirror/addon/comment/comment'; +import 'codemirror/addon/display/autorefresh'; +import 'codemirror/addon/edit/matchbrackets'; +import 'codemirror/addon/hint/show-hint'; +import 'codemirror/addon/hint/show-hint.css'; +import 'codemirror/keymap/sublime'; +import 'codemirror/lib/codemirror.css'; +import 'codemirror/mode/meta'; +import 'codemirror/theme/monokai.css'; +import React from 'react'; + +import { ban, keyWords, maxLineNum, operators } from '@appv2/config/nebulaQL'; + +import './index.less'; + +interface IProps { + options?: object; + value: string; + ref?: any; + width?: string; + height?: string; + onShiftEnter?: () => void; + onChange?: (value: string) => void; + onBlur?: (value: string) => void; + onChangeLine?: () => void; +} + +export default class ReactCodeMirror extends React.PureComponent { + codemirror; + editor; + textarea; + constructor(props) { + super(props); + } + public componentDidMount() { + CodeMirror.defineMode('nebula', () => { + return { + token: stream => { + if (stream.eatSpace()) { + return null; + } + stream.eatWhile(/[\$:\w\u4e00-\u9fa5]/); + const cur = stream.current(); + if (keyWords.some(item => item === cur)) { + return 'keyword'; + } else if (operators.some(item => item === cur)) { + return 'def'; + } else if (ban.some(item => item === cur)) { + return 'error'; + } + stream.next(); + }, + }; + }); + + CodeMirror.registerHelper('hint', 'nebula', cm => { + const cur = cm.getCursor(); + const token = cm.getTokenAt(cur); + const str = token.string; + const start = token.start; + const end = cur.ch; + + if (str === '') { + return; + } + + const list = [...keyWords, ...operators, ...ban].filter(item => { + return item.indexOf(str) === 0; + }); + + if (list.length) { + return { + list, + from: CodeMirror.Pos(cur.line, start), + to: CodeMirror.Pos(cur.line, end), + }; + } + }); + this.renderCodeMirror(); + } + renderCodeMirror() { + // parameters of the combined + const options = { + tabSize: 2, + fontSize: '14px', + autoCloseBrackets: true, + matchBrackets: true, + showCursorWhenSelecting: true, + lineWrapping: true, + // show number of rows + lineNumbers: true, + fullScreen: true, + mode: 'nebula', + ...this.props.options, + }; + this.editor = CodeMirror.fromTextArea(this.textarea, options); + // Getting CodeMirror is used to get some of these constants + this.codemirror = CodeMirror; + // event + this.editor.on('change', this.codemirrorValueChange); + this.editor.on('keydown', this.keydown); + this.editor.on('blur', this.blur); + const { value, width, height } = this.props; + this.editor.setValue(value || ''); + if (width || height) { + // set size + this.editor.setSize(width, height); + } + } + + blur = instance => { + if (this.props.onBlur) { + this.props.onBlur(instance.doc.getValue()); + } + }; + + keydown = (_, change) => { + if (change.shiftKey === true && change.keyCode === 13) { + if (this.props.onShiftEnter) { + this.props.onShiftEnter(); + } + change.preventDefault(); + } + }; + + codemirrorValueChange = (doc, change) => { + if (change.origin !== 'setValue') { + if (this.props.onChange) { + this.props.onChange(doc.getValue()); + } + } + if (change.origin === '+input') { + CodeMirror.commands.autocomplete(this.editor, null, { + completeSingle: false, + }); + } + if ( + this.props.onChangeLine && + (change.origin === '+delete' || change.origin === '+input') + ) { + this.props.onChangeLine(); + } + }; + + async UNSAFE_componentWillReceiveProps(nextProps) { + const { options, value } = nextProps; + await this.setOptions(options); + if (value !== this.editor.getValue()) { + this.editor.setValue(value || ''); + let line; + if (this.editor.lineCount() > maxLineNum) { + line = maxLineNum; + } else if (this.editor.lineCount() < 5) { + line = 5; + } else { + line = this.editor.lineCount(); + } + this.editor.setSize(undefined, line * 24 + 10 + 'px'); + } + } + + async setOptions(options) { + if (typeof options === 'object') { + const mode = CodeMirror.findModeByName(options.mode); + if (mode && mode.mode) { + await import(`codemirror/mode/${mode.mode}/${mode.mode}.js`); + } + if (mode) { + options.mode = mode.mime; + } + Object.keys(options).forEach(name => { + if (options[name] && JSON.stringify(options[name])) { + this.editor.setOption(name, options[name]); + } + }); + } + } + + componentWillUnmount() { + if (this.editor) { + this.editor.toTextArea(); + } + } + + render() { + return ( +