From 77928102fd805aa8d529d7f935fc27f295f3af42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8B=E7=BE=8A?= Date: Mon, 24 Feb 2020 21:30:20 +0800 Subject: [PATCH 1/9] =?UTF-8?q?feat:=E9=A1=B9=E7=9B=AE=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor-framework/.eslintignore | 6 +++ packages/editor-framework/.eslintrc.js | 5 +++ packages/editor-framework/.gitignore | 22 +++++++++++ packages/editor-framework/.prettierrc | 6 +++ packages/editor-framework/README copy.md | 11 ++++++ packages/editor-framework/build.json | 9 +++++ packages/editor-framework/demo/usage.md | 26 +++++++++++++ packages/editor-framework/es/index.d.ts | 7 ++++ packages/editor-framework/es/index.js | 13 +++++++ packages/editor-framework/es/index.scss | 4 ++ packages/editor-framework/es/style.js | 1 + packages/editor-framework/package.json | 49 ++++++++++++++++++++++++ packages/editor-framework/src/index.scss | 4 ++ packages/editor-framework/src/index.tsx | 15 ++++++++ packages/editor-framework/tsconfig.json | 21 ++++++++++ 15 files changed, 199 insertions(+) create mode 100644 packages/editor-framework/.eslintignore create mode 100644 packages/editor-framework/.eslintrc.js create mode 100644 packages/editor-framework/.gitignore create mode 100644 packages/editor-framework/.prettierrc create mode 100644 packages/editor-framework/README copy.md create mode 100644 packages/editor-framework/build.json create mode 100644 packages/editor-framework/demo/usage.md create mode 100644 packages/editor-framework/es/index.d.ts create mode 100644 packages/editor-framework/es/index.js create mode 100644 packages/editor-framework/es/index.scss create mode 100644 packages/editor-framework/es/style.js create mode 100644 packages/editor-framework/package.json create mode 100644 packages/editor-framework/src/index.scss create mode 100644 packages/editor-framework/src/index.tsx create mode 100644 packages/editor-framework/tsconfig.json diff --git a/packages/editor-framework/.eslintignore b/packages/editor-framework/.eslintignore new file mode 100644 index 000000000..f6ee039b9 --- /dev/null +++ b/packages/editor-framework/.eslintignore @@ -0,0 +1,6 @@ +# 忽略目录 +build/ +node_modules/ +**/*-min.js +**/*.min.js +coverage/ diff --git a/packages/editor-framework/.eslintrc.js b/packages/editor-framework/.eslintrc.js new file mode 100644 index 000000000..ebda54735 --- /dev/null +++ b/packages/editor-framework/.eslintrc.js @@ -0,0 +1,5 @@ +const { eslint, deepmerge } = require('@ice/spec'); + +module.exports = deepmerge(eslint, { + rules: {}, +}); diff --git a/packages/editor-framework/.gitignore b/packages/editor-framework/.gitignore new file mode 100644 index 000000000..84d2d58ff --- /dev/null +++ b/packages/editor-framework/.gitignore @@ -0,0 +1,22 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +node_modules/ + +# production +build/ +dist/ +tmp/ +lib/ + +# misc +.idea/ +.happypack +.DS_Store +*.swp +*.dia~ + +npm-debug.log* +yarn-debug.log* +yarn-error.log* +CHANGELOG.md diff --git a/packages/editor-framework/.prettierrc b/packages/editor-framework/.prettierrc new file mode 100644 index 000000000..8748c5ed3 --- /dev/null +++ b/packages/editor-framework/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": true, + "printWidth": 120, + "trailingComma": "all" +} diff --git a/packages/editor-framework/README copy.md b/packages/editor-framework/README copy.md new file mode 100644 index 000000000..6e9e79f5b --- /dev/null +++ b/packages/editor-framework/README copy.md @@ -0,0 +1,11 @@ +# demo component + +t-s-demo + +intro component + +## API + +| 参数名 | 说明 | 必填 | 类型 | 默认值 | 备注 | +| ------ | ---- | ---- | ---- | ------ | ---- | +| | | | | | | diff --git a/packages/editor-framework/build.json b/packages/editor-framework/build.json new file mode 100644 index 000000000..77627cdf9 --- /dev/null +++ b/packages/editor-framework/build.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + "build-plugin-component", + "build-plugin-fusion", + ["build-plugin-moment-locales", { + "locales": ["zh-cn"] + }] + ] +} \ No newline at end of file diff --git a/packages/editor-framework/demo/usage.md b/packages/editor-framework/demo/usage.md new file mode 100644 index 000000000..819d1ac15 --- /dev/null +++ b/packages/editor-framework/demo/usage.md @@ -0,0 +1,26 @@ +--- +title: Simple Usage +order: 1 +--- + +本 Demo 演示一行文字的用法。 + +````jsx +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; +import Demo from 'editor-framework'; + +class App extends Component { + render() { + return ( +
+ +
+ ); + } +} + +ReactDOM.render(( + +), mountNode); +```` diff --git a/packages/editor-framework/es/index.d.ts b/packages/editor-framework/es/index.d.ts new file mode 100644 index 000000000..ceb2fe364 --- /dev/null +++ b/packages/editor-framework/es/index.d.ts @@ -0,0 +1,7 @@ +/// +declare function TSDemo(props: any): JSX.Element; +declare namespace TSDemo { + var propTypes: {}; + var defaultProps: {}; +} +export default TSDemo; diff --git a/packages/editor-framework/es/index.js b/packages/editor-framework/es/index.js new file mode 100644 index 000000000..9ea3ef8aa --- /dev/null +++ b/packages/editor-framework/es/index.js @@ -0,0 +1,13 @@ +import _extends from "@babel/runtime/helpers/extends"; +import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/objectWithoutPropertiesLoose"; +import * as React from 'react'; +export default function TSDemo(props) { + var type = props.type, + others = _objectWithoutPropertiesLoose(props, ["type"]); + + return React.createElement("div", _extends({ + className: "TSDemo" + }, others), "Hello TSDemo"); +} +TSDemo.propTypes = {}; +TSDemo.defaultProps = {}; \ No newline at end of file diff --git a/packages/editor-framework/es/index.scss b/packages/editor-framework/es/index.scss new file mode 100644 index 000000000..3a0f72cfb --- /dev/null +++ b/packages/editor-framework/es/index.scss @@ -0,0 +1,4 @@ +/* write style here */ +.TSDemo { + +} diff --git a/packages/editor-framework/es/style.js b/packages/editor-framework/es/style.js new file mode 100644 index 000000000..bf3c23acb --- /dev/null +++ b/packages/editor-framework/es/style.js @@ -0,0 +1 @@ +import './index.scss'; \ No newline at end of file diff --git a/packages/editor-framework/package.json b/packages/editor-framework/package.json new file mode 100644 index 000000000..e76f5e372 --- /dev/null +++ b/packages/editor-framework/package.json @@ -0,0 +1,49 @@ +{ + "name": "editor-framework", + "version": "0.0.1", + "description": "alibaba lowcode editor framework", + "files": [ + "demo/", + "es/", + "lib/", + "build/" + ], + "main": "lib/index.js", + "module": "es/index.js", + "stylePath": "style.js", + "scripts": { + "start": "build-scripts start", + "build": "build-scripts build", + "prepublishOnly": "npm run prettier && npm run build", + "lint": "eslint --cache --ext .js,.jsx ./", + "prettier": "prettier --write \"./src/**/*.{ts,tsx,js,jsx,ejs,less,css,scss,json}\" " + }, + "keywords": [ + "lowcode", + "editor" + ], + "author": "xiayang.xy", + "dependencies": { + "prop-types": "^15.5.8" + }, + "devDependencies": { + "@alib/build-scripts": "^0.1.3", + "@alifd/next": "1.x", + "@ice/spec": "^0.1.1", + "@types/react": "^16.9.13", + "@types/react-dom": "^16.9.4", + "build-plugin-component": "^0.2.0", + "build-plugin-fusion": "^0.1.0", + "build-plugin-moment-locales": "^0.1.0", + "eslint": "^6.0.1", + "prettier": "^1.19.1", + "react": "^16.3.0", + "react-dom": "^16.3.0" + }, + "peerDependencies": { + "react": "^16.3.0", + "@alifd/next": "1.x" + }, + "license": "MIT", + "homepage": "https://unpkg.com/editor-framework@0.0.1/build/index.html" +} diff --git a/packages/editor-framework/src/index.scss b/packages/editor-framework/src/index.scss new file mode 100644 index 000000000..3a0f72cfb --- /dev/null +++ b/packages/editor-framework/src/index.scss @@ -0,0 +1,4 @@ +/* write style here */ +.TSDemo { + +} diff --git a/packages/editor-framework/src/index.tsx b/packages/editor-framework/src/index.tsx new file mode 100644 index 000000000..9752a43b7 --- /dev/null +++ b/packages/editor-framework/src/index.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; + +export default function TSDemo(props) { + const { type, ...others } = props; + + return ( +
Hello TSDemo
+ ); +} + +TSDemo.propTypes = { +}; + +TSDemo.defaultProps = { +}; diff --git a/packages/editor-framework/tsconfig.json b/packages/editor-framework/tsconfig.json new file mode 100644 index 000000000..a511d68ba --- /dev/null +++ b/packages/editor-framework/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compileOnSave": false, + "buildOnSave": false, + "compilerOptions": { + "outDir": "build", + "module": "esnext", + "target": "es6", + "jsx": "react", + "moduleResolution": "node", + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "noUnusedLocals": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": true, + "skipLibCheck": true + }, + "include": ["src/*.ts", "src/*.tsx"], + "exclude": ["node_modules", "build", "public"] +} From a198922fb3747876d7374678ce4dd28bf2f6e44c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8B=E7=BE=8A?= Date: Tue, 25 Feb 2020 21:50:30 +0800 Subject: [PATCH 2/9] daily tag --- packages/editor-framework/package.json | 5 ++ packages/editor-framework/src/context.ts | 3 + packages/editor-framework/src/editor.ts | 62 +++++++++++++++++ packages/editor-framework/src/index.scss | 4 -- packages/editor-framework/src/index.ts | 0 packages/editor-framework/src/index.tsx | 15 ---- packages/editor-framework/src/plugin.ts | 87 ++++++++++++++++++++++++ packages/editor-framework/src/utils.ts | 75 ++++++++++++++++++++ 8 files changed, 232 insertions(+), 19 deletions(-) create mode 100644 packages/editor-framework/src/context.ts create mode 100644 packages/editor-framework/src/editor.ts delete mode 100644 packages/editor-framework/src/index.scss create mode 100644 packages/editor-framework/src/index.ts delete mode 100644 packages/editor-framework/src/index.tsx create mode 100644 packages/editor-framework/src/plugin.ts create mode 100644 packages/editor-framework/src/utils.ts diff --git a/packages/editor-framework/package.json b/packages/editor-framework/package.json index e76f5e372..8866b4a77 100644 --- a/packages/editor-framework/package.json +++ b/packages/editor-framework/package.json @@ -24,12 +24,17 @@ ], "author": "xiayang.xy", "dependencies": { + "debug": "^4.1.1", + "events": "^3.1.0", + "intl-messageformat": "^7.8.4", + "lodash": "^4.17.15", "prop-types": "^15.5.8" }, "devDependencies": { "@alib/build-scripts": "^0.1.3", "@alifd/next": "1.x", "@ice/spec": "^0.1.1", + "@types/lodash": "^4.14.149", "@types/react": "^16.9.13", "@types/react-dom": "^16.9.4", "build-plugin-component": "^0.2.0", diff --git a/packages/editor-framework/src/context.ts b/packages/editor-framework/src/context.ts new file mode 100644 index 000000000..78d3ce177 --- /dev/null +++ b/packages/editor-framework/src/context.ts @@ -0,0 +1,3 @@ +import { createContext } from 'react'; +const context = createContext({}); +export default context; diff --git a/packages/editor-framework/src/editor.ts b/packages/editor-framework/src/editor.ts new file mode 100644 index 000000000..daeeb2182 --- /dev/null +++ b/packages/editor-framework/src/editor.ts @@ -0,0 +1,62 @@ +import EventEmitter from 'events'; +import Debug from 'debug'; +let instance = null; + +const debug = Debug('editor'); +EventEmitter.defaultMaxListeners = 100; + +export interface editor { + +}; + +export class Editor extends EventEmitter { + static getInstance = () => { + if (!instance) { + instance = new Editor(); + } + return instance; + }; + + constructor(config) { + super(); + instance = this; + Object.assign(this, config); + } + + init() { + + } + + destroy() { + + } + + get(key:string):any { + return this[key]; + } + + set(key, val) { + if (typeof key === 'string') { + this[key] = val; + } else if (typeof key === 'object') { + Object.keys(key).forEach(item => { + this[item] = key[item]; + }); + } + } + + batchOn(events, lisenter) { + if (!Array.isArray(events)) return; + events.forEach(event => this.on(event, lisenter)); + } + + batchOnce(events, lisenter) { + if (!Array.isArray(events)) return; + events.forEach(event => this.once(event, lisenter)); + } + + batchOff(events, lisenter) { + if (!Array.isArray(events)) return; + events.forEach(event => this.off(event, lisenter)); + } +} diff --git a/packages/editor-framework/src/index.scss b/packages/editor-framework/src/index.scss deleted file mode 100644 index 3a0f72cfb..000000000 --- a/packages/editor-framework/src/index.scss +++ /dev/null @@ -1,4 +0,0 @@ -/* write style here */ -.TSDemo { - -} diff --git a/packages/editor-framework/src/index.ts b/packages/editor-framework/src/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-framework/src/index.tsx b/packages/editor-framework/src/index.tsx deleted file mode 100644 index 9752a43b7..000000000 --- a/packages/editor-framework/src/index.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import * as React from 'react'; - -export default function TSDemo(props) { - const { type, ...others } = props; - - return ( -
Hello TSDemo
- ); -} - -TSDemo.propTypes = { -}; - -TSDemo.defaultProps = { -}; diff --git a/packages/editor-framework/src/plugin.ts b/packages/editor-framework/src/plugin.ts new file mode 100644 index 000000000..e42aa73cc --- /dev/null +++ b/packages/editor-framework/src/plugin.ts @@ -0,0 +1,87 @@ +import { PureComponent } from 'react'; + +import EditorContext from './context'; +import { isEmpty, generateI18n, goldlog } from './utils'; + +export interface pluginProps { + config: object, + editor: object, + locale: string, + messages: object +} + + + +export class Plugin extends PureComponent { + static displayName = 'lowcode-editor-plugin'; + static defaultProps = { + config: {} + }; + static contextType = EditorContext; + constructor(props, context) { + super(props, context); + if (isEmpty(props.config) || !props.config.addonKey) { + console.warn('luna addon has wrong config'); + return; + } + + + const { locale, messages, editor } = props; + // 注册插件 + this.editor = editor; + this.i18n = generateI18n(locale, messages); + this.pluginKey = props.config.pluginKey; + editor.plugins = editor.plugins || {}; + editor.plugins[this.pluginKey] = this; + } + + async componentWillUnmount() { + // 销毁插件 + if (this.editor && this.editor.plugins) { + delete this.editor.plugins[this.pluginKey]; + } + } + + open = () => { + return true; + }; + + close = () => { + return true; + }; + + goldlog = (goKey:string, params:any) => { + const { pluginKey, config = {} } = this.props.config || {}; + goldlog( + goKey, + { + pluginKey, + package: config.package, + version: config.version, + ...this.editor.logParams, + ...params + }, + 'addon' + ); + }; + + get utils() { + return this.editor.utils; + } + + get constants() { + return this.editor.constants; + } + + get history() { + return this.editor.history; + } + + get location() { + return this.editor.location; + } + + render() { + return null; + } +} diff --git a/packages/editor-framework/src/utils.ts b/packages/editor-framework/src/utils.ts new file mode 100644 index 000000000..5cc738bca --- /dev/null +++ b/packages/editor-framework/src/utils.ts @@ -0,0 +1,75 @@ + +import IntlMessageFormat from 'intl-messageformat'; +import _isEmpty from 'lodash/isEmpty'; + +export const isEmpty = _isEmpty; + +/** + * 用于构造国际化字符串处理函数 + * @param {*} locale 国际化标识,例如 zh-CN、en-US + * @param {*} messages 国际化语言包 + */ +export function generateI18n(locale = 'zh-CN', messages = {}) { + return (key, values = {}) => { + if (!messages || !messages[key]) return ''; + const formater = new IntlMessageFormat(messages[key], locale); + return formater.format(values); + }; +} + +/** + * 序列化参数 + * @param {*} obj 参数 + */ +export function serializeParams(obj:object):string { + if (typeof obj !== 'object') return ''; + + const res:Array = []; + Object.entries(obj).forEach(([key, val]) => { + if (val === null || val === undefined || val === '') return; + if (typeof val === 'object') { + res.push(`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`); + } else { + res.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`); + } + }); + return res.join('&'); +} + +/** + * 黄金令箭埋点 + * @param {String} gmKey 为黄金令箭业务类型 + * @param {Object} params 参数 + * @param {String} logKey 属性串 + */ +export function goldlog(gmKey, params = {}, logKey = 'other') { + const sendIDEMessage = window.sendIDEMessage || window.parent.sendIDEMessage; + const goKey = serializeParams({ + sdkVersion: pkg.version, + env: getEnv(), + ...params + }); + if (sendIDEMessage) { + sendIDEMessage({ + action: 'goldlog', + data: { + logKey: `/iceluna.core.${logKey}`, + gmKey, + goKey + } + }); + } + window.goldlog && window.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST'); +} + +/** + * 获取当前编辑器环境 + */ +export function getEnv() { + const userAgent = navigator.userAgent; + const isVscode = /Electron\//.test(userAgent); + if (isVscode) return ENV.VSCODE; + const isTheia = window.is_theia === true; + if (isTheia) return ENV.WEBIDE; + return ENV.WEB; +} \ No newline at end of file From a39d82e6527ea67d8bebb5f4f2fbc0dcd451194d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8B=E7=BE=8A?= Date: Wed, 26 Feb 2020 09:08:10 +0800 Subject: [PATCH 3/9] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0skeleton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/editor-framework/README.md | 2 +- packages/editor-framework/package.json | 4 +- packages/editor-skeleton/.editorconfig | 12 +++ packages/editor-skeleton/.eslintignore | 11 +++ packages/editor-skeleton/.eslintrc.js | 7 ++ packages/editor-skeleton/.gitignore | 20 +++++ packages/editor-skeleton/.stylelintignore | 7 ++ packages/editor-skeleton/.stylelintrc.js | 3 + packages/editor-skeleton/README.md | 1 + packages/editor-skeleton/abc.json | 4 + packages/editor-skeleton/ice.config.js | 30 +++++++ packages/editor-skeleton/jsconfig.json | 9 +++ packages/editor-skeleton/package.json | 47 +++++++++++ packages/editor-skeleton/public/favicon.png | Bin 0 -> 2719 bytes packages/editor-skeleton/public/index.html | 13 ++++ .../src/components/Greeting/index.tsx | 15 ++++ .../src/components/Guide/index.module.scss | 11 +++ .../src/components/Guide/index.tsx | 71 +++++++++++++++++ packages/editor-skeleton/src/config/menu.ts | 5 ++ packages/editor-skeleton/src/config/routes.ts | 21 +++++ packages/editor-skeleton/src/global.scss | 6 ++ packages/editor-skeleton/src/index.tsx | 13 ++++ .../src/layouts/BasicLayout/index.tsx | 9 +++ .../src/pages/Dashboard/index.tsx | 12 +++ packages/editor-skeleton/src/router.tsx | 73 ++++++++++++++++++ packages/editor-skeleton/tests/index.js | 1 + packages/editor-skeleton/tsconfig.json | 31 ++++++++ 27 files changed, 435 insertions(+), 3 deletions(-) create mode 100644 packages/editor-skeleton/.editorconfig create mode 100644 packages/editor-skeleton/.eslintignore create mode 100644 packages/editor-skeleton/.eslintrc.js create mode 100644 packages/editor-skeleton/.gitignore create mode 100644 packages/editor-skeleton/.stylelintignore create mode 100644 packages/editor-skeleton/.stylelintrc.js create mode 100644 packages/editor-skeleton/README.md create mode 100644 packages/editor-skeleton/abc.json create mode 100644 packages/editor-skeleton/ice.config.js create mode 100644 packages/editor-skeleton/jsconfig.json create mode 100644 packages/editor-skeleton/package.json create mode 100644 packages/editor-skeleton/public/favicon.png create mode 100644 packages/editor-skeleton/public/index.html create mode 100644 packages/editor-skeleton/src/components/Greeting/index.tsx create mode 100644 packages/editor-skeleton/src/components/Guide/index.module.scss create mode 100644 packages/editor-skeleton/src/components/Guide/index.tsx create mode 100644 packages/editor-skeleton/src/config/menu.ts create mode 100644 packages/editor-skeleton/src/config/routes.ts create mode 100644 packages/editor-skeleton/src/global.scss create mode 100644 packages/editor-skeleton/src/index.tsx create mode 100644 packages/editor-skeleton/src/layouts/BasicLayout/index.tsx create mode 100644 packages/editor-skeleton/src/pages/Dashboard/index.tsx create mode 100644 packages/editor-skeleton/src/router.tsx create mode 100644 packages/editor-skeleton/tests/index.js create mode 100644 packages/editor-skeleton/tsconfig.json diff --git a/packages/editor-framework/README.md b/packages/editor-framework/README.md index eb99c606a..8a6fb13f0 100644 --- a/packages/editor-framework/README.md +++ b/packages/editor-framework/README.md @@ -1 +1 @@ -编辑器框架 +## todo diff --git a/packages/editor-framework/package.json b/packages/editor-framework/package.json index 8866b4a77..ebcbe65db 100644 --- a/packages/editor-framework/package.json +++ b/packages/editor-framework/package.json @@ -1,7 +1,7 @@ { - "name": "editor-framework", + "name": "@ali/lowcode-engine-editor", "version": "0.0.1", - "description": "alibaba lowcode editor framework", + "description": "alibaba lowcode editor core", "files": [ "demo/", "es/", diff --git a/packages/editor-skeleton/.editorconfig b/packages/editor-skeleton/.editorconfig new file mode 100644 index 000000000..5760be583 --- /dev/null +++ b/packages/editor-skeleton/.editorconfig @@ -0,0 +1,12 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/packages/editor-skeleton/.eslintignore b/packages/editor-skeleton/.eslintignore new file mode 100644 index 000000000..3b437e614 --- /dev/null +++ b/packages/editor-skeleton/.eslintignore @@ -0,0 +1,11 @@ +# 忽略目录 +build/ +tests/ +demo/ + +# node 覆盖率文件 +coverage/ + +# 忽略文件 +**/*-min.js +**/*.min.js diff --git a/packages/editor-skeleton/.eslintrc.js b/packages/editor-skeleton/.eslintrc.js new file mode 100644 index 000000000..18ae6baa7 --- /dev/null +++ b/packages/editor-skeleton/.eslintrc.js @@ -0,0 +1,7 @@ +const { eslint, deepmerge } = require('@ice/spec'); + +module.exports = deepmerge(eslint, { + rules: { + "global-require": 0, + }, +}); diff --git a/packages/editor-skeleton/.gitignore b/packages/editor-skeleton/.gitignore new file mode 100644 index 000000000..c590da5b0 --- /dev/null +++ b/packages/editor-skeleton/.gitignore @@ -0,0 +1,20 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +/node_modules + +# production +/build +/dist + +# misc +.idea/ +.happypack +.DS_Store + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# ignore d.ts auto generated by css-modules-typescript-loader +*.module.scss.d.ts \ No newline at end of file diff --git a/packages/editor-skeleton/.stylelintignore b/packages/editor-skeleton/.stylelintignore new file mode 100644 index 000000000..82af6f60d --- /dev/null +++ b/packages/editor-skeleton/.stylelintignore @@ -0,0 +1,7 @@ +# 忽略目录 +build/ +tests/ +demo/ + +# node 覆盖率文件 +coverage/ diff --git a/packages/editor-skeleton/.stylelintrc.js b/packages/editor-skeleton/.stylelintrc.js new file mode 100644 index 000000000..eeb605b33 --- /dev/null +++ b/packages/editor-skeleton/.stylelintrc.js @@ -0,0 +1,3 @@ +const { stylelint } = require('@ice/spec'); + +module.exports = stylelint; diff --git a/packages/editor-skeleton/README.md b/packages/editor-skeleton/README.md new file mode 100644 index 000000000..8a6fb13f0 --- /dev/null +++ b/packages/editor-skeleton/README.md @@ -0,0 +1 @@ +## todo diff --git a/packages/editor-skeleton/abc.json b/packages/editor-skeleton/abc.json new file mode 100644 index 000000000..dce1f92ed --- /dev/null +++ b/packages/editor-skeleton/abc.json @@ -0,0 +1,4 @@ +{ + "type": "ice-scripts", + "builder": "@ali/builder-ice-scripts" +} diff --git a/packages/editor-skeleton/ice.config.js b/packages/editor-skeleton/ice.config.js new file mode 100644 index 000000000..f905f88e8 --- /dev/null +++ b/packages/editor-skeleton/ice.config.js @@ -0,0 +1,30 @@ +const path = require('path'); + +module.exports = { + entry: 'src/index.tsx', + publicPath: './', + alias: { + '@': path.resolve(__dirname, './src'), + }, + plugins: [ + ['ice-plugin-fusion', { + themePackage: '@icedesign/theme', + }], + ['ice-plugin-moment-locales', { + locales: ['zh-cn'], + }], + ], + chainWebpack: (config) => { + // 修改对应 css module的 loader,默认修改 scss-module 同理可以修改 css-module 和 less-module 规则 + ['scss-module'].forEach((rule) => { + if (config.module.rules.get(rule)) { + config.module.rule(rule).use('ts-css-module-loader') + .loader(require.resolve('css-modules-typescript-loader')) + .options({ modules: true, sass: true }); + // 指定应用loader的位置 + config.module.rule(rule).use('ts-css-module-loader').before('css-loader'); + } + }); + }, +}; + diff --git a/packages/editor-skeleton/jsconfig.json b/packages/editor-skeleton/jsconfig.json new file mode 100644 index 000000000..9e0f3c03d --- /dev/null +++ b/packages/editor-skeleton/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "jsx": "react", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json new file mode 100644 index 000000000..7ada377d0 --- /dev/null +++ b/packages/editor-skeleton/package.json @@ -0,0 +1,47 @@ +{ + "name": "@ali/lowcode-engine-skeleton", + "version": "0.0.1", + "description": "alibaba lowcode editor skeleton", + "author": "xiayang.xy", + "dependencies": { + "@alifd/next": "^1.x", + "@icedesign/theme": "^1.x", + "@types/react": "^16.8.3", + "@types/react-dom": "^16.8.2", + "moment": "^2.23.0", + "prop-types": "^15.5.8", + "react": "^16.4.1", + "react-dom": "^16.4.1", + "react-router-dom": "^5.0.1" + }, + "devDependencies": { + "@ice/spec": "^0.1.1", + "css-modules-typescript-loader": "^2.0.4", + "eslint": "^6.0.1", + "ice-plugin-fusion": "^0.1.4", + "ice-plugin-moment-locales": "^0.1.0", + "ice-scripts": "^2.0.0", + "stylelint": "^10.1.0" + }, + "scripts": { + "start": "ice-scripts dev", + "build": "ice-scripts build", + "lint": "npm run eslint && npm run stylelint", + "eslint": "eslint --cache --ext .js,.jsx ./", + "stylelint": "stylelint ./**/*.scss" + }, + "engines": { + "node": ">=8.0.0" + }, + "iceworks": { + "type": "react", + "adapter": "adapter-react-v3" + }, + "ideMode": { + "name": "ice-react" + }, + "repository": { + "type": "git", + "url": "https://github.com/ice-lab/react-materials/tree/master/scaffolds/ice-ts" + } +} diff --git a/packages/editor-skeleton/public/favicon.png b/packages/editor-skeleton/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..a2605c57e9d85aabd689ff5c565286dee148ab6d GIT binary patch literal 2719 zcmbVO`8yMiA9svQv58DRxk@YN91}_`b4{U4xk7!C`zZN1)+bw`g+vn_YM5h6q=V&5 zY&mjF?i^!|S(r95*X&b&!uR`np7;CpJg?XDeqPV>{^^x+*4bW0T3K2|L_`MVVB`9m z^Z$&LFJVfVn*0@o`Uz6v;qRJkbcT zDz0Q12lTZ%`ghogFZaE+CzC-?b?9FC>?D|Pkd}eIbR7dOXL}DpS5J}aua3Bq7}H>3 z8Og;>eXE)t$2j*0A8+@``?d@C5+r!O_l|zaGtfJqEVzm*-TcP@xe{qr(<>BvWYfnOA_I+w&Y*YLR6D?M(%fv^^ylDNyqy>P1;<>wK^T+^*ewX1F*_vqxLT{VuTwPl_xOnH+!=f&y+_wjGs5N)( zIwZ0G zVGDjn#KgnB&u8q{TCQ(JV{CoGszvgBKP2U=F>)r@Dd4e=_(ruDP;4@M8F1kb3KIcNh8el;^6SjxPb7UgHpr7VfQ{=QKLXR9T}an41j9LX(*&x z-s#Okyn>i7`hll*yEa0SH(77uC0SJod(1wZCZLG$IX8 zgg3YePH-pHznZz=(oROkAeRf08LlBU$+*P0OW%>UC7k&8KMHpwmfDk`%76o_!Ai5Q zv)lzh<8xj&BbK%Y*OQ66ME|)KPtGM~9a&2KcW&o<8==OD`^n_3dOjy-R&Dl$5-+l* zJk2a{$R7I*qYCI3d|S*C82Dj`%fgc&G38RV+WC_$Yb{Uz={03hE-uD{=}iJUvPZc@ zt@}G;<>y$*@SltoJE~focFsZ_%Vbscb&wTfMg7u*ewX*sdAFmh*0k%&US4YBUcE7I zS*M!n8G80JPMI?e(Kh;8I!0u|uubOc~y^z<%yrpt0`9F!ItogT=I31$ZGr>Mw-+qHLv=|9sm zqSffY$EJ3d$F3s{wZ^iI$ORCwgCovV*z7IJR_1c20|(Pu``hy5BR9x; zj`h)HANSU`uiHrH1al^C++E+4k-F#40F8fOzDsC(0Xs_TfF_ULf!|_ydeL)|a1Jro z`}oH|*+3cJgt{U(L5hV1`U2z}RHY)-m_|6j3|=SDC%ammwfd@xib@hygHkVx9b`lO z?$3^18)@?D*+AOx0ZjHT`-U6{Gw5FzvfmkN3 z>iM1+Vy@*`o~eUlNbT~YRw;AfVZk*Rzhw{rfS>9?r{1t{Jz0+mb{haKPbYZv5|eNn z{VNt9&hCNNqdZGqsVPQ278pp2rceA9wQu#Hu@5G@9)y1mxhlAJgx^x41b|O)>(hC6 zxd+|P4EGtQlBdYei{4G+%Yjm*!k3gG4k^VNrm%6MB+4qxbr8fSZ)yPg8T}_ZJT+IP zPp>P*QW*+sNU72cez@>5*Y{~{N`ykK-2%RGGE`#c5`~cR(bxhgHm7@CaT!_8+Mjy; zHnG=2&MHd)7E?Cz8+Avg7#vT&{UEqr*A$6pnRzmfOtWn~ryvDmrNp@=;|j;Q`*Pf~ z0wA-DHO_2Rx{kE?x=eA}-pcZLRF1s-8H3LN$F?iILXGiax~+Tq(*AT!Z$j3GqT~QM zDJ`F4Bhc|j5w71Oqzj^ZTcN0e;k$t-6jDX82_cvM&yKe5hu z{TF*|*%zp_PJCQ=e=%NMFhSpL9&+)wS_~GOdp1WOv0%r%V44FAam3hgp7A%Eqw9j7 zIXBU3EgbRndo#+!1Zz8t$)d)OHLuyV&l1PAn?smen<&|-$OZu2@8QTn+MFr)m3y5- zfPE&Ulh$|uf)x((@(gCxmst)n0lNsHVHV1vQ#pvcm1a`CF zS@fp^M4vE;eKx|G*CXdsmp&Wt1mx%QJo>M(<&}@+-#Hn(j8=1Slj$Qe+2o-f9!SL> zEHKc%kW6BqCu-JR%k3tZRt?x>Bi-k>>pFEKlwy7na65EDm3*q_CZ5vQlsF&RSHHAk zHG23^LvvqA)%5Kw(48PmI=s$K?gnb-%=m$(j$Z$1A5lTUkx;d$vpI=C9~KSm?z_~c z#SXvKTSJ~$>E(6?fHXpHU_&wdoa3=(dgo@B&oO~(nr*w#-l#{yhr6ZiG zDK@Pu;rBB-zUnG5K`1`LFFgGPx93J zo=uIxw8w{%Ah89CE zG%Z{%x-c=Q%W~)VcuxU+8@pZ@!$84eF)sG~iycG%+E-hae$E0JnM{rKv6SObgAvd= zaAXfwR)*@t?uUq zrv8D1TZTqxkCAXZq(WAY7Ogs8eUtXYJ4IS4%)&D|9^+Qd3!h**T4=;BlDsFb@~YaS zs8Ks~*8$jS{|`e^1EWtdZ4QHH1}etRcJfqc7dId05z9J`2!%ttHP;`wI>YxHjW8Z1 ztxy7L7%x$L(!+>)q|FupVfw;o&5+IA4Oi_6Kadib+iKTi>Ozfzo9y&%QVVV literal 0 HcmV?d00001 diff --git a/packages/editor-skeleton/public/index.html b/packages/editor-skeleton/public/index.html new file mode 100644 index 000000000..5c0692966 --- /dev/null +++ b/packages/editor-skeleton/public/index.html @@ -0,0 +1,13 @@ + + + + + + + ICE TypeScript Starter + + + +
+ + diff --git a/packages/editor-skeleton/src/components/Greeting/index.tsx b/packages/editor-skeleton/src/components/Greeting/index.tsx new file mode 100644 index 000000000..b9369ed63 --- /dev/null +++ b/packages/editor-skeleton/src/components/Greeting/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +export interface Props { + name: string; +} + +const Greeting = ({ name }: Props) => { + return ( +
+ Hello, {name} +
+ ); +}; + +export default Greeting; diff --git a/packages/editor-skeleton/src/components/Guide/index.module.scss b/packages/editor-skeleton/src/components/Guide/index.module.scss new file mode 100644 index 000000000..eeb4e21aa --- /dev/null +++ b/packages/editor-skeleton/src/components/Guide/index.module.scss @@ -0,0 +1,11 @@ +.item { + height: 34px; + line-height: 34px; +} +.title { + text-align: center; +} +.container { + width: 470px; + margin: 40px auto; +} \ No newline at end of file diff --git a/packages/editor-skeleton/src/components/Guide/index.tsx b/packages/editor-skeleton/src/components/Guide/index.tsx new file mode 100644 index 000000000..9a07b5789 --- /dev/null +++ b/packages/editor-skeleton/src/components/Guide/index.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { Button } from '@alifd/next'; +import styles from './index.module.scss'; + +const Guide = () => { + return ( +
+

使用指南

+
    +
  • + 1. 该模板适用于从 0 到 1 开始搭建项目,内置引导页面,路由和菜单展示。 +
  • +
  • 2. 菜单配置: menuConfig.js。
  • +
  • 3. 路由配置: routerConfig.js。
  • +
  • + 4. 通过 GUI 工具{' '} + + Iceworks + {' '} + 创建页面,会同步的更新菜单和路由配置。 +
  • +
  • + 5. 基于{' '} + + 物料 + {' '} + 生成的页面将会添加在 pages 目录。 +
  • +
  • + 6. 让前端工程变的轻松便捷, + + 下载 iceworks + {' '} + 。 +
  • +
+ +
+ ); +}; + +export default Guide; diff --git a/packages/editor-skeleton/src/config/menu.ts b/packages/editor-skeleton/src/config/menu.ts new file mode 100644 index 000000000..e3c4c9e37 --- /dev/null +++ b/packages/editor-skeleton/src/config/menu.ts @@ -0,0 +1,5 @@ +// 菜单配置 + +const asideMenuConfig = []; + +export { asideMenuConfig }; diff --git a/packages/editor-skeleton/src/config/routes.ts b/packages/editor-skeleton/src/config/routes.ts new file mode 100644 index 000000000..5d63e83cc --- /dev/null +++ b/packages/editor-skeleton/src/config/routes.ts @@ -0,0 +1,21 @@ +import Dashboard from '@/pages/Dashboard'; +import BasicLayout from '@/layouts/BasicLayout'; + +const routerConfig = [ + { + path: '/', + component: BasicLayout, + children: [ + { + path: '/dashboard', + component: Dashboard, + }, + { + path: '/', + redirect: '/dashboard', + } + ], + }, +]; + +export default routerConfig; diff --git a/packages/editor-skeleton/src/global.scss b/packages/editor-skeleton/src/global.scss new file mode 100644 index 000000000..6086b53a6 --- /dev/null +++ b/packages/editor-skeleton/src/global.scss @@ -0,0 +1,6 @@ +// 引入默认全局样式 +@import '@alifd/next/reset.scss'; + +body { + -webkit-font-smoothing: antialiased; +} \ No newline at end of file diff --git a/packages/editor-skeleton/src/index.tsx b/packages/editor-skeleton/src/index.tsx new file mode 100644 index 000000000..ba347d027 --- /dev/null +++ b/packages/editor-skeleton/src/index.tsx @@ -0,0 +1,13 @@ +import ReactDOM from 'react-dom'; + +import './global.scss'; + +import router from './router'; + +const ICE_CONTAINER = document.getElementById('ice-container'); + +if (!ICE_CONTAINER) { + throw new Error('当前页面不存在
节点.'); +} + +ReactDOM.render(router(), ICE_CONTAINER); diff --git a/packages/editor-skeleton/src/layouts/BasicLayout/index.tsx b/packages/editor-skeleton/src/layouts/BasicLayout/index.tsx new file mode 100644 index 000000000..b45f3c44e --- /dev/null +++ b/packages/editor-skeleton/src/layouts/BasicLayout/index.tsx @@ -0,0 +1,9 @@ +import React from 'react'; + +export default function BasicLayout({ children }) { + return ( +
+ {children} +
+ ); +} diff --git a/packages/editor-skeleton/src/pages/Dashboard/index.tsx b/packages/editor-skeleton/src/pages/Dashboard/index.tsx new file mode 100644 index 000000000..2130cc8ce --- /dev/null +++ b/packages/editor-skeleton/src/pages/Dashboard/index.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import Guide from '@/components/Guide'; +import Greeting from '@/components/Greeting'; + +export default function Dashboard() { + return ( +
+ + +
+ ); +} diff --git a/packages/editor-skeleton/src/router.tsx b/packages/editor-skeleton/src/router.tsx new file mode 100644 index 000000000..e260e72c8 --- /dev/null +++ b/packages/editor-skeleton/src/router.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom'; +import path from 'path'; +import routes from '@/config/routes'; + +const RouteItem = (props) => { + const { redirect, path: routePath, component, key } = props; + if (redirect) { + return ( + + ); + } + return ( + + ); +}; + +const router = () => { + return ( + + + {routes.map((route, id) => { + const { component: RouteComponent, children, ...others } = route; + return ( + { + return ( + children ? ( + + + {children.map((routeChild, idx) => { + const { redirect, path: childPath, component } = routeChild; + return RouteItem({ + key: `${id}-${idx}`, + redirect, + path: childPath && path.join(route.path, childPath), + component, + }); + })} + + + ) : ( + <> + { + RouteItem({ + key: id, + ...route, + }) + } + + ) + ); + }} + /> + ); + })} + + + ); +}; + +export default router; diff --git a/packages/editor-skeleton/tests/index.js b/packages/editor-skeleton/tests/index.js new file mode 100644 index 000000000..346e384d2 --- /dev/null +++ b/packages/editor-skeleton/tests/index.js @@ -0,0 +1 @@ +// test file diff --git a/packages/editor-skeleton/tsconfig.json b/packages/editor-skeleton/tsconfig.json new file mode 100644 index 000000000..3f5e62810 --- /dev/null +++ b/packages/editor-skeleton/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compileOnSave": false, + "buildOnSave": false, + "compilerOptions": { + "baseUrl": ".", + "outDir": "build", + "module": "esnext", + "target": "es6", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": false, + "importHelpers": true, + "strictNullChecks": true, + "suppressImplicitAnyIndexErrors": true, + "noUnusedLocals": true, + "skipLibCheck": true, + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src/*"], + "exclude": ["node_modules", "build", "public"] +} From 9e26b961ebcc2d1cde47e362f0d797646ac2c773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8B=E7=BE=8A?= Date: Wed, 4 Mar 2020 20:57:29 +0800 Subject: [PATCH 4/9] daily tag --- packages/editor-framework/demo/usage.md | 2 - packages/editor-framework/es/context.d.ts | 3 + packages/editor-framework/es/context.js | 3 + packages/editor-framework/es/index.d.ts | 7 - packages/editor-framework/es/index.js | 13 - packages/editor-framework/es/index.scss | 4 - packages/editor-framework/es/style.js | 1 - packages/editor-framework/package.json | 3 +- packages/editor-framework/src/editor.ts | 138 +++++- packages/editor-framework/src/index.ts | 4 + packages/editor-framework/src/utils.ts | 142 ++++++ packages/editor-skeleton/build.json | 9 + packages/editor-skeleton/demo/usage.md | 24 + .../es/components/LeftIcon/index.js | 0 .../es/components/LeftIcon/index.scss | 0 .../es/components/LeftPlugin/index.js | 0 .../es/components/LeftPlugin/index.scss | 0 .../es/components/Panel/index.js | 0 .../es/components/TopIcon/index.d.ts | 6 + .../es/components/TopIcon/index.js | 14 + .../es/components/TopPlugin/index.js | 0 .../es/components/TopPlugin/index.scss | 0 .../editor-skeleton/es/config/skeleton.d.ts | 14 + .../editor-skeleton/es/config/skeleton.js | 14 + packages/editor-skeleton/es/config/utils.d.ts | 2 + packages/editor-skeleton/es/config/utils.js | 3 + packages/editor-skeleton/es/global.scss | 23 + packages/editor-skeleton/es/index.d.ts | 9 + packages/editor-skeleton/es/index.js | 43 ++ .../es/layouts/CenterArea/index.d.ts | 7 + .../es/layouts/CenterArea/index.js | 24 + .../es/layouts/CenterArea/index.scss | 0 .../es/layouts/LeftArea/index.d.ts | 5 + .../es/layouts/LeftArea/index.js | 6 + .../es/layouts/LeftArea/index.scss | 0 .../es/layouts/LeftArea/nav.d.ts | 7 + .../es/layouts/LeftArea/nav.js | 24 + .../es/layouts/LeftArea/panel.d.ts | 7 + .../es/layouts/LeftArea/panel.js | 24 + .../es/layouts/RightArea/index.d.ts | 7 + .../es/layouts/RightArea/index.js | 24 + .../es/layouts/RightArea/index.scss | 0 .../es/layouts/TopArea/index.d.ts | 7 + .../es/layouts/TopArea/index.js | 24 + .../es/layouts/TopArea/index.scss | 0 packages/editor-skeleton/es/locale/en-US.js | 10 + packages/editor-skeleton/es/locale/ja-JP.js | 1 + packages/editor-skeleton/es/locale/zh-CN.js | 10 + packages/editor-skeleton/es/locale/zh-TW.js | 1 + packages/editor-skeleton/es/style.js | 2 + packages/editor-skeleton/package.json | 29 +- .../src/components/Guide/index.module.scss | 11 - .../src/components/Guide/index.tsx | 71 --- .../src/components/LeftIcon/index.scss | 0 .../src/components/LeftIcon/index.tsx | 0 .../src/components/LeftPlugin/index.scss | 0 .../src/components/LeftPlugin/index.tsx | 0 .../src/components/Panel/index.tsx | 0 .../{Greeting => TopIcon}/index.tsx | 0 .../src/components/TopPlugin/index.scss | 0 .../src/components/TopPlugin/index.tsx | 0 .../src/config/{routes.ts => skeleton.ts} | 0 .../src/config/{menu.ts => utils.ts} | 0 packages/editor-skeleton/src/global.scss | 29 +- packages/editor-skeleton/src/index.tsx | 53 ++- .../src/layouts/BasicLayout/index.tsx | 9 - .../src/layouts/CenterArea/index.scss | 0 .../src/layouts/CenterArea/index.tsx | 17 + .../src/layouts/LeftArea/index.scss | 0 .../src/layouts/LeftArea/index.tsx | 7 + .../src/layouts/LeftArea/nav.tsx | 17 + .../src/layouts/LeftArea/panel.tsx | 17 + .../src/layouts/RightArea/index.scss | 0 .../src/layouts/RightArea/index.tsx | 17 + .../src/layouts/TopArea/index.scss | 0 .../src/layouts/TopArea/index.tsx | 17 + packages/editor-skeleton/src/locale/en-US.js | 10 + packages/editor-skeleton/src/locale/ja-JP.js | 1 + packages/editor-skeleton/src/locale/zh-CN.js | 10 + packages/editor-skeleton/src/locale/zh-TW.js | 1 + .../src/pages/Dashboard/index.tsx | 12 - packages/editor-skeleton/src/router.tsx | 73 --- packages/editor-skeleton/tsconfig.json | 18 +- packages/editor/.editorconfig | 12 + packages/editor/.eslintignore | 11 + packages/editor/.eslintrc.js | 7 + packages/editor/.gitignore | 20 + packages/editor/.stylelintignore | 7 + packages/editor/.stylelintrc.js | 3 + packages/editor/README.md | 20 + packages/editor/abc.json | 4 + .../{editor-skeleton => editor}/ice.config.js | 0 packages/editor/jsconfig.json | 9 + packages/editor/package.json | 46 ++ .../public/favicon.png | Bin .../public/index.html | 0 packages/editor/src/config/components.js | 3 + packages/editor/src/config/constants.js | 3 + packages/editor/src/config/locale/en-US.js | 1 + packages/editor/src/config/locale/index.js | 10 + packages/editor/src/config/locale/ja-JP.js | 1 + packages/editor/src/config/locale/zh-CN.js | 1 + packages/editor/src/config/locale/zh-TW.js | 1 + packages/editor/src/config/skeleton.js | 435 ++++++++++++++++++ packages/editor/src/config/theme.scss | 0 packages/editor/src/config/utils.js | 3 + packages/editor/src/global.scss | 8 + packages/editor/src/index.tsx | 42 ++ packages/editor/tests/index.js | 1 + packages/editor/tsconfig.json | 31 ++ 110 files changed, 1520 insertions(+), 249 deletions(-) create mode 100644 packages/editor-framework/es/context.d.ts create mode 100644 packages/editor-framework/es/context.js delete mode 100644 packages/editor-framework/es/index.d.ts delete mode 100644 packages/editor-framework/es/index.js delete mode 100644 packages/editor-framework/es/index.scss delete mode 100644 packages/editor-framework/es/style.js create mode 100644 packages/editor-skeleton/build.json create mode 100644 packages/editor-skeleton/demo/usage.md create mode 100644 packages/editor-skeleton/es/components/LeftIcon/index.js create mode 100644 packages/editor-skeleton/es/components/LeftIcon/index.scss create mode 100644 packages/editor-skeleton/es/components/LeftPlugin/index.js create mode 100644 packages/editor-skeleton/es/components/LeftPlugin/index.scss create mode 100644 packages/editor-skeleton/es/components/Panel/index.js create mode 100644 packages/editor-skeleton/es/components/TopIcon/index.d.ts create mode 100644 packages/editor-skeleton/es/components/TopIcon/index.js create mode 100644 packages/editor-skeleton/es/components/TopPlugin/index.js create mode 100644 packages/editor-skeleton/es/components/TopPlugin/index.scss create mode 100644 packages/editor-skeleton/es/config/skeleton.d.ts create mode 100644 packages/editor-skeleton/es/config/skeleton.js create mode 100644 packages/editor-skeleton/es/config/utils.d.ts create mode 100644 packages/editor-skeleton/es/config/utils.js create mode 100644 packages/editor-skeleton/es/global.scss create mode 100644 packages/editor-skeleton/es/index.d.ts create mode 100644 packages/editor-skeleton/es/index.js create mode 100644 packages/editor-skeleton/es/layouts/CenterArea/index.d.ts create mode 100644 packages/editor-skeleton/es/layouts/CenterArea/index.js create mode 100644 packages/editor-skeleton/es/layouts/CenterArea/index.scss create mode 100644 packages/editor-skeleton/es/layouts/LeftArea/index.d.ts create mode 100644 packages/editor-skeleton/es/layouts/LeftArea/index.js create mode 100644 packages/editor-skeleton/es/layouts/LeftArea/index.scss create mode 100644 packages/editor-skeleton/es/layouts/LeftArea/nav.d.ts create mode 100644 packages/editor-skeleton/es/layouts/LeftArea/nav.js create mode 100644 packages/editor-skeleton/es/layouts/LeftArea/panel.d.ts create mode 100644 packages/editor-skeleton/es/layouts/LeftArea/panel.js create mode 100644 packages/editor-skeleton/es/layouts/RightArea/index.d.ts create mode 100644 packages/editor-skeleton/es/layouts/RightArea/index.js create mode 100644 packages/editor-skeleton/es/layouts/RightArea/index.scss create mode 100644 packages/editor-skeleton/es/layouts/TopArea/index.d.ts create mode 100644 packages/editor-skeleton/es/layouts/TopArea/index.js create mode 100644 packages/editor-skeleton/es/layouts/TopArea/index.scss create mode 100644 packages/editor-skeleton/es/locale/en-US.js create mode 100644 packages/editor-skeleton/es/locale/ja-JP.js create mode 100644 packages/editor-skeleton/es/locale/zh-CN.js create mode 100644 packages/editor-skeleton/es/locale/zh-TW.js create mode 100644 packages/editor-skeleton/es/style.js delete mode 100644 packages/editor-skeleton/src/components/Guide/index.module.scss delete mode 100644 packages/editor-skeleton/src/components/Guide/index.tsx create mode 100644 packages/editor-skeleton/src/components/LeftIcon/index.scss create mode 100644 packages/editor-skeleton/src/components/LeftIcon/index.tsx create mode 100644 packages/editor-skeleton/src/components/LeftPlugin/index.scss create mode 100644 packages/editor-skeleton/src/components/LeftPlugin/index.tsx create mode 100644 packages/editor-skeleton/src/components/Panel/index.tsx rename packages/editor-skeleton/src/components/{Greeting => TopIcon}/index.tsx (100%) create mode 100644 packages/editor-skeleton/src/components/TopPlugin/index.scss create mode 100644 packages/editor-skeleton/src/components/TopPlugin/index.tsx rename packages/editor-skeleton/src/config/{routes.ts => skeleton.ts} (100%) rename packages/editor-skeleton/src/config/{menu.ts => utils.ts} (100%) delete mode 100644 packages/editor-skeleton/src/layouts/BasicLayout/index.tsx create mode 100644 packages/editor-skeleton/src/layouts/CenterArea/index.scss create mode 100644 packages/editor-skeleton/src/layouts/CenterArea/index.tsx create mode 100644 packages/editor-skeleton/src/layouts/LeftArea/index.scss create mode 100644 packages/editor-skeleton/src/layouts/LeftArea/index.tsx create mode 100644 packages/editor-skeleton/src/layouts/LeftArea/nav.tsx create mode 100644 packages/editor-skeleton/src/layouts/LeftArea/panel.tsx create mode 100644 packages/editor-skeleton/src/layouts/RightArea/index.scss create mode 100644 packages/editor-skeleton/src/layouts/RightArea/index.tsx create mode 100644 packages/editor-skeleton/src/layouts/TopArea/index.scss create mode 100644 packages/editor-skeleton/src/layouts/TopArea/index.tsx create mode 100644 packages/editor-skeleton/src/locale/en-US.js create mode 100644 packages/editor-skeleton/src/locale/ja-JP.js create mode 100644 packages/editor-skeleton/src/locale/zh-CN.js create mode 100644 packages/editor-skeleton/src/locale/zh-TW.js delete mode 100644 packages/editor-skeleton/src/pages/Dashboard/index.tsx delete mode 100644 packages/editor-skeleton/src/router.tsx create mode 100644 packages/editor/.editorconfig create mode 100644 packages/editor/.eslintignore create mode 100644 packages/editor/.eslintrc.js create mode 100644 packages/editor/.gitignore create mode 100644 packages/editor/.stylelintignore create mode 100644 packages/editor/.stylelintrc.js create mode 100644 packages/editor/README.md create mode 100644 packages/editor/abc.json rename packages/{editor-skeleton => editor}/ice.config.js (100%) create mode 100644 packages/editor/jsconfig.json create mode 100644 packages/editor/package.json rename packages/{editor-skeleton => editor}/public/favicon.png (100%) rename packages/{editor-skeleton => editor}/public/index.html (100%) create mode 100644 packages/editor/src/config/components.js create mode 100644 packages/editor/src/config/constants.js create mode 100644 packages/editor/src/config/locale/en-US.js create mode 100644 packages/editor/src/config/locale/index.js create mode 100644 packages/editor/src/config/locale/ja-JP.js create mode 100644 packages/editor/src/config/locale/zh-CN.js create mode 100644 packages/editor/src/config/locale/zh-TW.js create mode 100644 packages/editor/src/config/skeleton.js create mode 100644 packages/editor/src/config/theme.scss create mode 100644 packages/editor/src/config/utils.js create mode 100644 packages/editor/src/global.scss create mode 100644 packages/editor/src/index.tsx create mode 100644 packages/editor/tests/index.js create mode 100644 packages/editor/tsconfig.json diff --git a/packages/editor-framework/demo/usage.md b/packages/editor-framework/demo/usage.md index 819d1ac15..9f19eae0b 100644 --- a/packages/editor-framework/demo/usage.md +++ b/packages/editor-framework/demo/usage.md @@ -8,13 +8,11 @@ order: 1 ````jsx import React, { Component } from 'react'; import ReactDOM from 'react-dom'; -import Demo from 'editor-framework'; class App extends Component { render() { return (
-
); } diff --git a/packages/editor-framework/es/context.d.ts b/packages/editor-framework/es/context.d.ts new file mode 100644 index 000000000..7836d2580 --- /dev/null +++ b/packages/editor-framework/es/context.d.ts @@ -0,0 +1,3 @@ +/// +declare const context: import("react").Context<{}>; +export default context; diff --git a/packages/editor-framework/es/context.js b/packages/editor-framework/es/context.js new file mode 100644 index 000000000..450ae72fb --- /dev/null +++ b/packages/editor-framework/es/context.js @@ -0,0 +1,3 @@ +import { createContext } from 'react'; +var context = createContext({}); +export default context; \ No newline at end of file diff --git a/packages/editor-framework/es/index.d.ts b/packages/editor-framework/es/index.d.ts deleted file mode 100644 index ceb2fe364..000000000 --- a/packages/editor-framework/es/index.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -/// -declare function TSDemo(props: any): JSX.Element; -declare namespace TSDemo { - var propTypes: {}; - var defaultProps: {}; -} -export default TSDemo; diff --git a/packages/editor-framework/es/index.js b/packages/editor-framework/es/index.js deleted file mode 100644 index 9ea3ef8aa..000000000 --- a/packages/editor-framework/es/index.js +++ /dev/null @@ -1,13 +0,0 @@ -import _extends from "@babel/runtime/helpers/extends"; -import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/objectWithoutPropertiesLoose"; -import * as React from 'react'; -export default function TSDemo(props) { - var type = props.type, - others = _objectWithoutPropertiesLoose(props, ["type"]); - - return React.createElement("div", _extends({ - className: "TSDemo" - }, others), "Hello TSDemo"); -} -TSDemo.propTypes = {}; -TSDemo.defaultProps = {}; \ No newline at end of file diff --git a/packages/editor-framework/es/index.scss b/packages/editor-framework/es/index.scss deleted file mode 100644 index 3a0f72cfb..000000000 --- a/packages/editor-framework/es/index.scss +++ /dev/null @@ -1,4 +0,0 @@ -/* write style here */ -.TSDemo { - -} diff --git a/packages/editor-framework/es/style.js b/packages/editor-framework/es/style.js deleted file mode 100644 index bf3c23acb..000000000 --- a/packages/editor-framework/es/style.js +++ /dev/null @@ -1 +0,0 @@ -import './index.scss'; \ No newline at end of file diff --git a/packages/editor-framework/package.json b/packages/editor-framework/package.json index ebcbe65db..2b11f2c53 100644 --- a/packages/editor-framework/package.json +++ b/packages/editor-framework/package.json @@ -28,7 +28,8 @@ "events": "^3.1.0", "intl-messageformat": "^7.8.4", "lodash": "^4.17.15", - "prop-types": "^15.5.8" + "prop-types": "^15.5.8", + "store": "^2.0.12" }, "devDependencies": { "@alib/build-scripts": "^0.1.3", diff --git a/packages/editor-framework/src/editor.ts b/packages/editor-framework/src/editor.ts index daeeb2182..b3328df2d 100644 --- a/packages/editor-framework/src/editor.ts +++ b/packages/editor-framework/src/editor.ts @@ -1,7 +1,46 @@ import EventEmitter from 'events'; import Debug from 'debug'; -let instance = null; +import store from 'store'; +import { + unRegistShortCuts, + registShortCuts, + transformToPromise, + generateI18n +} from './utils'; + +// 根据url参数设置debug选项 +const res = /_?debug=(.*?)(&|$)/.exec(location.search); +if (res && res[1]) { + window.__isDebug = true; + store.storage.write('debug', res[1] === 'true' ? '*' : res[1]); +} else { + window.__isDebug = false; + store.remove('debug'); +} + +//重要,用于矫正画布执行new Function的window对象上下文 +window.__newFunc = funContext => { + return new Function(funContext); +}; + +//关闭浏览器前提醒,只有产生过交互才会生效 +window.onbeforeunload = function(e) { + e = e || window.event; + // 本地调试不生效 + if (location.href.indexOf('localhost') > 0) return; + var msg = '您确定要离开此页面吗?'; + e.cancelBubble = true; + e.returnValue = msg; + if (e.stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } + return msg; +}; + + +let instance = null; const debug = Debug('editor'); EventEmitter.defaultMaxListeners = 100; @@ -9,7 +48,7 @@ export interface editor { }; -export class Editor extends EventEmitter { +export default class Editor extends EventEmitter { static getInstance = () => { if (!instance) { instance = new Editor(); @@ -21,22 +60,62 @@ export class Editor extends EventEmitter { super(); instance = this; Object.assign(this, config); + this.init(); } init() { + const { + hooks, + shortCuts, + lifeCycles + } = this.config || {}; + this.destroy(); + this.locale = store.get('lowcode-editor-locale') || 'zh-CN'; + this.messages = this.messagesSet[this.locale]; + this.i18n = generateI18n(this.locale, this.messages); + this.pluginStatus = this.initPluginStatus(); + this.initHooks(hooks, appHelper); + appHelper.emit('editor.beforeInit'); + const init = lifeCycles && lifeCycles.init || () => {}; + // 用户可以通过设置extensions.init自定义初始化流程; + transformToPromise(init(this)) + .then(() => { + // 注册快捷键 + registShortCuts(shortCuts, this); + this.emit('editor.afterInit'); + }) + .catch(err => { + console.warn(err); + }); } destroy() { - + try { + const { + hooks = [], + shortCuts = [], + lifeCycles = {} + } = this.config; + unRegistShortCuts(shortCuts); + this.destroyHooks(hooks); + lifeCycles.destroy && lifeCycles.destroy(); + } catch (err) { + console.warn(err); + return; + } } get(key:string):any { return this[key]; } - set(key, val) { + set(key:string|object, val:any):void { if (typeof key === 'string') { + if (['init', 'destroy', 'get', 'set', 'batchOn', 'batchOff', 'batchOnce'].includes(key)) { + console.warning('init, destroy, get, set, batchOn, batchOff, batchOnce is private attribute'); + return; + } this[key] = val; } else if (typeof key === 'object') { Object.keys(key).forEach(item => { @@ -45,18 +124,63 @@ export class Editor extends EventEmitter { } } - batchOn(events, lisenter) { + batchOn(events:Array, lisenter:function):void { if (!Array.isArray(events)) return; events.forEach(event => this.on(event, lisenter)); } - batchOnce(events, lisenter) { + batchOnce(events:Array, lisenter:function):void { if (!Array.isArray(events)) return; events.forEach(event => this.once(event, lisenter)); } - batchOff(events, lisenter) { + batchOff(events:Array, lisenter:function):void { if (!Array.isArray(events)) return; events.forEach(event => this.off(event, lisenter)); } + + //销毁hooks中的消息监听 + private destroyHooks(hooks = []) { + hooks.forEach((item, idx) => { + if (typeof this.__hooksFuncs[idx] === 'function') { + this.appHelper.off(item.message, this.__hooksFuncs[idx]); + } + }); + delete this.__hooksFuncs; + }; + + //初始化hooks中的消息监听 + private initHooks(hooks = []) { + this.__hooksFuncs = hooks.map(item => { + const func = (...args) => { + item.handler(this, ...args); + }; + this[item.type](item.message, func); + return func; + }); + }; + + + private initPluginStatus () { + const {plugins = {}} = this.config; + const pluginAreas = Object.keys(plugins); + const res = {}; + pluginAreas.forEach(area => { + (plugins[area] || []).forEach(plugin => { + if (plugin.type === 'Divider') return; + const { visible, disabled, dotted } = plugin.props || {}; + res[plugin.pluginKey] = { + visible: typeof visible === 'boolean' ? visible : true, + disabled: typeof disabled === 'boolean' ? disabled : false, + dotted: typeof dotted === 'boolean' ? dotted : false + }; + const pluginClass = this.props.components[skeletonUtils.generateAddonCompName(addon.addonKey)]; + // 判断如果编辑器插件有init静态方法,则在此执行init方法 + if (pluginClass && pluginClass.init) { + pluginClass.init(this); + } + }); + }); + return res; + }; } diff --git a/packages/editor-framework/src/index.ts b/packages/editor-framework/src/index.ts index e69de29bb..aac18d138 100644 --- a/packages/editor-framework/src/index.ts +++ b/packages/editor-framework/src/index.ts @@ -0,0 +1,4 @@ +import Editor from './editor'; + + +export default Editor; \ No newline at end of file diff --git a/packages/editor-framework/src/utils.ts b/packages/editor-framework/src/utils.ts index 5cc738bca..d09c4929c 100644 --- a/packages/editor-framework/src/utils.ts +++ b/packages/editor-framework/src/utils.ts @@ -72,4 +72,146 @@ export function getEnv() { const isTheia = window.is_theia === true; if (isTheia) return ENV.WEBIDE; return ENV.WEB; +} + +// 注册快捷键 +export function registShortCuts(config, editor) { + const keyboardFilter = (keymaster.filter = event => { + let eTarget = event.target || event.srcElement; + let tagName = eTarget.tagName; + let isInput = !!(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA'); + let isContenteditable = !!eTarget.getAttribute('contenteditable'); + if (isInput || isContenteditable) { + if (event.metaKey === true && [70, 83].includes(event.keyCode)) event.preventDefault(); //禁止触发chrome原生的页面保存或查找 + return false; + } else { + return true; + } + }); + + const ideMessage = appHelper.utils && appHelper.utils.ideMessage; + + //复制 + if (!document.copyListener) { + document.copyListener = e => { + if (!keyboardFilter(e) || appHelper.isCopying) return; + const schema = appHelper.schemaHelper && appHelper.schemaHelper.schemaMap[appHelper.activeKey]; + if (!schema || !isSchema(schema)) return; + appHelper.isCopying = true; + const schemaStr = serialize(transformSchemaToPure(schema), { + unsafe: true + }); + setClipboardData(schemaStr) + .then(() => { + ideMessage && ideMessage('success', '当前内容已复制到剪贴板,请使用快捷键Command+v进行粘贴'); + appHelper.emit('schema.copy', schemaStr, schema); + appHelper.isCopying = false; + }) + .catch(errMsg => { + ideMessage && ideMessage('error', errMsg); + appHelper.isCopying = false; + }); + }; + document.addEventListener('copy', document.copyListener); + if (window.parent.vscode) { + keymaster('command+c', document.copyListener); + } + } + + //粘贴 + if (!document.pasteListener) { + const doPaste = (e, text) => { + if (!keyboardFilter(e) || appHelper.isPasting) return; + const schemaHelper = appHelper.schemaHelper; + let targetKey = appHelper.activeKey; + let direction = 'after'; + const topKey = schemaHelper.schema && schemaHelper.schema.__ctx && schemaHelper.schema.__ctx.lunaKey; + if (!targetKey || topKey === targetKey) { + const schemaHelper = appHelper.schemaHelper; + const topKey = schemaHelper.schema && schemaHelper.schema.__ctx && schemaHelper.schema.__ctx.lunaKey; + if (!topKey) return; + targetKey = topKey; + direction = 'in'; + } + appHelper.isPasting = true; + const schema = parseObj(text); + if (!isSchema(schema)) { + appHelper.emit('illegalSchema.paste', text); + // ideMessage && ideMessage('error', '当前内容不是模型结构,不能粘贴进来!'); + console.warn('paste schema illegal'); + appHelper.isPasting = false; + return; + } + appHelper.emit('material.add', { + schema, + targetKey, + direction + }); + appHelper.isPasting = false; + appHelper.emit('schema.paste', schema); + }; + document.pasteListener = e => { + const clipboardData = e.clipboardData || window.clipboardData; + const text = clipboardData && clipboardData.getData('text'); + doPaste(e, text); + }; + document.addEventListener('paste', document.pasteListener); + if (window.parent.vscode) { + keymaster('command+v', e => { + const sendIDEMessage = window.parent.sendIDEMessage; + sendIDEMessage && + sendIDEMessage({ + action: 'readClipboard' + }) + .then(text => { + doPaste(e, text); + }) + .catch(err => { + console.warn(err); + }); + }); + } + } + + (config || []).forEach(item => { + keymaster(item.keyboard, ev => { + ev.preventDefault(); + item.handler(ev, appHelper, keymaster); + }); + }); +} + +// 取消注册快捷 +export function unRegistShortCuts(config) { + (config || []).forEach(item => { + keymaster.unbind(item.keyboard); + }); + if (window.parent.vscode) { + keymaster.unbind('command+c'); + keymaster.unbind('command+v'); + } + if (document.copyListener) { + document.removeEventListener('copy', document.copyListener); + delete document.copyListener; + } + if (document.pasteListener) { + document.removeEventListener('paste', document.pasteListener); + delete document.pasteListener; + } +} + +// 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve +export function transformToPromise(input) { + if (input instanceof Promise) return input; + return new Promise((resolve, reject) => { + if (input || input === undefined) { + resolve(); + } else { + reject(); + } + }); +} + +export function comboEditorConfig(defaultConfig, customConfig) { + } \ No newline at end of file diff --git a/packages/editor-skeleton/build.json b/packages/editor-skeleton/build.json new file mode 100644 index 000000000..77627cdf9 --- /dev/null +++ b/packages/editor-skeleton/build.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + "build-plugin-component", + "build-plugin-fusion", + ["build-plugin-moment-locales", { + "locales": ["zh-cn"] + }] + ] +} \ No newline at end of file diff --git a/packages/editor-skeleton/demo/usage.md b/packages/editor-skeleton/demo/usage.md new file mode 100644 index 000000000..9f19eae0b --- /dev/null +++ b/packages/editor-skeleton/demo/usage.md @@ -0,0 +1,24 @@ +--- +title: Simple Usage +order: 1 +--- + +本 Demo 演示一行文字的用法。 + +````jsx +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; + +class App extends Component { + render() { + return ( +
+
+ ); + } +} + +ReactDOM.render(( + +), mountNode); +```` diff --git a/packages/editor-skeleton/es/components/LeftIcon/index.js b/packages/editor-skeleton/es/components/LeftIcon/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/es/components/LeftIcon/index.scss b/packages/editor-skeleton/es/components/LeftIcon/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/es/components/LeftPlugin/index.js b/packages/editor-skeleton/es/components/LeftPlugin/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/es/components/LeftPlugin/index.scss b/packages/editor-skeleton/es/components/LeftPlugin/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/es/components/Panel/index.js b/packages/editor-skeleton/es/components/Panel/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/es/components/TopIcon/index.d.ts b/packages/editor-skeleton/es/components/TopIcon/index.d.ts new file mode 100644 index 000000000..0e1aea7cb --- /dev/null +++ b/packages/editor-skeleton/es/components/TopIcon/index.d.ts @@ -0,0 +1,6 @@ +/// +export interface Props { + name: string; +} +declare const Greeting: ({ name }: Props) => JSX.Element; +export default Greeting; diff --git a/packages/editor-skeleton/es/components/TopIcon/index.js b/packages/editor-skeleton/es/components/TopIcon/index.js new file mode 100644 index 000000000..3a62c613d --- /dev/null +++ b/packages/editor-skeleton/es/components/TopIcon/index.js @@ -0,0 +1,14 @@ +import React from 'react'; + +var Greeting = function Greeting(_ref) { + var name = _ref.name; + return React.createElement("div", { + style: { + textAlign: 'center', + fontSize: '40px', + fontWeight: 'bold' + } + }, "Hello, ", name); +}; + +export default Greeting; \ No newline at end of file diff --git a/packages/editor-skeleton/es/components/TopPlugin/index.js b/packages/editor-skeleton/es/components/TopPlugin/index.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/es/components/TopPlugin/index.scss b/packages/editor-skeleton/es/components/TopPlugin/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/es/config/skeleton.d.ts b/packages/editor-skeleton/es/config/skeleton.d.ts new file mode 100644 index 000000000..67ae8a0ae --- /dev/null +++ b/packages/editor-skeleton/es/config/skeleton.d.ts @@ -0,0 +1,14 @@ +declare const routerConfig: { + path: string; + component: any; + children: ({ + path: string; + component: any; + redirect?: undefined; + } | { + path: string; + redirect: string; + component?: undefined; + })[]; +}[]; +export default routerConfig; diff --git a/packages/editor-skeleton/es/config/skeleton.js b/packages/editor-skeleton/es/config/skeleton.js new file mode 100644 index 000000000..8fb0727ab --- /dev/null +++ b/packages/editor-skeleton/es/config/skeleton.js @@ -0,0 +1,14 @@ +import Dashboard from '@/pages/Dashboard'; +import BasicLayout from '@/layouts/BasicLayout'; +var routerConfig = [{ + path: '/', + component: BasicLayout, + children: [{ + path: '/dashboard', + component: Dashboard + }, { + path: '/', + redirect: '/dashboard' + }] +}]; +export default routerConfig; \ No newline at end of file diff --git a/packages/editor-skeleton/es/config/utils.d.ts b/packages/editor-skeleton/es/config/utils.d.ts new file mode 100644 index 000000000..5e5bda5ab --- /dev/null +++ b/packages/editor-skeleton/es/config/utils.d.ts @@ -0,0 +1,2 @@ +declare const asideMenuConfig: any[]; +export { asideMenuConfig }; diff --git a/packages/editor-skeleton/es/config/utils.js b/packages/editor-skeleton/es/config/utils.js new file mode 100644 index 000000000..39cee308b --- /dev/null +++ b/packages/editor-skeleton/es/config/utils.js @@ -0,0 +1,3 @@ +// 菜单配置 +var asideMenuConfig = []; +export { asideMenuConfig }; \ No newline at end of file diff --git a/packages/editor-skeleton/es/global.scss b/packages/editor-skeleton/es/global.scss new file mode 100644 index 000000000..68689a592 --- /dev/null +++ b/packages/editor-skeleton/es/global.scss @@ -0,0 +1,23 @@ +.next-loading { + .next-loading-wrap { + height: 100%; + } +} +.lowcode-editor { + .lowcode-main-content { + position: absolute; + top: 54px; + left: 0; + right: 0; + bottom: 0; + display: flex; + } + .lowcode-center-area { + flex: 1; + display: flex; + flex-direction: column; + background: #f7f7f7; + padding: 10px; + overflow: auto; + } +} diff --git a/packages/editor-skeleton/es/index.d.ts b/packages/editor-skeleton/es/index.d.ts new file mode 100644 index 000000000..f07a622f4 --- /dev/null +++ b/packages/editor-skeleton/es/index.d.ts @@ -0,0 +1,9 @@ +/// +import { PureComponent } from 'react-dom'; +import './global.scss'; +export default class Skeleton extends PureComponent { + static displayName: string; + constructor(props: any); + componentWillUnmount(): void; + render(): JSX.Element; +} diff --git a/packages/editor-skeleton/es/index.js b/packages/editor-skeleton/es/index.js new file mode 100644 index 000000000..7031d8ee9 --- /dev/null +++ b/packages/editor-skeleton/es/index.js @@ -0,0 +1,43 @@ +import _ConfigProvider from "@alifd/next/es/config-provider"; +import _Loading from "@alifd/next/es/loading"; +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; +import React, { PureComponent } from 'react-dom'; // import Editor from '@ali/lowcode-engine-editor'; + +import './global.scss'; + +var Skeleton = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(Skeleton, _PureComponent); + + function Skeleton(props) { + return _PureComponent.call(this, props) || this; // this.editor = new Editor(props.config, props.utils); + } + + var _proto = Skeleton.prototype; + + _proto.componentWillUnmount = function componentWillUnmount() {// this.editor && this.editor.destroy(); + // this.editor = null; + }; + + _proto.render = function render() { + var _this$props = this.props, + location = _this$props.location, + history = _this$props.history, + messages = _this$props.messages; + return React.createElement(_ConfigProvider, { + locale: messages[appHelper.locale] + }, React.createElement(_Loading, { + tip: this.i18n('loading'), + size: "large", + visible: loading || !initReady, + shape: "fusion-reactor", + fullScreen: true + }, React.createElement("div", { + className: "lowcode-editor" + }))); + }; + + return Skeleton; +}(PureComponent); + +Skeleton.displayName = 'lowcodeEditorSkeleton'; +export { Skeleton as default }; \ No newline at end of file diff --git a/packages/editor-skeleton/es/layouts/CenterArea/index.d.ts b/packages/editor-skeleton/es/layouts/CenterArea/index.d.ts new file mode 100644 index 000000000..201db137a --- /dev/null +++ b/packages/editor-skeleton/es/layouts/CenterArea/index.d.ts @@ -0,0 +1,7 @@ +import { PureComponent } from 'react'; +import './index.scss'; +export default class CenterArea extends PureComponent { + static displayName: string; + constructor(props: any); + render(): JSX.Element; +} diff --git a/packages/editor-skeleton/es/layouts/CenterArea/index.js b/packages/editor-skeleton/es/layouts/CenterArea/index.js new file mode 100644 index 000000000..4b9710f81 --- /dev/null +++ b/packages/editor-skeleton/es/layouts/CenterArea/index.js @@ -0,0 +1,24 @@ +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; +import React, { PureComponent } from 'react'; +import './index.scss'; + +var CenterArea = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(CenterArea, _PureComponent); + + function CenterArea(props) { + return _PureComponent.call(this, props) || this; + } + + var _proto = CenterArea.prototype; + + _proto.render = function render() { + return React.createElement("div", { + className: "lowcode-center-area" + }); + }; + + return CenterArea; +}(PureComponent); + +CenterArea.displayName = 'lowcodeCenterArea'; +export { CenterArea as default }; \ No newline at end of file diff --git a/packages/editor-skeleton/es/layouts/CenterArea/index.scss b/packages/editor-skeleton/es/layouts/CenterArea/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/es/layouts/LeftArea/index.d.ts b/packages/editor-skeleton/es/layouts/LeftArea/index.d.ts new file mode 100644 index 000000000..d888d90f0 --- /dev/null +++ b/packages/editor-skeleton/es/layouts/LeftArea/index.d.ts @@ -0,0 +1,5 @@ +declare const _default: { + Nav: any; + Panel: any; +}; +export default _default; diff --git a/packages/editor-skeleton/es/layouts/LeftArea/index.js b/packages/editor-skeleton/es/layouts/LeftArea/index.js new file mode 100644 index 000000000..50ecfad2f --- /dev/null +++ b/packages/editor-skeleton/es/layouts/LeftArea/index.js @@ -0,0 +1,6 @@ +import Nav from './nav'; +import Panel from './panel'; +export default { + Nav: Nav, + Panel: Panel +}; \ No newline at end of file diff --git a/packages/editor-skeleton/es/layouts/LeftArea/index.scss b/packages/editor-skeleton/es/layouts/LeftArea/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/es/layouts/LeftArea/nav.d.ts b/packages/editor-skeleton/es/layouts/LeftArea/nav.d.ts new file mode 100644 index 000000000..c44a09527 --- /dev/null +++ b/packages/editor-skeleton/es/layouts/LeftArea/nav.d.ts @@ -0,0 +1,7 @@ +import { PureComponent } from 'react'; +import './index.scss'; +export default class LeftAreaPanel extends PureComponent { + static displayName: string; + constructor(props: any); + render(): JSX.Element; +} diff --git a/packages/editor-skeleton/es/layouts/LeftArea/nav.js b/packages/editor-skeleton/es/layouts/LeftArea/nav.js new file mode 100644 index 000000000..962129007 --- /dev/null +++ b/packages/editor-skeleton/es/layouts/LeftArea/nav.js @@ -0,0 +1,24 @@ +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; +import React, { PureComponent } from 'react'; +import './index.scss'; + +var LeftAreaPanel = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(LeftAreaPanel, _PureComponent); + + function LeftAreaPanel(props) { + return _PureComponent.call(this, props) || this; + } + + var _proto = LeftAreaPanel.prototype; + + _proto.render = function render() { + return React.createElement("div", { + className: "lowcode-left-area-nav" + }); + }; + + return LeftAreaPanel; +}(PureComponent); + +LeftAreaPanel.displayName = 'lowcodeLeftAreaNav'; +export { LeftAreaPanel as default }; \ No newline at end of file diff --git a/packages/editor-skeleton/es/layouts/LeftArea/panel.d.ts b/packages/editor-skeleton/es/layouts/LeftArea/panel.d.ts new file mode 100644 index 000000000..c44a09527 --- /dev/null +++ b/packages/editor-skeleton/es/layouts/LeftArea/panel.d.ts @@ -0,0 +1,7 @@ +import { PureComponent } from 'react'; +import './index.scss'; +export default class LeftAreaPanel extends PureComponent { + static displayName: string; + constructor(props: any); + render(): JSX.Element; +} diff --git a/packages/editor-skeleton/es/layouts/LeftArea/panel.js b/packages/editor-skeleton/es/layouts/LeftArea/panel.js new file mode 100644 index 000000000..0120423d8 --- /dev/null +++ b/packages/editor-skeleton/es/layouts/LeftArea/panel.js @@ -0,0 +1,24 @@ +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; +import React, { PureComponent } from 'react'; +import './index.scss'; + +var LeftAreaPanel = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(LeftAreaPanel, _PureComponent); + + function LeftAreaPanel(props) { + return _PureComponent.call(this, props) || this; + } + + var _proto = LeftAreaPanel.prototype; + + _proto.render = function render() { + return React.createElement("div", { + className: "lowcode-left-area" + }); + }; + + return LeftAreaPanel; +}(PureComponent); + +LeftAreaPanel.displayName = 'lowcodeLeftAreaPanel'; +export { LeftAreaPanel as default }; \ No newline at end of file diff --git a/packages/editor-skeleton/es/layouts/RightArea/index.d.ts b/packages/editor-skeleton/es/layouts/RightArea/index.d.ts new file mode 100644 index 000000000..ad87a57de --- /dev/null +++ b/packages/editor-skeleton/es/layouts/RightArea/index.d.ts @@ -0,0 +1,7 @@ +import { PureComponent } from 'react'; +import './index.scss'; +export default class RightArea extends PureComponent { + static displayName: string; + constructor(props: any); + render(): JSX.Element; +} diff --git a/packages/editor-skeleton/es/layouts/RightArea/index.js b/packages/editor-skeleton/es/layouts/RightArea/index.js new file mode 100644 index 000000000..d1d38689d --- /dev/null +++ b/packages/editor-skeleton/es/layouts/RightArea/index.js @@ -0,0 +1,24 @@ +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; +import React, { PureComponent } from 'react'; +import './index.scss'; + +var RightArea = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(RightArea, _PureComponent); + + function RightArea(props) { + return _PureComponent.call(this, props) || this; + } + + var _proto = RightArea.prototype; + + _proto.render = function render() { + return React.createElement("div", { + className: "lowcode-right-area" + }); + }; + + return RightArea; +}(PureComponent); + +RightArea.displayName = 'lowcodeRightArea'; +export { RightArea as default }; \ No newline at end of file diff --git a/packages/editor-skeleton/es/layouts/RightArea/index.scss b/packages/editor-skeleton/es/layouts/RightArea/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/es/layouts/TopArea/index.d.ts b/packages/editor-skeleton/es/layouts/TopArea/index.d.ts new file mode 100644 index 000000000..716bd94a8 --- /dev/null +++ b/packages/editor-skeleton/es/layouts/TopArea/index.d.ts @@ -0,0 +1,7 @@ +import { PureComponent } from 'react'; +import './index.scss'; +export default class TopArea extends PureComponent { + static displayName: string; + constructor(props: any); + render(): JSX.Element; +} diff --git a/packages/editor-skeleton/es/layouts/TopArea/index.js b/packages/editor-skeleton/es/layouts/TopArea/index.js new file mode 100644 index 000000000..39f2b2b23 --- /dev/null +++ b/packages/editor-skeleton/es/layouts/TopArea/index.js @@ -0,0 +1,24 @@ +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; +import React, { PureComponent } from 'react'; +import './index.scss'; + +var TopArea = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(TopArea, _PureComponent); + + function TopArea(props) { + return _PureComponent.call(this, props) || this; + } + + var _proto = TopArea.prototype; + + _proto.render = function render() { + return React.createElement("div", { + className: "lowcode-top-area" + }); + }; + + return TopArea; +}(PureComponent); + +TopArea.displayName = 'lowcodeTopArea'; +export { TopArea as default }; \ No newline at end of file diff --git a/packages/editor-skeleton/es/layouts/TopArea/index.scss b/packages/editor-skeleton/es/layouts/TopArea/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/es/locale/en-US.js b/packages/editor-skeleton/es/locale/en-US.js new file mode 100644 index 000000000..f190625da --- /dev/null +++ b/packages/editor-skeleton/es/locale/en-US.js @@ -0,0 +1,10 @@ +export default { + loading: 'loading...', + rejectRedirect: 'Redirect is not allowed', + expand: 'Unfold', + fold: 'Fold', + pageNotExist: 'The current Page not exist', + enterFromAppCenter: 'Please enter from the app center', + noPermission: 'Sorry, you do not have the develop permission', + getPermission: 'Please connect the app owners {owners} to get the permission' +}; \ No newline at end of file diff --git a/packages/editor-skeleton/es/locale/ja-JP.js b/packages/editor-skeleton/es/locale/ja-JP.js new file mode 100644 index 000000000..7c645e42f --- /dev/null +++ b/packages/editor-skeleton/es/locale/ja-JP.js @@ -0,0 +1 @@ +export default {}; \ No newline at end of file diff --git a/packages/editor-skeleton/es/locale/zh-CN.js b/packages/editor-skeleton/es/locale/zh-CN.js new file mode 100644 index 000000000..51791f741 --- /dev/null +++ b/packages/editor-skeleton/es/locale/zh-CN.js @@ -0,0 +1,10 @@ +export default { + loading: '加载中...', + rejectRedirect: '开发中,已阻止发生跳转', + expand: '展开', + fold: '收起', + pageNotExist: '当前访问地址不存在', + enterFromAppCenter: '请从应用中心入口重新进入', + noPermission: '抱歉,您暂无开发权限', + getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限' +}; \ No newline at end of file diff --git a/packages/editor-skeleton/es/locale/zh-TW.js b/packages/editor-skeleton/es/locale/zh-TW.js new file mode 100644 index 000000000..7c645e42f --- /dev/null +++ b/packages/editor-skeleton/es/locale/zh-TW.js @@ -0,0 +1 @@ +export default {}; \ No newline at end of file diff --git a/packages/editor-skeleton/es/style.js b/packages/editor-skeleton/es/style.js new file mode 100644 index 000000000..ba2702ef4 --- /dev/null +++ b/packages/editor-skeleton/es/style.js @@ -0,0 +1,2 @@ +import '@alifd/next/es/config-provider/style'; +import '@alifd/next/es/loading/style'; \ No newline at end of file diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json index 7ada377d0..9067b4544 100644 --- a/packages/editor-skeleton/package.json +++ b/packages/editor-skeleton/package.json @@ -15,20 +15,26 @@ "react-router-dom": "^5.0.1" }, "devDependencies": { + "@alib/build-scripts": "^0.1.3", + "@alifd/next": "1.x", "@ice/spec": "^0.1.1", - "css-modules-typescript-loader": "^2.0.4", + "@types/lodash": "^4.14.149", + "@types/react": "^16.9.13", + "@types/react-dom": "^16.9.4", + "build-plugin-component": "^0.2.0", + "build-plugin-fusion": "^0.1.0", + "build-plugin-moment-locales": "^0.1.0", "eslint": "^6.0.1", - "ice-plugin-fusion": "^0.1.4", - "ice-plugin-moment-locales": "^0.1.0", - "ice-scripts": "^2.0.0", - "stylelint": "^10.1.0" + "prettier": "^1.19.1", + "react": "^16.8.0", + "react-dom": "^16.8.0" }, "scripts": { - "start": "ice-scripts dev", - "build": "ice-scripts build", - "lint": "npm run eslint && npm run stylelint", - "eslint": "eslint --cache --ext .js,.jsx ./", - "stylelint": "stylelint ./**/*.scss" + "start": "build-scripts start", + "build": "build-scripts build", + "prepublishOnly": "npm run prettier && npm run build", + "lint": "eslint --cache --ext .js,.jsx ./", + "prettier": "prettier --write \"./src/**/*.{ts,tsx,js,jsx,ejs,less,css,scss,json}\" " }, "engines": { "node": ">=8.0.0" @@ -43,5 +49,6 @@ "repository": { "type": "git", "url": "https://github.com/ice-lab/react-materials/tree/master/scaffolds/ice-ts" - } + }, + "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-engine-skeleton@0.0.1/build/index.html" } diff --git a/packages/editor-skeleton/src/components/Guide/index.module.scss b/packages/editor-skeleton/src/components/Guide/index.module.scss deleted file mode 100644 index eeb4e21aa..000000000 --- a/packages/editor-skeleton/src/components/Guide/index.module.scss +++ /dev/null @@ -1,11 +0,0 @@ -.item { - height: 34px; - line-height: 34px; -} -.title { - text-align: center; -} -.container { - width: 470px; - margin: 40px auto; -} \ No newline at end of file diff --git a/packages/editor-skeleton/src/components/Guide/index.tsx b/packages/editor-skeleton/src/components/Guide/index.tsx deleted file mode 100644 index 9a07b5789..000000000 --- a/packages/editor-skeleton/src/components/Guide/index.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import { Button } from '@alifd/next'; -import styles from './index.module.scss'; - -const Guide = () => { - return ( -
-

使用指南

-
    -
  • - 1. 该模板适用于从 0 到 1 开始搭建项目,内置引导页面,路由和菜单展示。 -
  • -
  • 2. 菜单配置: menuConfig.js。
  • -
  • 3. 路由配置: routerConfig.js。
  • -
  • - 4. 通过 GUI 工具{' '} - - Iceworks - {' '} - 创建页面,会同步的更新菜单和路由配置。 -
  • -
  • - 5. 基于{' '} - - 物料 - {' '} - 生成的页面将会添加在 pages 目录。 -
  • -
  • - 6. 让前端工程变的轻松便捷, - - 下载 iceworks - {' '} - 。 -
  • -
- -
- ); -}; - -export default Guide; diff --git a/packages/editor-skeleton/src/components/LeftIcon/index.scss b/packages/editor-skeleton/src/components/LeftIcon/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/src/components/LeftIcon/index.tsx b/packages/editor-skeleton/src/components/LeftIcon/index.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/src/components/LeftPlugin/index.scss b/packages/editor-skeleton/src/components/LeftPlugin/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/src/components/LeftPlugin/index.tsx b/packages/editor-skeleton/src/components/LeftPlugin/index.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/src/components/Panel/index.tsx b/packages/editor-skeleton/src/components/Panel/index.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/src/components/Greeting/index.tsx b/packages/editor-skeleton/src/components/TopIcon/index.tsx similarity index 100% rename from packages/editor-skeleton/src/components/Greeting/index.tsx rename to packages/editor-skeleton/src/components/TopIcon/index.tsx diff --git a/packages/editor-skeleton/src/components/TopPlugin/index.scss b/packages/editor-skeleton/src/components/TopPlugin/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/src/components/TopPlugin/index.tsx b/packages/editor-skeleton/src/components/TopPlugin/index.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/src/config/routes.ts b/packages/editor-skeleton/src/config/skeleton.ts similarity index 100% rename from packages/editor-skeleton/src/config/routes.ts rename to packages/editor-skeleton/src/config/skeleton.ts diff --git a/packages/editor-skeleton/src/config/menu.ts b/packages/editor-skeleton/src/config/utils.ts similarity index 100% rename from packages/editor-skeleton/src/config/menu.ts rename to packages/editor-skeleton/src/config/utils.ts diff --git a/packages/editor-skeleton/src/global.scss b/packages/editor-skeleton/src/global.scss index 6086b53a6..68689a592 100644 --- a/packages/editor-skeleton/src/global.scss +++ b/packages/editor-skeleton/src/global.scss @@ -1,6 +1,23 @@ -// 引入默认全局样式 -@import '@alifd/next/reset.scss'; - -body { - -webkit-font-smoothing: antialiased; -} \ No newline at end of file +.next-loading { + .next-loading-wrap { + height: 100%; + } +} +.lowcode-editor { + .lowcode-main-content { + position: absolute; + top: 54px; + left: 0; + right: 0; + bottom: 0; + display: flex; + } + .lowcode-center-area { + flex: 1; + display: flex; + flex-direction: column; + background: #f7f7f7; + padding: 10px; + overflow: auto; + } +} diff --git a/packages/editor-skeleton/src/index.tsx b/packages/editor-skeleton/src/index.tsx index ba347d027..2a9643b69 100644 --- a/packages/editor-skeleton/src/index.tsx +++ b/packages/editor-skeleton/src/index.tsx @@ -1,13 +1,52 @@ -import ReactDOM from 'react-dom'; +import React, {PureComponent} from 'react-dom'; + +// import Editor from '@ali/lowcode-engine-editor'; +import { Loading, ConfigProvider } from '@alifd/next'; +import defaultConfig from './config/skeleton'; + +import TopArea from './layouts/TopArea'; +import LeftArea from './layouts/LeftArea'; +import CenterArea from './layouts/CenterArea'; +import RightArea from './layouts/RightArea'; import './global.scss'; -import router from './router'; +export default class Skeleton extends PureComponent { + static displayName = 'lowcodeEditorSkeleton'; -const ICE_CONTAINER = document.getElementById('ice-container'); + constructor(props) { + super(props); + // this.editor = new Editor(props.config, props.utils); + } -if (!ICE_CONTAINER) { - throw new Error('当前页面不存在
节点.'); + componentWillUnmount() { + // this.editor && this.editor.destroy(); + // this.editor = null; + } + + render() { + const { location, history, messages } = this.props; + + return ( + + +
+ {/* +
+ + + + +
*/} +
+
+
+ ); + } } - -ReactDOM.render(router(), ICE_CONTAINER); diff --git a/packages/editor-skeleton/src/layouts/BasicLayout/index.tsx b/packages/editor-skeleton/src/layouts/BasicLayout/index.tsx deleted file mode 100644 index b45f3c44e..000000000 --- a/packages/editor-skeleton/src/layouts/BasicLayout/index.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; - -export default function BasicLayout({ children }) { - return ( -
- {children} -
- ); -} diff --git a/packages/editor-skeleton/src/layouts/CenterArea/index.scss b/packages/editor-skeleton/src/layouts/CenterArea/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/src/layouts/CenterArea/index.tsx b/packages/editor-skeleton/src/layouts/CenterArea/index.tsx new file mode 100644 index 000000000..36263969c --- /dev/null +++ b/packages/editor-skeleton/src/layouts/CenterArea/index.tsx @@ -0,0 +1,17 @@ +import React, {PureComponent} from 'react'; + +import './index.scss'; + +export default class CenterArea extends PureComponent { + static displayName = 'lowcodeCenterArea'; + + constructor(props) { + super(props); + } + + render() { + return ( +
+ ); + } +} \ No newline at end of file diff --git a/packages/editor-skeleton/src/layouts/LeftArea/index.scss b/packages/editor-skeleton/src/layouts/LeftArea/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/src/layouts/LeftArea/index.tsx b/packages/editor-skeleton/src/layouts/LeftArea/index.tsx new file mode 100644 index 000000000..f77a9b830 --- /dev/null +++ b/packages/editor-skeleton/src/layouts/LeftArea/index.tsx @@ -0,0 +1,7 @@ +import Nav from './nav'; +import Panel from './panel'; + +export default { + Nav, + Panel +}; \ No newline at end of file diff --git a/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx b/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx new file mode 100644 index 000000000..5c1fbea92 --- /dev/null +++ b/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx @@ -0,0 +1,17 @@ +import React, {PureComponent} from 'react'; + +import './index.scss'; + +export default class LeftAreaPanel extends PureComponent { + static displayName = 'lowcodeLeftAreaNav'; + + constructor(props) { + super(props); + } + + render() { + return ( +
+ ); + } +} \ No newline at end of file diff --git a/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx b/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx new file mode 100644 index 000000000..c378ad929 --- /dev/null +++ b/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx @@ -0,0 +1,17 @@ +import React, {PureComponent} from 'react'; + +import './index.scss'; + +export default class LeftAreaPanel extends PureComponent { + static displayName = 'lowcodeLeftAreaPanel'; + + constructor(props) { + super(props); + } + + render() { + return ( +
+ ); + } +} \ No newline at end of file diff --git a/packages/editor-skeleton/src/layouts/RightArea/index.scss b/packages/editor-skeleton/src/layouts/RightArea/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/src/layouts/RightArea/index.tsx b/packages/editor-skeleton/src/layouts/RightArea/index.tsx new file mode 100644 index 000000000..449a273e5 --- /dev/null +++ b/packages/editor-skeleton/src/layouts/RightArea/index.tsx @@ -0,0 +1,17 @@ +import React, {PureComponent} from 'react'; + +import './index.scss'; + +export default class RightArea extends PureComponent { + static displayName = 'lowcodeRightArea'; + + constructor(props) { + super(props); + } + + render() { + return ( +
+ ); + } +} \ No newline at end of file diff --git a/packages/editor-skeleton/src/layouts/TopArea/index.scss b/packages/editor-skeleton/src/layouts/TopArea/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor-skeleton/src/layouts/TopArea/index.tsx b/packages/editor-skeleton/src/layouts/TopArea/index.tsx new file mode 100644 index 000000000..3bc7e3562 --- /dev/null +++ b/packages/editor-skeleton/src/layouts/TopArea/index.tsx @@ -0,0 +1,17 @@ +import React, {PureComponent} from 'react'; + +import './index.scss'; + +export default class TopArea extends PureComponent { + static displayName = 'lowcodeTopArea'; + + constructor(props) { + super(props); + } + + render() { + return ( +
+ ); + } +} \ No newline at end of file diff --git a/packages/editor-skeleton/src/locale/en-US.js b/packages/editor-skeleton/src/locale/en-US.js new file mode 100644 index 000000000..936701e33 --- /dev/null +++ b/packages/editor-skeleton/src/locale/en-US.js @@ -0,0 +1,10 @@ +export default { + loading: 'loading...', + rejectRedirect: 'Redirect is not allowed', + expand: 'Unfold', + fold: 'Fold', + pageNotExist: 'The current Page not exist', + enterFromAppCenter: 'Please enter from the app center', + noPermission: 'Sorry, you do not have the develop permission', + getPermission: 'Please connect the app owners {owners} to get the permission' +}; diff --git a/packages/editor-skeleton/src/locale/ja-JP.js b/packages/editor-skeleton/src/locale/ja-JP.js new file mode 100644 index 000000000..ff8b4c563 --- /dev/null +++ b/packages/editor-skeleton/src/locale/ja-JP.js @@ -0,0 +1 @@ +export default {}; diff --git a/packages/editor-skeleton/src/locale/zh-CN.js b/packages/editor-skeleton/src/locale/zh-CN.js new file mode 100644 index 000000000..efe4ea898 --- /dev/null +++ b/packages/editor-skeleton/src/locale/zh-CN.js @@ -0,0 +1,10 @@ +export default { + loading: '加载中...', + rejectRedirect: '开发中,已阻止发生跳转', + expand: '展开', + fold: '收起', + pageNotExist: '当前访问地址不存在', + enterFromAppCenter: '请从应用中心入口重新进入', + noPermission: '抱歉,您暂无开发权限', + getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限' +}; diff --git a/packages/editor-skeleton/src/locale/zh-TW.js b/packages/editor-skeleton/src/locale/zh-TW.js new file mode 100644 index 000000000..ff8b4c563 --- /dev/null +++ b/packages/editor-skeleton/src/locale/zh-TW.js @@ -0,0 +1 @@ +export default {}; diff --git a/packages/editor-skeleton/src/pages/Dashboard/index.tsx b/packages/editor-skeleton/src/pages/Dashboard/index.tsx deleted file mode 100644 index 2130cc8ce..000000000 --- a/packages/editor-skeleton/src/pages/Dashboard/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import Guide from '@/components/Guide'; -import Greeting from '@/components/Greeting'; - -export default function Dashboard() { - return ( -
- - -
- ); -} diff --git a/packages/editor-skeleton/src/router.tsx b/packages/editor-skeleton/src/router.tsx deleted file mode 100644 index e260e72c8..000000000 --- a/packages/editor-skeleton/src/router.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom'; -import path from 'path'; -import routes from '@/config/routes'; - -const RouteItem = (props) => { - const { redirect, path: routePath, component, key } = props; - if (redirect) { - return ( - - ); - } - return ( - - ); -}; - -const router = () => { - return ( - - - {routes.map((route, id) => { - const { component: RouteComponent, children, ...others } = route; - return ( - { - return ( - children ? ( - - - {children.map((routeChild, idx) => { - const { redirect, path: childPath, component } = routeChild; - return RouteItem({ - key: `${id}-${idx}`, - redirect, - path: childPath && path.join(route.path, childPath), - component, - }); - })} - - - ) : ( - <> - { - RouteItem({ - key: id, - ...route, - }) - } - - ) - ); - }} - /> - ); - })} - - - ); -}; - -export default router; diff --git a/packages/editor-skeleton/tsconfig.json b/packages/editor-skeleton/tsconfig.json index 3f5e62810..a511d68ba 100644 --- a/packages/editor-skeleton/tsconfig.json +++ b/packages/editor-skeleton/tsconfig.json @@ -2,30 +2,20 @@ "compileOnSave": false, "buildOnSave": false, "compilerOptions": { - "baseUrl": ".", "outDir": "build", "module": "esnext", "target": "es6", "jsx": "react", "moduleResolution": "node", - "allowSyntheticDefaultImports": true, "lib": ["es6", "dom"], "sourceMap": true, "allowJs": true, - "rootDir": "src", - "forceConsistentCasingInFileNames": true, + "noUnusedLocals": true, "noImplicitReturns": true, "noImplicitThis": true, - "noImplicitAny": false, - "importHelpers": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true, - "skipLibCheck": true, - "paths": { - "@/*": ["./src/*"] - } + "noImplicitAny": true, + "skipLibCheck": true }, - "include": ["src/*"], + "include": ["src/*.ts", "src/*.tsx"], "exclude": ["node_modules", "build", "public"] } diff --git a/packages/editor/.editorconfig b/packages/editor/.editorconfig new file mode 100644 index 000000000..5760be583 --- /dev/null +++ b/packages/editor/.editorconfig @@ -0,0 +1,12 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/packages/editor/.eslintignore b/packages/editor/.eslintignore new file mode 100644 index 000000000..3b437e614 --- /dev/null +++ b/packages/editor/.eslintignore @@ -0,0 +1,11 @@ +# 忽略目录 +build/ +tests/ +demo/ + +# node 覆盖率文件 +coverage/ + +# 忽略文件 +**/*-min.js +**/*.min.js diff --git a/packages/editor/.eslintrc.js b/packages/editor/.eslintrc.js new file mode 100644 index 000000000..18ae6baa7 --- /dev/null +++ b/packages/editor/.eslintrc.js @@ -0,0 +1,7 @@ +const { eslint, deepmerge } = require('@ice/spec'); + +module.exports = deepmerge(eslint, { + rules: { + "global-require": 0, + }, +}); diff --git a/packages/editor/.gitignore b/packages/editor/.gitignore new file mode 100644 index 000000000..c590da5b0 --- /dev/null +++ b/packages/editor/.gitignore @@ -0,0 +1,20 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +/node_modules + +# production +/build +/dist + +# misc +.idea/ +.happypack +.DS_Store + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# ignore d.ts auto generated by css-modules-typescript-loader +*.module.scss.d.ts \ No newline at end of file diff --git a/packages/editor/.stylelintignore b/packages/editor/.stylelintignore new file mode 100644 index 000000000..82af6f60d --- /dev/null +++ b/packages/editor/.stylelintignore @@ -0,0 +1,7 @@ +# 忽略目录 +build/ +tests/ +demo/ + +# node 覆盖率文件 +coverage/ diff --git a/packages/editor/.stylelintrc.js b/packages/editor/.stylelintrc.js new file mode 100644 index 000000000..eeb605b33 --- /dev/null +++ b/packages/editor/.stylelintrc.js @@ -0,0 +1,3 @@ +const { stylelint } = require('@ice/spec'); + +module.exports = stylelint; diff --git a/packages/editor/README.md b/packages/editor/README.md new file mode 100644 index 000000000..6a1a5d0fa --- /dev/null +++ b/packages/editor/README.md @@ -0,0 +1,20 @@ +# ice-typescript-starter + +## 使用 + +- 启动调试服务: `npm start` +- 构建 dist: `npm run build` + +## 目录结构 + +- 入口文件: `src/index.jsx` +- 导航配置: `src/config/menu.js` +- 路由配置: `src/config/routes.js` +- 路由入口: `src/router.jsx` +- 布局文件: `src/layouts` +- 通用组件: `src/components` +- 页面文件: `src/pages` + +## 效果图 + +![screenshot](https://img.alicdn.com/tfs/TB13AFlH6TpK1RjSZKPXXa3UpXa-2860-1580.png) diff --git a/packages/editor/abc.json b/packages/editor/abc.json new file mode 100644 index 000000000..dce1f92ed --- /dev/null +++ b/packages/editor/abc.json @@ -0,0 +1,4 @@ +{ + "type": "ice-scripts", + "builder": "@ali/builder-ice-scripts" +} diff --git a/packages/editor-skeleton/ice.config.js b/packages/editor/ice.config.js similarity index 100% rename from packages/editor-skeleton/ice.config.js rename to packages/editor/ice.config.js diff --git a/packages/editor/jsconfig.json b/packages/editor/jsconfig.json new file mode 100644 index 000000000..9e0f3c03d --- /dev/null +++ b/packages/editor/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "jsx": "react", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/packages/editor/package.json b/packages/editor/package.json new file mode 100644 index 000000000..5f31e94e0 --- /dev/null +++ b/packages/editor/package.json @@ -0,0 +1,46 @@ +{ + "name": "@icedesign/ts-scaffold", + "version": "0.0.1", + "description": "低代码编辑器", + "dependencies": { + "@alifd/next": "^1.x", + "@icedesign/theme": "^1.x", + "@types/react": "^16.8.3", + "@types/react-dom": "^16.8.2", + "moment": "^2.23.0", + "prop-types": "^15.5.8", + "react": "^16.4.1", + "react-dom": "^16.4.1", + "react-router-dom": "^5.1.2" + }, + "devDependencies": { + "@ice/spec": "^0.1.1", + "css-modules-typescript-loader": "^2.0.4", + "eslint": "^6.0.1", + "ice-plugin-fusion": "^0.1.4", + "ice-plugin-moment-locales": "^0.1.0", + "ice-scripts": "^2.0.0", + "stylelint": "^10.1.0" + }, + "scripts": { + "start": "ice-scripts dev", + "build": "ice-scripts build", + "lint": "npm run eslint && npm run stylelint", + "eslint": "eslint --cache --ext .js,.jsx ./", + "stylelint": "stylelint ./**/*.scss" + }, + "engines": { + "node": ">=8.0.0" + }, + "iceworks": { + "type": "react", + "adapter": "adapter-react-v3" + }, + "ideMode": { + "name": "ice-react" + }, + "repository": { + "type": "git", + "url": "https://github.com/ice-lab/react-materials/tree/master/scaffolds/ice-ts" + } +} diff --git a/packages/editor-skeleton/public/favicon.png b/packages/editor/public/favicon.png similarity index 100% rename from packages/editor-skeleton/public/favicon.png rename to packages/editor/public/favicon.png diff --git a/packages/editor-skeleton/public/index.html b/packages/editor/public/index.html similarity index 100% rename from packages/editor-skeleton/public/index.html rename to packages/editor/public/index.html diff --git a/packages/editor/src/config/components.js b/packages/editor/src/config/components.js new file mode 100644 index 000000000..96acc32a7 --- /dev/null +++ b/packages/editor/src/config/components.js @@ -0,0 +1,3 @@ + +export default { +}; \ No newline at end of file diff --git a/packages/editor/src/config/constants.js b/packages/editor/src/config/constants.js new file mode 100644 index 000000000..1c713fc4e --- /dev/null +++ b/packages/editor/src/config/constants.js @@ -0,0 +1,3 @@ +export default { + "namespace": "page" +} \ No newline at end of file diff --git a/packages/editor/src/config/locale/en-US.js b/packages/editor/src/config/locale/en-US.js new file mode 100644 index 000000000..7c645e42f --- /dev/null +++ b/packages/editor/src/config/locale/en-US.js @@ -0,0 +1 @@ +export default {}; \ No newline at end of file diff --git a/packages/editor/src/config/locale/index.js b/packages/editor/src/config/locale/index.js new file mode 100644 index 000000000..eaa4da099 --- /dev/null +++ b/packages/editor/src/config/locale/index.js @@ -0,0 +1,10 @@ +import en_us from './en-US'; +import zh_cn from './zh-CN'; +import zh_tw from './zh-TW'; +import ja_jp from './ja-JP'; +export default { + 'en-US': en_us, + 'zh-CN': zh_cn, + 'zh-TW': zh_tw, + 'ja-JP': ja_jp +}; \ No newline at end of file diff --git a/packages/editor/src/config/locale/ja-JP.js b/packages/editor/src/config/locale/ja-JP.js new file mode 100644 index 000000000..7c645e42f --- /dev/null +++ b/packages/editor/src/config/locale/ja-JP.js @@ -0,0 +1 @@ +export default {}; \ No newline at end of file diff --git a/packages/editor/src/config/locale/zh-CN.js b/packages/editor/src/config/locale/zh-CN.js new file mode 100644 index 000000000..7c645e42f --- /dev/null +++ b/packages/editor/src/config/locale/zh-CN.js @@ -0,0 +1 @@ +export default {}; \ No newline at end of file diff --git a/packages/editor/src/config/locale/zh-TW.js b/packages/editor/src/config/locale/zh-TW.js new file mode 100644 index 000000000..7c645e42f --- /dev/null +++ b/packages/editor/src/config/locale/zh-TW.js @@ -0,0 +1 @@ +export default {}; \ No newline at end of file diff --git a/packages/editor/src/config/skeleton.js b/packages/editor/src/config/skeleton.js new file mode 100644 index 000000000..3b3841521 --- /dev/null +++ b/packages/editor/src/config/skeleton.js @@ -0,0 +1,435 @@ +export default { + "skeleton": { + "config": { + "package": "@ali/lowcode-skeleton", + "version": "0.0.1" + }, + }, + "theme": { + "fusion": { + "package": "@alife/dpl-iceluna", + "version": "^2.3.0" + }, + "scss": "" + }, + "constants": { + "namespace": "page" + }, + "utils": [], + "plugins": { + "topArea": [{ + "pluginKey": "logo", + "type": "Custom", + "props": { + "width": 110, + "align": "left" + }, + "config": { + "package": "@ali/iceluna-addon-logo", + "version": "^1.0.2" + }, + "pluginProps": {} + }, { + "pluginKey": "divider", + "type": "Divider", + "props": { + "align": "left" + } + }, { + "pluginKey": "pageList", + "type": "Custom", + "props": { + "align": "left", + "width": 360 + }, + "config": { + "package": "@ali/iceluna-addon-page-list", + "version": "^1.0.11" + }, + "pluginProps": {} + }, { + "pluginKey": "partner", + "type": "Custom", + "props": { + "align": "right", + "width": 200 + }, + "config": { + "package": "@ali/iceluna-addon-partner", + "version": "^1.0.3" + }, + "pluginProps": {} + }, { + "pluginKey": "divider", + "type": "Divider", + "props": { + "align": "right" + } + }, { + "pluginKey": "designMode", + "type": "Custom", + "props": { + "align": "right", + "width": 144 + }, + "config": { + "package": "@ali/iceluna-addon-design-mode", + "version": "^1.0.3" + }, + "pluginProps": {} + }, { + "pluginKey": "divider", + "type": "Divider", + "props": { + "align": "right" + } + }, { + "pluginKey": "undoRedo", + "type": "Custom", + "props": { + "align": "right", + "width": 88 + }, + "config": { + "package": "@ali/iceluna-addon-undo-redo", + "version": "^1.0.3" + }, + "pluginProps": {} + }, { + "pluginKey": "d2c", + "type": "Custom", + "props": { + "align": "right", + "width": 44 + }, + "config": { + "package": "@ali/iceluna-addon-d2c", + "version": "^1.0.1" + }, + "pluginProps": {} + }, { + "pluginKey": "history", + "type": "DialogIcon", + "props": { + "align": "right", + "icon": "lishijilu1", + "title": "历史", + "dialogProps": { + "title": "历史记录", + "footer": false, + "shouldUpdatePosition": true + } + }, + "config": { + "package": "@ali/iceluna-addon-history", + "version": "^1.0.3" + }, + "pluginProps": {} + }, { + "pluginKey": "refresh", + "type": "Icon", + "props": { + "align": "right", + "icon": "shuaxin", + "title": "刷新", + "onClick": function(appHelper) { + appHelper.emit('ide.reset'); + } + } + }, { + "pluginKey": "divider", + "type": "Divider", + "props": { + "align": "right" + } + }, { + "pluginKey": "save", + "type": "Custom", + "props": { + "align": "right", + "width": 86 + }, + "config": { + "package": "@ali/iceluna-addon-save", + "version": "^1.0.3" + }, + "pluginProps": {} + }, { + "pluginKey": "preview", + "type": "Custom", + "props": { + "align": "right", + "width": 86 + }, + "config": { + "package": "@ali/iceluna-addon-preview", + "version": "^1.0.1" + }, + "pluginProps": {} + }, { + "pluginKey": "publish", + "type": "Custom", + "props": { + "align": "right", + "width": 104 + }, + "config": { + "package": "@ali/iceluna-addon-publish", + "version": "^1.0.1" + }, + "pluginProps": {} + }], + "leftArea": [{ + "pluginKey": "componentTree", + "type": "PanelIcon", + "props": { + "align": "top", + "icon": "shuxingkongjian", + "title": "组件树", + "panelProps": { + "minWidth": 100, + "maxWidth": 500 + } + }, + "config": { + "package": "@ali/iceluna-addon-component-tree", + "version": "^1.0.5" + }, + "pluginProps": {} + }, { + "pluginKey": "componentList", + "type": "PanelIcon", + "props": { + "align": "top", + "icon": "zujianku", + "title": "组件库" + }, + "config": { + "package": "@ali/iceluna-addon-component-list", + "version": "^1.0.4" + }, + "pluginProps": {} + }, { + "pluginKey": "blockList", + "type": "PanelIcon", + "props": { + "align": "top", + "icon": "jihe", + "title": "区块库" + }, + "config": { + "package": "@ali/iceluna-addon-block-list", + "version": "^1.0.2" + }, + "pluginProps": {} + }, { + "pluginKey": "schema", + "type": "PanelIcon", + "props": { + "align": "bottom", + "icon": "ceshi", + "title": "schema 源码开发", + "panelProps": { + "defaultWidth": 480 + } + }, + "config": { + "package": "@ali/iceluna-addon-schema", + "version": "^1.0.3" + }, + "pluginProps": {} + }, { + "pluginKey": "style", + "type": "PanelIcon", + "props": { + "align": "bottom", + "icon": "SCSS", + "title": "scss 全局样式设置", + "panelProps": { + "defaultWidth": 480 + } + }, + "config": { + "package": "@ali/iceluna-addon-style", + "version": "^1.0.2" + }, + "pluginProps": {} + }, { + "pluginKey": "utils", + "type": "PanelIcon", + "props": { + "align": "bottom", + "icon": "funcsgaiban", + "title": "utils 全局公共函数设置", + "panelProps": { + "defaultWidth": 540 + } + }, + "config": { + "package": "@ali/iceluna-addon-utils", + "version": "^1.0.7" + }, + "pluginProps": {} + }, { + "pluginKey": "constants", + "type": "PanelIcon", + "props": { + "align": "bottom", + "icon": "constgaiban", + "title": "constants 全局常量设置", + "panelProps": { + "defaultWidth": 480 + } + }, + "config": { + "package": "@ali/iceluna-addon-constants", + "version": "^1.0.3" + }, + "pluginProps": {} + }, { + "pluginKey": "package", + "type": "PanelIcon", + "props": { + "align": "bottom", + "icon": "packagegaiban", + "title": "package.json 应用设置", + "panelProps": { + "defaultWidth": 480 + } + }, + "config": { + "package": "@ali/iceluna-addon-package", + "version": "^1.0.2" + }, + "pluginProps": {} + }, { + "pluginKey": "canvasSetting", + "type": "PanelIcon", + "props": { + "align": "bottom", + "icon": "huabushezhi", + "title": "canvas 画布配置", + "panelProps": { + "defaultWidth": 300 + } + }, + "config": { + "package": "@ali/iceluna-addon-canvas-setting", + "version": "^1.0.2" + }, + "pluginProps": {} + }, { + "pluginKey": "issue", + "type": "LinkIcon", + "props": { + "align": "bottom", + "icon": "chongzi", + "title": "issue 问题反馈", + "linkProps": { + "href": "//work.aone.alibaba-inc.com/project/860698/issue/new", + "target": "blank" + } + } + }, { + "pluginKey": "document", + "type": "LinkIcon", + "props": { + "align": "bottom", + "icon": "wendangzhongxin", + "title": "docs 文档中心", + "linkProps": { + "href": "https://iceluna.alibaba-inc.com/#/document", + "target": "blank" + } + } + }], + "rightArea": [{ + "pluginKey": "componentStyle", + "props": { + "title": "样式" + }, + "config": { + "package": "@ali/iceluna-addon-component-style", + "version": "^1.0.8" + } + }, { + "pluginKey": "componentAttr", + "props": { + "title": "属性" + }, + "config": { + "package": "@ali/iceluna-addon-component-attr", + "version": "^1.0.3" + }, + "pluginProps": {} + }, { + "pluginKey": "componentEvent", + "props": { + "title": "事件" + }, + "config": { + "package": "@ali/iceluna-addon-component-event", + "version": "^1.0.4" + }, + "pluginProps": {} + }, { + "pluginKey": "componentData", + "props": { + "title": "数据" + }, + "config": { + "package": "@ali/iceluna-addon-component-data", + "version": "^1.0.3" + }, + "pluginProps": {} + }], + "centerArea": [{ + "pluginKey": "canvas", + "config": { + "package": "@ali/iceluna-addon-canvas", + "version": "^1.0.8" + } + }, { + "pluginKey": "guide", + "config": { + "package": "@ali/iceluna-addon-guide", + "version": "^1.0.1" + } + }] + }, + "hooks": [{ + "message": "wsHelper.result.updateInfo", + "type": "on", + "handler": function(appHelper, data) { + const pageInfo = appHelper.pageInfo; + if (data && data.code > 0 && pageInfo) { + const { + clientLocks, + entityLocks, + entityUsers, + entityPubInfo + } = data.data; + if (JSON.stringify(clientLocks || {}) !== JSON.stringify(appHelper.clientLocks || {})) { + clientLocks.schema = clientLocks[pageInfo.id]; + appHelper.set('clientLocks', clientLocks); + appHelper.emit('wsHelper.update.clientLocks', clientLocks); + } + if (JSON.stringify(entityLocks || {}) !== JSON.stringify(appHelper.entityLocks || {})) { + entityLocks.schema = entityLocks[pageInfo.id]; + appHelper.set('entityLocks', entityLocks); + appHelper.emit('wsHelper.update.entityLocks', entityLocks); + } + if (JSON.stringify(entityUsers || {}) !== JSON.stringify(appHelper.entityUsers || {})) { + appHelper.set('entityUsers', entityUsers); + appHelper.emit('wsHelper.update.entityUsers', entityUsers); + } + if (JSON.stringify(entityPubInfo || {}) !== JSON.stringify(appHelper.entityPubInfo || {})) { + appHelper.set('entityPubInfo', entityPubInfo); + appHelper.emit('wsHelper.update.entityPubInfo', entityPubInfo); + } + } + } + }], + "shortCuts": [], + "lifeCycles": {} +}; \ No newline at end of file diff --git a/packages/editor/src/config/theme.scss b/packages/editor/src/config/theme.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor/src/config/utils.js b/packages/editor/src/config/utils.js new file mode 100644 index 000000000..dd651ca49 --- /dev/null +++ b/packages/editor/src/config/utils.js @@ -0,0 +1,3 @@ +export default { + +}; \ No newline at end of file diff --git a/packages/editor/src/global.scss b/packages/editor/src/global.scss new file mode 100644 index 000000000..e827d91b7 --- /dev/null +++ b/packages/editor/src/global.scss @@ -0,0 +1,8 @@ +body { + font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, Arial, PingFang SC-Light, + Microsoft YaHei; + font-size: 12px; + * { + box-sizing: border-box; + } +} \ No newline at end of file diff --git a/packages/editor/src/index.tsx b/packages/editor/src/index.tsx new file mode 100644 index 000000000..f4b8bc855 --- /dev/null +++ b/packages/editor/src/index.tsx @@ -0,0 +1,42 @@ +import ReactDOM from 'react-dom'; +import { HashRouter as Router, Route } from 'react-router-dom'; +import Skeleton from '@ali/lowcode-engine-skeleton'; + +import config from './config/skeleton'; +import components from './config/components'; +import componentsMap from './config/componentsMap'; +import utils from './config/utils'; +import constants from './config/constants'; +import messages from './config/locale'; + +import pkg from '../package.json'; +import './global.scss'; +import './config/theme.scss'; + +window.__pkg = pkg; + +const ICE_CONTAINER = document.getElementById('ice-container'); + +if (!ICE_CONTAINER) { + throw new Error('当前页面不存在
节点.'); +} + +ReactDOM.render( + + { + return ( + + ); + }} + /> + , ICE_CONTAINER); diff --git a/packages/editor/tests/index.js b/packages/editor/tests/index.js new file mode 100644 index 000000000..346e384d2 --- /dev/null +++ b/packages/editor/tests/index.js @@ -0,0 +1 @@ +// test file diff --git a/packages/editor/tsconfig.json b/packages/editor/tsconfig.json new file mode 100644 index 000000000..3f5e62810 --- /dev/null +++ b/packages/editor/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compileOnSave": false, + "buildOnSave": false, + "compilerOptions": { + "baseUrl": ".", + "outDir": "build", + "module": "esnext", + "target": "es6", + "jsx": "react", + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "lib": ["es6", "dom"], + "sourceMap": true, + "allowJs": true, + "rootDir": "src", + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": false, + "importHelpers": true, + "strictNullChecks": true, + "suppressImplicitAnyIndexErrors": true, + "noUnusedLocals": true, + "skipLibCheck": true, + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src/*"], + "exclude": ["node_modules", "build", "public"] +} From c9d139bf302e8cfc2eb29a14ec6de33a87bc6203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8B=E7=BE=8A?= Date: Sun, 8 Mar 2020 16:32:25 +0800 Subject: [PATCH 5/9] daily tag --- packages/editor-framework/package.json | 8 +- packages/editor-framework/src/definitions.ts | 48 ++ packages/editor-framework/src/plugin.ts | 42 ++ packages/editor-framework/src/utils.ts | 27 +- .../es/components/LeftPlugin/index.d.ts | 30 + .../es/components/LeftPlugin/index.js | 259 +++++++ .../es/components/LeftPlugin/index.scss | 59 ++ .../es/components/TopIcon/index.d.ts | 34 +- .../es/components/TopIcon/index.js | 86 ++- .../es/components/TopIcon/index.scss | 32 + .../es/components/TopPlugin/index.d.ts | 21 + .../es/components/TopPlugin/index.js | 213 ++++++ .../es/components/TopPlugin/index.scss | 2 + packages/editor-skeleton/es/global.scss | 14 +- packages/editor-skeleton/es/index.d.ts | 3 +- packages/editor-skeleton/es/index.js | 43 +- .../es/layouts/CenterArea/index.scss | 3 + .../es/layouts/LeftArea/index.scss | 21 + .../es/layouts/LeftArea/panel.js | 2 +- .../es/layouts/RightArea/index.scss | 157 +++++ .../es/layouts/TopArea/index.d.ts | 4 + .../es/layouts/TopArea/index.js | 63 +- .../es/layouts/TopArea/index.scss | 5 + packages/editor-skeleton/es/style.js | 8 +- packages/editor-skeleton/package.json | 39 +- .../src/components/LeftPlugin/index.scss | 59 ++ .../src/components/LeftPlugin/index.tsx | 223 ++++++ .../src/components/TopIcon/index.scss | 32 + .../src/components/TopIcon/index.tsx | 79 ++- .../src/components/TopPlugin/index.scss | 2 + .../src/components/TopPlugin/index.tsx | 174 +++++ .../editor-skeleton/src/config/skeleton.ts | 2 +- packages/editor-skeleton/src/global.scss | 14 +- packages/editor-skeleton/src/index.tsx | 30 +- .../src/layouts/CenterArea/index.scss | 3 + .../src/layouts/CenterArea/index.tsx | 8 +- .../src/layouts/LeftArea/index.scss | 21 + .../src/layouts/LeftArea/index.tsx | 4 +- .../src/layouts/LeftArea/nav.tsx | 8 +- .../src/layouts/LeftArea/panel.tsx | 8 +- .../src/layouts/RightArea/index.scss | 157 +++++ .../src/layouts/RightArea/index.tsx | 8 +- .../src/layouts/TopArea/index.scss | 5 + .../src/layouts/TopArea/index.tsx | 70 +- packages/editor-skeleton/src/locale/en-US.js | 2 +- packages/editor-skeleton/src/locale/zh-CN.js | 2 +- packages/editor/ice.config.js | 2 +- packages/editor/package.json | 6 +- packages/editor/src/config/components.js | 23 +- packages/editor/src/config/constants.js | 4 +- packages/editor/src/config/locale/en-US.js | 2 +- packages/editor/src/config/locale/index.js | 4 +- packages/editor/src/config/locale/ja-JP.js | 2 +- packages/editor/src/config/locale/zh-CN.js | 2 +- packages/editor/src/config/locale/zh-TW.js | 2 +- packages/editor/src/config/skeleton.js | 640 ++++++------------ packages/editor/src/config/utils.js | 4 +- packages/editor/src/framework/context.ts | 3 + packages/editor/src/framework/definitions.ts | 41 ++ packages/editor/src/framework/editor.ts | 186 +++++ packages/editor/src/framework/index.ts | 3 + packages/editor/src/framework/plugin.js | 54 ++ packages/editor/src/framework/utils.ts | 320 +++++++++ packages/editor/src/global.scss | 6 +- packages/editor/src/index.tsx | 34 +- .../skeleton/components/LeftPlugin/index.scss | 59 ++ .../skeleton/components/LeftPlugin/index.tsx | 205 ++++++ .../src/skeleton/components/Panel/index.scss | 52 ++ .../src/skeleton/components/Panel/index.tsx | 23 + .../skeleton/components/TopIcon/index.scss | 32 + .../src/skeleton/components/TopIcon/index.tsx | 68 ++ .../skeleton/components/TopPlugin/index.scss | 2 + .../skeleton/components/TopPlugin/index.tsx | 192 ++++++ .../editor/src/skeleton/config/skeleton.ts | 1 + packages/editor/src/skeleton/config/utils.ts | 1 + packages/editor/src/skeleton/global.scss | 33 + packages/editor/src/skeleton/index.tsx | 129 ++++ .../skeleton/layouts/CenterArea/index.scss | 3 + .../src/skeleton/layouts/CenterArea/index.tsx | 33 + .../src/skeleton/layouts/LeftArea/index.scss | 21 + .../src/skeleton/layouts/LeftArea/index.tsx | 7 + .../src/skeleton/layouts/LeftArea/nav.tsx | 51 ++ .../src/skeleton/layouts/LeftArea/panel.tsx | 39 ++ .../src/skeleton/layouts/RightArea/index.scss | 157 +++++ .../src/skeleton/layouts/RightArea/index.tsx | 53 ++ .../src/skeleton/layouts/TopArea/index.scss | 27 + .../src/skeleton/layouts/TopArea/index.tsx | 80 +++ packages/editor/src/skeleton/locale/en-US.js | 10 + packages/editor/src/skeleton/locale/ja-JP.js | 1 + packages/editor/src/skeleton/locale/zh-CN.js | 10 + packages/editor/src/skeleton/locale/zh-TW.js | 1 + 91 files changed, 4182 insertions(+), 580 deletions(-) create mode 100644 packages/editor-framework/src/definitions.ts create mode 100644 packages/editor-skeleton/es/components/LeftPlugin/index.d.ts create mode 100644 packages/editor-skeleton/es/components/TopIcon/index.scss create mode 100644 packages/editor-skeleton/es/components/TopPlugin/index.d.ts create mode 100644 packages/editor-skeleton/src/components/TopIcon/index.scss create mode 100644 packages/editor/src/framework/context.ts create mode 100644 packages/editor/src/framework/definitions.ts create mode 100644 packages/editor/src/framework/editor.ts create mode 100644 packages/editor/src/framework/index.ts create mode 100644 packages/editor/src/framework/plugin.js create mode 100644 packages/editor/src/framework/utils.ts create mode 100644 packages/editor/src/skeleton/components/LeftPlugin/index.scss create mode 100644 packages/editor/src/skeleton/components/LeftPlugin/index.tsx create mode 100644 packages/editor/src/skeleton/components/Panel/index.scss create mode 100644 packages/editor/src/skeleton/components/Panel/index.tsx create mode 100644 packages/editor/src/skeleton/components/TopIcon/index.scss create mode 100644 packages/editor/src/skeleton/components/TopIcon/index.tsx create mode 100644 packages/editor/src/skeleton/components/TopPlugin/index.scss create mode 100644 packages/editor/src/skeleton/components/TopPlugin/index.tsx create mode 100644 packages/editor/src/skeleton/config/skeleton.ts create mode 100644 packages/editor/src/skeleton/config/utils.ts create mode 100644 packages/editor/src/skeleton/global.scss create mode 100644 packages/editor/src/skeleton/index.tsx create mode 100644 packages/editor/src/skeleton/layouts/CenterArea/index.scss create mode 100644 packages/editor/src/skeleton/layouts/CenterArea/index.tsx create mode 100644 packages/editor/src/skeleton/layouts/LeftArea/index.scss create mode 100644 packages/editor/src/skeleton/layouts/LeftArea/index.tsx create mode 100644 packages/editor/src/skeleton/layouts/LeftArea/nav.tsx create mode 100644 packages/editor/src/skeleton/layouts/LeftArea/panel.tsx create mode 100644 packages/editor/src/skeleton/layouts/RightArea/index.scss create mode 100644 packages/editor/src/skeleton/layouts/RightArea/index.tsx create mode 100644 packages/editor/src/skeleton/layouts/TopArea/index.scss create mode 100644 packages/editor/src/skeleton/layouts/TopArea/index.tsx create mode 100644 packages/editor/src/skeleton/locale/en-US.js create mode 100644 packages/editor/src/skeleton/locale/ja-JP.js create mode 100644 packages/editor/src/skeleton/locale/zh-CN.js create mode 100644 packages/editor/src/skeleton/locale/zh-TW.js diff --git a/packages/editor-framework/package.json b/packages/editor-framework/package.json index 2b11f2c53..3d1465113 100644 --- a/packages/editor-framework/package.json +++ b/packages/editor-framework/package.json @@ -38,16 +38,16 @@ "@types/lodash": "^4.14.149", "@types/react": "^16.9.13", "@types/react-dom": "^16.9.4", - "build-plugin-component": "^0.2.0", + "build-plugin-component": "^0.2.7-1", "build-plugin-fusion": "^0.1.0", "build-plugin-moment-locales": "^0.1.0", "eslint": "^6.0.1", "prettier": "^1.19.1", - "react": "^16.3.0", - "react-dom": "^16.3.0" + "react": "^16.8.0", + "react-dom": "^16.8.0" }, "peerDependencies": { - "react": "^16.3.0", + "react": "^16.8.0", "@alifd/next": "1.x" }, "license": "MIT", diff --git a/packages/editor-framework/src/definitions.ts b/packages/editor-framework/src/definitions.ts new file mode 100644 index 000000000..87dd5dca2 --- /dev/null +++ b/packages/editor-framework/src/definitions.ts @@ -0,0 +1,48 @@ + +export interface EditorConfig { + +}; + +export interface NpmConfig { + version: string, + package: string, + main?: string, + exportName?: string, + subName?: string, + destructuring?: boolean +}; + +export interface SkeletonConfig { + config: NpmConfig, + props?: object, + handler?: (EditorConfig) => EditorConfig +}; + +export interface FusionTheme { + package: string, + version: string +}; + +export interface ThemeConfig { + fusion?: FusionTheme +} + +export interface PluginsConfig { + [key]: Array +}; + +export interface PluginConfig { + pluginKey: string, + type: string, + props: object, + config: NpmConfig, + pluginProps: object +}; + +export type HooksConfig = Array; + +export interface HookConfig { + +}; + + diff --git a/packages/editor-framework/src/plugin.ts b/packages/editor-framework/src/plugin.ts index e42aa73cc..33d81cc48 100644 --- a/packages/editor-framework/src/plugin.ts +++ b/packages/editor-framework/src/plugin.ts @@ -10,6 +10,48 @@ export interface pluginProps { messages: object } +export default function plugin(Comp) { + + class Plugin extends PureComponent { + static displayName = 'lowcode-editor-plugin'; + static defaultProps = { + config: {} + }; + static contextType = EditorContext; + constructor(props, context) { + super(props, context); + if (isEmpty(props.config) || !props.config.pluginKey) { + console.warn('lowcode editor plugin has wrong config'); + return; + } + + const { locale, messages, editor } = props; + // 注册插件 + this.editor = editor; + this.i18n = generateI18n(locale, messages); + this.pluginKey = props.config.pluginKey; + editor.plugins = editor.plugins || {}; + editor.plugins[this.pluginKey] = this; + } + + componentWillUnmount() { + // 销毁插件 + if (this.editor && this.editor.plugins) { + delete this.editor.plugins[this.pluginKey]; + } + } + + render() { + const { + config + } = this.props; + return + } + } + + return Plugin; +} + export class Plugin extends PureComponent { diff --git a/packages/editor-framework/src/utils.ts b/packages/editor-framework/src/utils.ts index d09c4929c..d1c5eaf2b 100644 --- a/packages/editor-framework/src/utils.ts +++ b/packages/editor-framework/src/utils.ts @@ -213,5 +213,30 @@ export function transformToPromise(input) { } export function comboEditorConfig(defaultConfig, customConfig) { - + const { ideConfig = {}, utils = {} } = this.props; + const comboShortCuts = () => { + const defaultShortCuts = defaultIdeConfig.shortCuts; + const shortCuts = ideConfig.shortCuts || []; + const configMap = skeletonUtils.transformArrayToMap(defaultShortCuts, 'keyboard'); + (shortCuts || []).forEach(item => { + configMap[item.keyboard] = item; + }); + return Object.keys(configMap).map(key => configMap[key]); + }; + return { + ...ideConfig, + utils: { + ...skeletonUtils, + ...utils + }, + constants: { + ...defaultIdeConfig.constants, + ...ideConfig.constants + }, + extensions: { + ...defaultIdeConfig.extensions, + ...ideConfig.extensions + }, + shortCuts: comboShortCuts() + }; } \ No newline at end of file diff --git a/packages/editor-skeleton/es/components/LeftPlugin/index.d.ts b/packages/editor-skeleton/es/components/LeftPlugin/index.d.ts new file mode 100644 index 000000000..312fc40aa --- /dev/null +++ b/packages/editor-skeleton/es/components/LeftPlugin/index.d.ts @@ -0,0 +1,30 @@ +import { PureComponent } from 'react'; +import './index.scss'; +export default class LeftAddon extends PureComponent { + static displayName: string; + static propTypes: { + active: any; + config: any; + disabled: any; + dotted: any; + locked: any; + onClick: any; + }; + static defaultProps: { + active: boolean; + config: {}; + disabled: boolean; + dotted: boolean; + locked: boolean; + onClick: () => void; + }; + static contextType: any; + constructor(props: any, context: any); + componentDidMount(): void; + componentWillUnmount(): void; + handleClose: () => void; + handleOpen: () => void; + handleShow: () => void; + renderIcon: (clickCallback: any) => JSX.Element; + render(): JSX.Element; +} diff --git a/packages/editor-skeleton/es/components/LeftPlugin/index.js b/packages/editor-skeleton/es/components/LeftPlugin/index.js index e69de29bb..517104928 100644 --- a/packages/editor-skeleton/es/components/LeftPlugin/index.js +++ b/packages/editor-skeleton/es/components/LeftPlugin/index.js @@ -0,0 +1,259 @@ +import _extends from "@babel/runtime/helpers/extends"; +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; +import React, { PureComponent, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import AppContext from '@ali/iceluna-sdk/lib/context/appContext'; +import { Balloon, Dialog, Icon, Badge } from '@alife/next'; +import './index.scss'; + +var LeftAddon = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(LeftAddon, _PureComponent); + + function LeftAddon(_props, context) { + var _this; + + _this = _PureComponent.call(this, _props, context) || this; + + _this.handleClose = function () { + var addonKey = _this.props.config && _this.props.config.addonKey; + var currentAddon = _this.appHelper.addons && _this.appHelper.addons[addonKey]; + + if (currentAddon) { + _this.utils.transformToPromise(currentAddon.close()).then(function () { + _this.setState({ + dialogVisible: false + }); + }); + } + }; + + _this.handleOpen = function () { + // todo 对话框类型的插件初始时拿不到插件实例 + _this.setState({ + dialogVisible: true + }); + }; + + _this.handleShow = function () { + var _this$props = _this.props, + disabled = _this$props.disabled, + config = _this$props.config, + onClick = _this$props.onClick; + var addonKey = config && config.addonKey; + if (disabled || !addonKey) return; //考虑到弹窗情况,延时发送消息 + + setTimeout(function () { + return _this.appHelper.emit(addonKey + ".addon.activate"); + }, 0); + + _this.handleOpen(); + + onClick && onClick(); + }; + + _this.renderIcon = function (clickCallback) { + var _this$props2 = _this.props, + active = _this$props2.active, + disabled = _this$props2.disabled, + dotted = _this$props2.dotted, + locked = _this$props2.locked, + _onClick = _this$props2.onClick, + config = _this$props2.config; + + var _ref = config || {}, + addonKey = _ref.addonKey, + props = _ref.props; + + var _ref2 = props || {}, + icon = _ref2.icon, + title = _ref2.title; + + return React.createElement("div", { + className: classNames('luna-left-addon', addonKey, { + active: active, + disabled: disabled, + locked: locked + }), + "data-tooltip": title, + onClick: function onClick() { + if (disabled) return; //考虑到弹窗情况,延时发送消息 + + clickCallback && clickCallback(); + _onClick && _onClick(); + } + }, dotted ? React.createElement(Badge, { + dot: true + }, React.createElement(Icon, { + type: icon, + size: "small" + })) : React.createElement(Icon, { + type: icon, + size: "small" + })); + }; + + _this.state = { + dialogVisible: false + }; + _this.appHelper = context.appHelper; + _this.utils = _this.appHelper.utils; + _this.constants = _this.appHelper.constants; + return _this; + } + + var _proto = LeftAddon.prototype; + + _proto.componentDidMount = function componentDidMount() { + var config = this.props.config; + var addonKey = config && config.addonKey; + var appHelper = this.appHelper; + + if (appHelper && addonKey) { + appHelper.on(addonKey + ".dialog.show", this.handleShow); + appHelper.on(addonKey + ".dialog.close", this.handleClose); + } + }; + + _proto.componentWillUnmount = function componentWillUnmount() { + var config = this.props.config; + var appHelper = this.appHelper; + var addonKey = config && config.addonKey; + + if (appHelper && addonKey) { + appHelper.off(addonKey + ".dialog.show", this.handleShow); + appHelper.off(addonKey + ".dialog.close", this.handleClose); + } + }; + + _proto.render = function render() { + var _this2 = this; + + var _this$props3 = this.props, + dotted = _this$props3.dotted, + locked = _this$props3.locked, + active = _this$props3.active, + disabled = _this$props3.disabled, + config = _this$props3.config; + + var _ref3 = config || {}, + addonKey = _ref3.addonKey, + props = _ref3.props, + type = _ref3.type, + addonProps = _ref3.addonProps; + + var _ref4 = props || {}, + _onClick2 = _ref4.onClick, + title = _ref4.title; + + var dialogVisible = this.state.dialogVisible; + var _this$context = this.context, + appHelper = _this$context.appHelper, + components = _this$context.components; + if (!addonKey || !type || !props) return null; + var componentName = appHelper.utils.generateAddonCompName(addonKey); + var localeProps = {}; + var locale = appHelper.locale, + messages = appHelper.messages; + + if (locale) { + localeProps.locale = locale; + } + + if (messages && messages[componentName]) { + localeProps.messages = messages[componentName]; + } + + var AddonComp = components && components[componentName]; + var node = AddonComp && React.createElement(AddonComp, _extends({ + active: active, + locked: locked, + disabled: disabled, + config: config, + onClick: function onClick() { + _onClick2 && _onClick2.call(null, appHelper); + } + }, localeProps, addonProps || {})) || null; + + switch (type) { + case 'LinkIcon': + return React.createElement("a", props.linkProps || {}, this.renderIcon(function () { + _onClick2 && _onClick2.call(null, appHelper); + })); + + case 'Icon': + return this.renderIcon(function () { + _onClick2 && _onClick2.call(null, appHelper); + }); + + case 'DialogIcon': + return React.createElement(Fragment, null, this.renderIcon(function () { + _onClick2 && _onClick2.call(null, appHelper); + + _this2.handleOpen(); + }), React.createElement(Dialog, _extends({ + onOk: function onOk() { + appHelper.emit(addonKey + ".dialog.onOk"); + + _this2.handleClose(); + }, + onCancel: this.handleClose, + onClose: this.handleClose, + title: title + }, props.dialogProps || {}, { + visible: dialogVisible + }), node)); + + case 'BalloonIcon': + return React.createElement(Balloon, _extends({ + trigger: this.renderIcon(function () { + _onClick2 && _onClick2.call(null, appHelper); + }), + align: "r", + triggerType: ['click', 'hover'] + }, props.balloonProps || {}), node); + + case 'PanelIcon': + return this.renderIcon(function () { + _onClick2 && _onClick2.call(null, appHelper); + + _this2.handleOpen(); + }); + + case 'Custom': + return dotted ? React.createElement(Badge, { + dot: true + }, node) : node; + + default: + return null; + } + }; + + return LeftAddon; +}(PureComponent); + +LeftAddon.displayName = 'LunaLeftAddon'; +LeftAddon.propTypes = { + active: PropTypes.bool, + config: PropTypes.shape({ + addonKey: PropTypes.string, + addonProps: PropTypes.object, + props: PropTypes.object, + type: PropTypes.oneOf(['DialogIcon', 'BalloonIcon', 'PanelIcon', 'LinkIcon', 'Icon', 'Custom']) + }), + disabled: PropTypes.bool, + dotted: PropTypes.bool, + locked: PropTypes.bool, + onClick: PropTypes.func +}; +LeftAddon.defaultProps = { + active: false, + config: {}, + disabled: false, + dotted: false, + locked: false, + onClick: function onClick() {} +}; +LeftAddon.contextType = AppContext; +export { LeftAddon as default }; \ No newline at end of file diff --git a/packages/editor-skeleton/es/components/LeftPlugin/index.scss b/packages/editor-skeleton/es/components/LeftPlugin/index.scss index e69de29bb..9c6922129 100644 --- a/packages/editor-skeleton/es/components/LeftPlugin/index.scss +++ b/packages/editor-skeleton/es/components/LeftPlugin/index.scss @@ -0,0 +1,59 @@ +.luna-left-addon { + font-size: 16px; + text-align: center; + line-height: 36px; + height: 36px; + position: relative; + cursor: pointer; + transition: all 0.3s ease; + color: #777; + &.collapse { + height: 40px; + color: #8c8c8c; + border-bottom: 1px solid #bfbfbf; + } + &.locked { + color: red !important; + } + &.active { + color: #fff !important; + background-color: $color-brand1-9 !important; + &.disabled { + color: #fff; + background-color: $color-fill1-7; + } + } + &.disabled { + cursor: not-allowed; + color: $color-text1-1; + } + &:hover { + background-color: $color-brand1-1; + color: $color-brand1-6; + &:before { + content: attr(data-tooltip); + display: block; + position: absolute; + left: 50px; + top: 5px; + line-height: 18px; + font-size: 12px; + white-space: nowrap; + padding: 6px 8px; + border-radius: 4px; + background: rgba(0, 0, 0, 0.75); + color: #fff; + z-index: 100; + } + &:after { + content: ''; + display: block; + position: absolute; + left: 40px; + top: 15px; + border: 5px solid transparent; + border-right-color: rgba(0, 0, 0, 0.75); + z-index: 100; + } + } +} diff --git a/packages/editor-skeleton/es/components/TopIcon/index.d.ts b/packages/editor-skeleton/es/components/TopIcon/index.d.ts index 0e1aea7cb..2bb6854b4 100644 --- a/packages/editor-skeleton/es/components/TopIcon/index.d.ts +++ b/packages/editor-skeleton/es/components/TopIcon/index.d.ts @@ -1,6 +1,30 @@ -/// -export interface Props { - name: string; +import { PureComponent } from 'react'; +import './index.scss'; +export default class TopIcon extends PureComponent { + static displayName: string; + static propTypes: { + active: any; + className: any; + disabled: any; + icon: any; + id: any; + locked: any; + onClick: any; + showTitle: any; + style: any; + title: any; + }; + static defaultProps: { + active: boolean; + className: string; + disabled: boolean; + icon: string; + id: string; + locked: boolean; + onClick: () => void; + showTitle: boolean; + style: {}; + title: string; + }; + render(): JSX.Element; } -declare const Greeting: ({ name }: Props) => JSX.Element; -export default Greeting; diff --git a/packages/editor-skeleton/es/components/TopIcon/index.js b/packages/editor-skeleton/es/components/TopIcon/index.js index 3a62c613d..bfa50a96b 100644 --- a/packages/editor-skeleton/es/components/TopIcon/index.js +++ b/packages/editor-skeleton/es/components/TopIcon/index.js @@ -1,14 +1,76 @@ -import React from 'react'; +import _Button from "@alifd/next/es/button"; +import _Icon from "@alifd/next/es/icon"; +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import './index.scss'; -var Greeting = function Greeting(_ref) { - var name = _ref.name; - return React.createElement("div", { - style: { - textAlign: 'center', - fontSize: '40px', - fontWeight: 'bold' - } - }, "Hello, ", name); +var TopIcon = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(TopIcon, _PureComponent); + + function TopIcon() { + return _PureComponent.apply(this, arguments) || this; + } + + var _proto = TopIcon.prototype; + + _proto.render = function render() { + var _this$props = this.props, + active = _this$props.active, + disabled = _this$props.disabled, + icon = _this$props.icon, + locked = _this$props.locked, + title = _this$props.title, + className = _this$props.className, + id = _this$props.id, + style = _this$props.style, + showTitle = _this$props.showTitle, + onClick = _this$props.onClick; + return React.createElement(_Button, { + type: "normal", + size: "large", + text: true, + className: classNames('lowcode-top-btn', className, { + active: active, + disabled: disabled, + locked: locked + }), + id: id, + style: style, + onClick: disabled ? null : onClick + }, React.createElement("div", null, React.createElement(_Icon, { + size: "large", + type: icon + }), showTitle && React.createElement("span", null, title))); + }; + + return TopIcon; +}(PureComponent); + +TopIcon.displayName = 'TopIcon'; +TopIcon.propTypes = { + active: PropTypes.bool, + className: PropTypes.string, + disabled: PropTypes.bool, + icon: PropTypes.string, + id: PropTypes.string, + locked: PropTypes.bool, + onClick: PropTypes.func, + showTitle: PropTypes.bool, + style: PropTypes.object, + title: PropTypes.string }; - -export default Greeting; \ No newline at end of file +TopIcon.defaultProps = { + active: false, + className: '', + disabled: false, + icon: '', + id: '', + locked: false, + onClick: function onClick() {}, + showTitle: false, + style: {}, + title: '' +}; +export { TopIcon as default }; \ No newline at end of file diff --git a/packages/editor-skeleton/es/components/TopIcon/index.scss b/packages/editor-skeleton/es/components/TopIcon/index.scss new file mode 100644 index 000000000..1cb3bdfdf --- /dev/null +++ b/packages/editor-skeleton/es/components/TopIcon/index.scss @@ -0,0 +1,32 @@ +.next-btn.next-large.lowcode-top-btn { + width: 44px; + height: 44px; + padding: 0; + margin: 4px -2px; + text-align: center; + border-radius: 8px; + border: 1px solid transparent; + color: #777; + &.disabled { + cursor: not-allowed; + color: $color-text1-1; + } + &.locked { + color: red !important; + } + i.next-icon { + &:before { + font-size: 17px; + } + margin-right: 0; + line-height: 18px; + } + span { + display: block; + margin: 0px -5px 0; + line-height: 16px; + text-align: center; + font-size: 12px; + transform: scale(0.8); + } +} diff --git a/packages/editor-skeleton/es/components/TopPlugin/index.d.ts b/packages/editor-skeleton/es/components/TopPlugin/index.d.ts new file mode 100644 index 000000000..dc09c377e --- /dev/null +++ b/packages/editor-skeleton/es/components/TopPlugin/index.d.ts @@ -0,0 +1,21 @@ +import { PureComponent } from 'react'; +import './index.scss'; +export default class TopPlugin extends PureComponent { + static displayName: string; + static defaultProps: { + active: boolean; + config: {}; + disabled: boolean; + dotted: boolean; + locked: boolean; + onClick: () => void; + }; + constructor(props: any, context: any); + componentDidMount(): void; + componentWillUnmount(): void; + handleShow: () => void; + handleClose: () => void; + handleOpen: () => void; + renderIcon: (clickCallback: any) => JSX.Element; + render(): JSX.Element; +} diff --git a/packages/editor-skeleton/es/components/TopPlugin/index.js b/packages/editor-skeleton/es/components/TopPlugin/index.js index e69de29bb..e881bb6c1 100644 --- a/packages/editor-skeleton/es/components/TopPlugin/index.js +++ b/packages/editor-skeleton/es/components/TopPlugin/index.js @@ -0,0 +1,213 @@ +import _Balloon from "@alifd/next/es/balloon"; +import _Dialog from "@alifd/next/es/dialog"; +import _extends from "@babel/runtime/helpers/extends"; +import _Badge from "@alifd/next/es/badge"; +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; +import React, { PureComponent, Fragment } from 'react'; +import TopIcon from '../TopIcon'; +import './index.scss'; + +var TopPlugin = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(TopPlugin, _PureComponent); + + function TopPlugin(_props, context) { + var _this; + + _this = _PureComponent.call(this, _props, context) || this; + + _this.handleShow = function () { + var _this$props = _this.props, + disabled = _this$props.disabled, + config = _this$props.config, + onClick = _this$props.onClick; + var addonKey = config && config.addonKey; + if (disabled || !addonKey) return; //考虑到弹窗情况,延时发送消息 + + setTimeout(function () { + return _this.appHelper.emit(addonKey + ".addon.activate"); + }, 0); + + _this.handleOpen(); + + onClick && onClick(); + }; + + _this.handleClose = function () { + var addonKey = _this.props.config && _this.props.config.addonKey; + var currentAddon = _this.appHelper.addons && _this.appHelper.addons[addonKey]; + + if (currentAddon) { + _this.utils.transformToPromise(currentAddon.close()).then(function () { + _this.setState({ + dialogVisible: false + }); + }); + } + }; + + _this.handleOpen = function () { + // todo dialog类型的插件初始时拿不动插件实例 + _this.setState({ + dialogVisible: true + }); + }; + + _this.renderIcon = function (clickCallback) { + var _this$props2 = _this.props, + active = _this$props2.active, + disabled = _this$props2.disabled, + dotted = _this$props2.dotted, + locked = _this$props2.locked, + config = _this$props2.config, + _onClick = _this$props2.onClick; + + var _ref = config || {}, + pluginKey = _ref.pluginKey, + props = _ref.props; + + var _ref2 = props || {}, + icon = _ref2.icon, + title = _ref2.title; + + var node = React.createElement(TopIcon, { + className: "lowcode-top-addon " + pluginKey, + active: active, + disabled: disabled, + locked: locked, + icon: icon, + title: title, + onClick: function onClick() { + if (disabled) return; //考虑到弹窗情况,延时发送消息 + + setTimeout(function () { + return _this.appHelper.emit(pluginKey + ".addon.activate"); + }, 0); + clickCallback && clickCallback(); + _onClick && _onClick(); + } + }); + return dotted ? React.createElement(_Badge, { + dot: true + }, node) : node; + }; + + _this.state = { + dialogVisible: false + }; + return _this; + } + + var _proto = TopPlugin.prototype; + + _proto.componentDidMount = function componentDidMount() { + var config = this.props.config; + var pluginKey = config && config.pluginKey; // const appHelper = this.appHelper; + // if (appHelper && addonKey) { + // appHelper.on(`${addonKey}.dialog.show`, this.handleShow); + // appHelper.on(`${addonKey}.dialog.close`, this.handleClose); + // } + }; + + _proto.componentWillUnmount = function componentWillUnmount() {// const { config } = this.props; + // const addonKey = config && config.addonKey; + // const appHelper = this.appHelper; + // if (appHelper && addonKey) { + // appHelper.off(`${addonKey}.dialog.show`, this.handleShow); + // appHelper.off(`${addonKey}.dialog.close`, this.handleClose); + // } + }; + + _proto.render = function render() { + var _this2 = this; + + var _this$props3 = this.props, + active = _this$props3.active, + dotted = _this$props3.dotted, + locked = _this$props3.locked, + disabled = _this$props3.disabled, + config = _this$props3.config, + editor = _this$props3.editor, + Comp = _this$props3.pluginClass; + + var _ref3 = config || {}, + pluginKey = _ref3.pluginKey, + pluginProps = _ref3.pluginProps, + props = _ref3.props, + type = _ref3.type; + + var _ref4 = props || {}, + _onClick2 = _ref4.onClick, + title = _ref4.title; + + var dialogVisible = this.state.dialogVisible; + if (!pluginKey || !type || !Comp) return null; + var node = React.createElement(Comp, _extends({ + active: active, + locked: locked, + disabled: disabled, + config: config, + onClick: function onClick() { + _onClick2 && _onClick2.call(null, editor); + } + }, pluginProps)); + + switch (type) { + case 'LinkIcon': + return React.createElement("a", props.linkProps, this.renderIcon(function () { + _onClick2 && _onClick2.call(null, editor); + })); + + case 'Icon': + return this.renderIcon(function () { + _onClick2 && _onClick2.call(null, editor); + }); + + case 'DialogIcon': + return React.createElement(Fragment, null, this.renderIcon(function () { + _onClick2 && _onClick2.call(null, editor); + + _this2.handleOpen(); + }), React.createElement(_Dialog, _extends({ + onOk: function onOk() { + editor.emit(pluginKey + ".dialog.onOk"); + + _this2.handleClose(); + }, + onCancel: this.handleClose, + onClose: this.handleClose, + title: title + }, props.dialogProps, { + visible: dialogVisible + }), node)); + + case 'BalloonIcon': + return React.createElement(_Balloon, _extends({ + trigger: this.renderIcon(function () { + _onClick2 && _onClick2.call(null, editor); + }), + triggerType: ['click', 'hover'] + }, props.balloonProps), node); + + case 'Custom': + return dotted ? React.createElement(_Badge, { + dot: true + }, node) : node; + + default: + return null; + } + }; + + return TopPlugin; +}(PureComponent); + +TopPlugin.displayName = 'lowcodeTopPlugin'; +TopPlugin.defaultProps = { + active: false, + config: {}, + disabled: false, + dotted: false, + locked: false, + onClick: function onClick() {} +}; +export { TopPlugin as default }; \ No newline at end of file diff --git a/packages/editor-skeleton/es/components/TopPlugin/index.scss b/packages/editor-skeleton/es/components/TopPlugin/index.scss index e69de29bb..4bdd7b8d2 100644 --- a/packages/editor-skeleton/es/components/TopPlugin/index.scss +++ b/packages/editor-skeleton/es/components/TopPlugin/index.scss @@ -0,0 +1,2 @@ +.lowcode-top-addon { +} diff --git a/packages/editor-skeleton/es/global.scss b/packages/editor-skeleton/es/global.scss index 68689a592..0a710b895 100644 --- a/packages/editor-skeleton/es/global.scss +++ b/packages/editor-skeleton/es/global.scss @@ -1,3 +1,13 @@ +body { + font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, + Arial, PingFang SC-Light, Microsoft YaHei; + font-size: 12px; + padding: 0; + margin: 0; + * { + box-sizing: border-box; + } +} .next-loading { .next-loading-wrap { height: 100%; @@ -6,17 +16,17 @@ .lowcode-editor { .lowcode-main-content { position: absolute; - top: 54px; + top: 48px; left: 0; right: 0; bottom: 0; display: flex; + background-color: #d8d8d8; } .lowcode-center-area { flex: 1; display: flex; flex-direction: column; - background: #f7f7f7; padding: 10px; overflow: auto; } diff --git a/packages/editor-skeleton/es/index.d.ts b/packages/editor-skeleton/es/index.d.ts index f07a622f4..468afa29c 100644 --- a/packages/editor-skeleton/es/index.d.ts +++ b/packages/editor-skeleton/es/index.d.ts @@ -1,5 +1,4 @@ -/// -import { PureComponent } from 'react-dom'; +import { PureComponent } from 'react'; import './global.scss'; export default class Skeleton extends PureComponent { static displayName: string; diff --git a/packages/editor-skeleton/es/index.js b/packages/editor-skeleton/es/index.js index 7031d8ee9..f875f6604 100644 --- a/packages/editor-skeleton/es/index.js +++ b/packages/editor-skeleton/es/index.js @@ -1,15 +1,29 @@ import _ConfigProvider from "@alifd/next/es/config-provider"; import _Loading from "@alifd/next/es/loading"; import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; -import React, { PureComponent } from 'react-dom'; // import Editor from '@ali/lowcode-engine-editor'; +import React, { PureComponent } from 'react'; // import Editor from '@ali/lowcode-engine-editor'; +import TopArea from './layouts/TopArea'; +import LeftArea from './layouts/LeftArea'; +import CenterArea from './layouts/CenterArea'; +import RightArea from './layouts/RightArea'; import './global.scss'; var Skeleton = /*#__PURE__*/function (_PureComponent) { _inheritsLoose(Skeleton, _PureComponent); function Skeleton(props) { - return _PureComponent.call(this, props) || this; // this.editor = new Editor(props.config, props.utils); + var _this; + + _this = _PureComponent.call(this, props) || this; // this.editor = new Editor(props.config, props.utils); + + _this.editor = { + on: function on() {}, + off: function off() {}, + config: props.config, + pluginComponents: props.pluginComponents + }; + return _this; } var _proto = Skeleton.prototype; @@ -23,17 +37,30 @@ var Skeleton = /*#__PURE__*/function (_PureComponent) { location = _this$props.location, history = _this$props.history, messages = _this$props.messages; - return React.createElement(_ConfigProvider, { - locale: messages[appHelper.locale] - }, React.createElement(_Loading, { - tip: this.i18n('loading'), + this.editor.location = location; + this.editor.history = history; + this.editor.messages = messages; + return React.createElement(_ConfigProvider, null, React.createElement(_Loading, { + tip: "Loading", size: "large", - visible: loading || !initReady, + visible: false, shape: "fusion-reactor", fullScreen: true }, React.createElement("div", { className: "lowcode-editor" - }))); + }, React.createElement(TopArea, { + editor: this.editor + }), React.createElement("div", { + className: "lowcode-main-content" + }, React.createElement(LeftArea.Nav, { + editor: this.editor + }), React.createElement(LeftArea.Panel, { + editor: this.editor + }), React.createElement(CenterArea, { + editor: this.editor + }), React.createElement(RightArea, { + editor: this.editor + }))))); }; return Skeleton; diff --git a/packages/editor-skeleton/es/layouts/CenterArea/index.scss b/packages/editor-skeleton/es/layouts/CenterArea/index.scss index e69de29bb..b2584ed2b 100644 --- a/packages/editor-skeleton/es/layouts/CenterArea/index.scss +++ b/packages/editor-skeleton/es/layouts/CenterArea/index.scss @@ -0,0 +1,3 @@ +.lowcode-center-area { + padding: 12px; +} diff --git a/packages/editor-skeleton/es/layouts/LeftArea/index.scss b/packages/editor-skeleton/es/layouts/LeftArea/index.scss index e69de29bb..dac1b6b0a 100644 --- a/packages/editor-skeleton/es/layouts/LeftArea/index.scss +++ b/packages/editor-skeleton/es/layouts/LeftArea/index.scss @@ -0,0 +1,21 @@ +.lowcode-left-area-nav { + width: 48px; + height: 100%; + background: #ffffff; + border-right: 1px solid #e8ebee; + position: relative; + .top-area { + position: absolute; + top: 0; + width: 100%; + background: #ffffff; + max-height: 100%; + } + .bottom-area { + position: absolute; + bottom: 20px; + width: 100%; + background: #ffffff; + max-height: calc(100% - 20px); + } +} diff --git a/packages/editor-skeleton/es/layouts/LeftArea/panel.js b/packages/editor-skeleton/es/layouts/LeftArea/panel.js index 0120423d8..332529ce2 100644 --- a/packages/editor-skeleton/es/layouts/LeftArea/panel.js +++ b/packages/editor-skeleton/es/layouts/LeftArea/panel.js @@ -13,7 +13,7 @@ var LeftAreaPanel = /*#__PURE__*/function (_PureComponent) { _proto.render = function render() { return React.createElement("div", { - className: "lowcode-left-area" + className: "lowcode-left-area-panel" }); }; diff --git a/packages/editor-skeleton/es/layouts/RightArea/index.scss b/packages/editor-skeleton/es/layouts/RightArea/index.scss index e69de29bb..120ef4f11 100644 --- a/packages/editor-skeleton/es/layouts/RightArea/index.scss +++ b/packages/editor-skeleton/es/layouts/RightArea/index.scss @@ -0,0 +1,157 @@ +.lowcode-right-area { + width: 300px; + height: 100%; + background-color: #ffffff; + border-left: 1px solid #e8ebee; + .right-plugin-title { + &.locked { + color: red !important; + } + &.active { + color: $color-brand1-9 !important; + } + &.disabled { + cursor: not-allowed; + color: $color-text1-1; + } + } + + //tab定义 + .next-tabs-wrapped.right-tabs { + display: flex; + flex-direction: column; + margin-top: -1px; + .next-tabs-bar { + z-index: 1; + } + .next-tabs-nav { + display: block; + .next-tabs-tab { + &:first-child { + border-left: none; + } + font-size: 14px; + text-align: center; + border-right: none !important; + margin-right: 0 !important; + width: 25%; + &.active { + background: none; + border-bottom-color: #f7f7f7 !important; + } + } + } + } + .next-tabs-content { + flex: 1; + .next-tabs-tabpane.active { + height: 100%; + overflow-y: auto; + } + } + //组件 + .select-comp { + padding: 10px 16px; + line-height: 16px; + color: #989a9c; + & > span { + font-size: 12px; + line-height: 16px; + font-weight: 400; + } + & > .btn-wrap, + & > .next-btn { + width: auto; + margin: 0 5px; + float: right; + } + } + + .unselected { + padding: 60px 0; + text-align: center; + } + //右侧属性面板样式调整; + .offset-56 { + padding-left: 56px; + margin-bottom: 16px; + overflow: hidden; + } + .fixedSpan.next-form-item { + & > .next-form-item-label { + width: 56px; + flex: none; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + & > .next-form-item-control { + padding-right: 24px; + } + } + .fixedSpan.next-form-item, + .offset-56 .next-form-item { + display: flex; + & > .next-form-item-control { + width: auto; + flex: 1; + max-width: none; + .next-input, + .next-select, + .next-radio-group, + .next-number-picker, + .luna-reactnode-btn, + .luna-monaco-button button, + .luna-object-button button { + width: 100%; + } + .next-number-picker { + width: 100%; + .next-after { + padding-right: 5px; + } + } + .next-radio-group { + display: flex; + label { + flex: 1; + text-align: center; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + } + } + } + .topSpan.next-form-item { + margin-bottom: 4px; + & > .next-form-item-control { + padding-right: 24px; + .next-input, + .next-select, + .next-radio-group, + .next-number-picker, + .luna-reactnode-btn, + .luna-monaco-button button, + .luna-object-button button { + width: 100%; + } + .next-number-picker { + width: 100%; + .next-after { + padding-right: 5px; + } + } + .next-radio-group { + display: flex; + label { + flex: 1; + text-align: center; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + } + } + } +} diff --git a/packages/editor-skeleton/es/layouts/TopArea/index.d.ts b/packages/editor-skeleton/es/layouts/TopArea/index.d.ts index 716bd94a8..e800a1e74 100644 --- a/packages/editor-skeleton/es/layouts/TopArea/index.d.ts +++ b/packages/editor-skeleton/es/layouts/TopArea/index.d.ts @@ -3,5 +3,9 @@ import './index.scss'; export default class TopArea extends PureComponent { static displayName: string; constructor(props: any); + componentDidMount(): void; + componentWillUnmount(): void; + handlePluginStatusChange: () => void; + renderPluginList: (list?: any[]) => JSX.Element[]; render(): JSX.Element; } diff --git a/packages/editor-skeleton/es/layouts/TopArea/index.js b/packages/editor-skeleton/es/layouts/TopArea/index.js index 39f2b2b23..c82eefc61 100644 --- a/packages/editor-skeleton/es/layouts/TopArea/index.js +++ b/packages/editor-skeleton/es/layouts/TopArea/index.js @@ -1,20 +1,79 @@ import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; +import _Grid from "@alifd/next/es/grid"; import React, { PureComponent } from 'react'; +import TopPlugin from '../../components/TopPlugin'; import './index.scss'; +var Row = _Grid.Row, + Col = _Grid.Col; var TopArea = /*#__PURE__*/function (_PureComponent) { _inheritsLoose(TopArea, _PureComponent); function TopArea(props) { - return _PureComponent.call(this, props) || this; + var _this; + + _this = _PureComponent.call(this, props) || this; + + _this.handlePluginStatusChange = function () {}; + + _this.renderPluginList = function (list) { + if (list === void 0) { + list = []; + } + + return list.map(function (item, idx) { + var isDivider = item.type === 'Divider'; + return React.createElement(Col, { + className: isDivider ? 'divider' : '', + key: isDivider ? idx : item.pluginKey, + style: { + width: item.props && item.props.width || 40, + flex: 'none' + } + }, !isDivider && React.createElement(TopPlugin, { + config: item, + pluginClass: _this.editor.pluginComponents[item.pluginKey], + status: _this.editor.pluginStatus[item.pluginKey] + })); + }); + }; + + _this.editor = props.editor; + _this.config = _this.editor.config.plugins && _this.editor.config.plugins.topArea; + return _this; } var _proto = TopArea.prototype; + _proto.componentDidMount = function componentDidMount() {}; + + _proto.componentWillUnmount = function componentWillUnmount() {}; + _proto.render = function render() { + if (!this.config) return null; + var leftList = []; + var rightList = []; + this.config.forEach(function (item) { + var align = item.props && item.props.align === 'right' ? 'right' : 'left'; // 分隔符不允许相邻 + + if (item.type === 'Divider') { + var currentList = align === 'right' ? rightList : leftList; + if (currList.length === 0 || currList[currList.length - 1].type === 'Divider') return; + } + + if (align === 'right') { + rightList.push(item); + } else { + leftList.push(item); + } + }); return React.createElement("div", { className: "lowcode-top-area" - }); + }, React.createElement("div", { + className: "left-area" + }, this.renderPluginList(leftList)), React.createElement("div", { + classname: "right-area" + }, this.renderPluginList(rightList))); }; return TopArea; diff --git a/packages/editor-skeleton/es/layouts/TopArea/index.scss b/packages/editor-skeleton/es/layouts/TopArea/index.scss index e69de29bb..ca8bbd825 100644 --- a/packages/editor-skeleton/es/layouts/TopArea/index.scss +++ b/packages/editor-skeleton/es/layouts/TopArea/index.scss @@ -0,0 +1,5 @@ +.lowcode-top-area { + height: 48px; + background-color: #ffffff; + border-bottom: 1px solid #e8ebee; +} diff --git a/packages/editor-skeleton/es/style.js b/packages/editor-skeleton/es/style.js index ba2702ef4..7f81d0018 100644 --- a/packages/editor-skeleton/es/style.js +++ b/packages/editor-skeleton/es/style.js @@ -1,2 +1,8 @@ import '@alifd/next/es/config-provider/style'; -import '@alifd/next/es/loading/style'; \ No newline at end of file +import '@alifd/next/es/loading/style'; +import '@alifd/next/es/grid/style'; +import '@alifd/next/es/balloon/style'; +import '@alifd/next/es/dialog/style'; +import '@alifd/next/es/badge/style'; +import '@alifd/next/es/button/style'; +import '@alifd/next/es/icon/style'; \ No newline at end of file diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json index 9067b4544..1320d2903 100644 --- a/packages/editor-skeleton/package.json +++ b/packages/editor-skeleton/package.json @@ -2,6 +2,26 @@ "name": "@ali/lowcode-engine-skeleton", "version": "0.0.1", "description": "alibaba lowcode editor skeleton", + "files": [ + "demo/", + "es/", + "lib/", + "build/" + ], + "main": "lib/index.tsx", + "module": "es/index.js", + "stylePath": "style.js", + "scripts": { + "start": "build-scripts start", + "build": "build-scripts build --skip-demo", + "prepublishOnly": "npm run prettier && npm run build", + "lint": "eslint --cache --ext .js,.jsx ./", + "prettier": "prettier --write \"./src/**/*.{ts,tsx,js,jsx,ejs,less,css,scss,json}\" " + }, + "keywords": [ + "lowcode", + "editor" + ], "author": "xiayang.xy", "dependencies": { "@alifd/next": "^1.x", @@ -21,7 +41,7 @@ "@types/lodash": "^4.14.149", "@types/react": "^16.9.13", "@types/react-dom": "^16.9.4", - "build-plugin-component": "^0.2.0", + "build-plugin-component": "^0.2.7-1", "build-plugin-fusion": "^0.1.0", "build-plugin-moment-locales": "^0.1.0", "eslint": "^6.0.1", @@ -29,23 +49,6 @@ "react": "^16.8.0", "react-dom": "^16.8.0" }, - "scripts": { - "start": "build-scripts start", - "build": "build-scripts build", - "prepublishOnly": "npm run prettier && npm run build", - "lint": "eslint --cache --ext .js,.jsx ./", - "prettier": "prettier --write \"./src/**/*.{ts,tsx,js,jsx,ejs,less,css,scss,json}\" " - }, - "engines": { - "node": ">=8.0.0" - }, - "iceworks": { - "type": "react", - "adapter": "adapter-react-v3" - }, - "ideMode": { - "name": "ice-react" - }, "repository": { "type": "git", "url": "https://github.com/ice-lab/react-materials/tree/master/scaffolds/ice-ts" diff --git a/packages/editor-skeleton/src/components/LeftPlugin/index.scss b/packages/editor-skeleton/src/components/LeftPlugin/index.scss index e69de29bb..9c6922129 100644 --- a/packages/editor-skeleton/src/components/LeftPlugin/index.scss +++ b/packages/editor-skeleton/src/components/LeftPlugin/index.scss @@ -0,0 +1,59 @@ +.luna-left-addon { + font-size: 16px; + text-align: center; + line-height: 36px; + height: 36px; + position: relative; + cursor: pointer; + transition: all 0.3s ease; + color: #777; + &.collapse { + height: 40px; + color: #8c8c8c; + border-bottom: 1px solid #bfbfbf; + } + &.locked { + color: red !important; + } + &.active { + color: #fff !important; + background-color: $color-brand1-9 !important; + &.disabled { + color: #fff; + background-color: $color-fill1-7; + } + } + &.disabled { + cursor: not-allowed; + color: $color-text1-1; + } + &:hover { + background-color: $color-brand1-1; + color: $color-brand1-6; + &:before { + content: attr(data-tooltip); + display: block; + position: absolute; + left: 50px; + top: 5px; + line-height: 18px; + font-size: 12px; + white-space: nowrap; + padding: 6px 8px; + border-radius: 4px; + background: rgba(0, 0, 0, 0.75); + color: #fff; + z-index: 100; + } + &:after { + content: ''; + display: block; + position: absolute; + left: 40px; + top: 15px; + border: 5px solid transparent; + border-right-color: rgba(0, 0, 0, 0.75); + z-index: 100; + } + } +} diff --git a/packages/editor-skeleton/src/components/LeftPlugin/index.tsx b/packages/editor-skeleton/src/components/LeftPlugin/index.tsx index e69de29bb..d9a637a21 100644 --- a/packages/editor-skeleton/src/components/LeftPlugin/index.tsx +++ b/packages/editor-skeleton/src/components/LeftPlugin/index.tsx @@ -0,0 +1,223 @@ +import React, { PureComponent, Fragment } from 'react'; + +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import AppContext from '@ali/iceluna-sdk/lib/context/appContext'; +import { Balloon, Dialog, Icon, Badge } from '@alife/next'; + +import './index.scss'; +export default class LeftAddon extends PureComponent { + static displayName = 'LunaLeftAddon'; + static propTypes = { + active: PropTypes.bool, + config: PropTypes.shape({ + addonKey: PropTypes.string, + addonProps: PropTypes.object, + props: PropTypes.object, + type: PropTypes.oneOf([ + 'DialogIcon', + 'BalloonIcon', + 'PanelIcon', + 'LinkIcon', + 'Icon', + 'Custom', + ]), + }), + disabled: PropTypes.bool, + dotted: PropTypes.bool, + locked: PropTypes.bool, + onClick: PropTypes.func, + }; + static defaultProps = { + active: false, + config: {}, + disabled: false, + dotted: false, + locked: false, + onClick: () => {}, + }; + static contextType = AppContext; + + constructor(props, context) { + super(props, context); + this.state = { + dialogVisible: false, + }; + this.appHelper = context.appHelper; + this.utils = this.appHelper.utils; + this.constants = this.appHelper.constants; + } + + componentDidMount() { + const { config } = this.props; + const addonKey = config && config.addonKey; + const appHelper = this.appHelper; + if (appHelper && addonKey) { + appHelper.on(`${addonKey}.dialog.show`, this.handleShow); + appHelper.on(`${addonKey}.dialog.close`, this.handleClose); + } + } + + componentWillUnmount() { + const { config } = this.props; + const appHelper = this.appHelper; + const addonKey = config && config.addonKey; + if (appHelper && addonKey) { + appHelper.off(`${addonKey}.dialog.show`, this.handleShow); + appHelper.off(`${addonKey}.dialog.close`, this.handleClose); + } + } + + handleClose = () => { + const addonKey = this.props.config && this.props.config.addonKey; + const currentAddon = + this.appHelper.addons && this.appHelper.addons[addonKey]; + if (currentAddon) { + this.utils.transformToPromise(currentAddon.close()).then(() => { + this.setState({ + dialogVisible: false, + }); + }); + } + }; + + handleOpen = () => { + // todo 对话框类型的插件初始时拿不到插件实例 + this.setState({ + dialogVisible: true, + }); + }; + + handleShow = () => { + const { disabled, config, onClick } = this.props; + const addonKey = config && config.addonKey; + if (disabled || !addonKey) return; + //考虑到弹窗情况,延时发送消息 + setTimeout(() => this.appHelper.emit(`${addonKey}.addon.activate`), 0); + this.handleOpen(); + onClick && onClick(); + }; + + renderIcon = clickCallback => { + const { active, disabled, dotted, locked, onClick, config } = this.props; + const { addonKey, props } = config || {}; + const { icon, title } = props || {}; + return ( +
{ + if (disabled) return; + //考虑到弹窗情况,延时发送消息 + clickCallback && clickCallback(); + onClick && onClick(); + }} + > + {dotted ? ( + + + + ) : ( + + )} +
+ ); + }; + + render() { + const { dotted, locked, active, disabled, config } = this.props; + const { addonKey, props, type, addonProps } = config || {}; + const { onClick, title } = props || {}; + const { dialogVisible } = this.state; + const { appHelper, components } = this.context; + if (!addonKey || !type || !props) return null; + const componentName = appHelper.utils.generateAddonCompName(addonKey); + const localeProps = {}; + const { locale, messages } = appHelper; + if (locale) { + localeProps.locale = locale; + } + if (messages && messages[componentName]) { + localeProps.messages = messages[componentName]; + } + const AddonComp = components && components[componentName]; + const node = + (AddonComp && ( + { + onClick && onClick.call(null, appHelper); + }} + {...localeProps} + {...(addonProps || {})} + /> + )) || + null; + + switch (type) { + case 'LinkIcon': + return ( + + {this.renderIcon(() => { + onClick && onClick.call(null, appHelper); + })} + + ); + case 'Icon': + return this.renderIcon(() => { + onClick && onClick.call(null, appHelper); + }); + case 'DialogIcon': + return ( + + {this.renderIcon(() => { + onClick && onClick.call(null, appHelper); + this.handleOpen(); + })} + { + appHelper.emit(`${addonKey}.dialog.onOk`); + this.handleClose(); + }} + onCancel={this.handleClose} + onClose={this.handleClose} + title={title} + {...(props.dialogProps || {})} + visible={dialogVisible} + > + {node} + + + ); + case 'BalloonIcon': + return ( + { + onClick && onClick.call(null, appHelper); + })} + align="r" + triggerType={['click', 'hover']} + {...(props.balloonProps || {})} + > + {node} + + ); + case 'PanelIcon': + return this.renderIcon(() => { + onClick && onClick.call(null, appHelper); + this.handleOpen(); + }); + case 'Custom': + return dotted ? {node} : node; + default: + return null; + } + } +} diff --git a/packages/editor-skeleton/src/components/TopIcon/index.scss b/packages/editor-skeleton/src/components/TopIcon/index.scss new file mode 100644 index 000000000..1cb3bdfdf --- /dev/null +++ b/packages/editor-skeleton/src/components/TopIcon/index.scss @@ -0,0 +1,32 @@ +.next-btn.next-large.lowcode-top-btn { + width: 44px; + height: 44px; + padding: 0; + margin: 4px -2px; + text-align: center; + border-radius: 8px; + border: 1px solid transparent; + color: #777; + &.disabled { + cursor: not-allowed; + color: $color-text1-1; + } + &.locked { + color: red !important; + } + i.next-icon { + &:before { + font-size: 17px; + } + margin-right: 0; + line-height: 18px; + } + span { + display: block; + margin: 0px -5px 0; + line-height: 16px; + text-align: center; + font-size: 12px; + transform: scale(0.8); + } +} diff --git a/packages/editor-skeleton/src/components/TopIcon/index.tsx b/packages/editor-skeleton/src/components/TopIcon/index.tsx index b9369ed63..8e81c9c3a 100644 --- a/packages/editor-skeleton/src/components/TopIcon/index.tsx +++ b/packages/editor-skeleton/src/components/TopIcon/index.tsx @@ -1,15 +1,68 @@ -import React from 'react'; +import React, { PureComponent } from 'react'; -export interface Props { - name: string; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { Icon, Button } from '@alifd/next'; +import './index.scss'; +export default class TopIcon extends PureComponent { + static displayName = 'TopIcon'; + static propTypes = { + active: PropTypes.bool, + className: PropTypes.string, + disabled: PropTypes.bool, + icon: PropTypes.string, + id: PropTypes.string, + locked: PropTypes.bool, + onClick: PropTypes.func, + showTitle: PropTypes.bool, + style: PropTypes.object, + title: PropTypes.string, + }; + static defaultProps = { + active: false, + className: '', + disabled: false, + icon: '', + id: '', + locked: false, + onClick: () => {}, + showTitle: false, + style: {}, + title: '', + }; + + render() { + const { + active, + disabled, + icon, + locked, + title, + className, + id, + style, + showTitle, + onClick, + } = this.props; + return ( + + ); + } } - -const Greeting = ({ name }: Props) => { - return ( -
- Hello, {name} -
- ); -}; - -export default Greeting; diff --git a/packages/editor-skeleton/src/components/TopPlugin/index.scss b/packages/editor-skeleton/src/components/TopPlugin/index.scss index e69de29bb..4bdd7b8d2 100644 --- a/packages/editor-skeleton/src/components/TopPlugin/index.scss +++ b/packages/editor-skeleton/src/components/TopPlugin/index.scss @@ -0,0 +1,2 @@ +.lowcode-top-addon { +} diff --git a/packages/editor-skeleton/src/components/TopPlugin/index.tsx b/packages/editor-skeleton/src/components/TopPlugin/index.tsx index e69de29bb..04f5d0e59 100644 --- a/packages/editor-skeleton/src/components/TopPlugin/index.tsx +++ b/packages/editor-skeleton/src/components/TopPlugin/index.tsx @@ -0,0 +1,174 @@ +import React, { PureComponent, Fragment } from 'react'; + +import PropTypes from 'prop-types'; +import TopIcon from '../TopIcon'; +import { Balloon, Badge, Dialog } from '@alifd/next'; + +import './index.scss'; +export default class TopPlugin extends PureComponent { + static displayName = 'lowcodeTopPlugin'; + + static defaultProps = { + active: false, + config: {}, + disabled: false, + dotted: false, + locked: false, + onClick: () => {}, + }; + + constructor(props, context) { + super(props, context); + this.state = { + dialogVisible: false, + }; + } + + componentDidMount() { + const { config } = this.props; + const pluginKey = config && config.pluginKey; + // const appHelper = this.appHelper; + // if (appHelper && addonKey) { + // appHelper.on(`${addonKey}.dialog.show`, this.handleShow); + // appHelper.on(`${addonKey}.dialog.close`, this.handleClose); + // } + } + + componentWillUnmount() { + // const { config } = this.props; + // const addonKey = config && config.addonKey; + // const appHelper = this.appHelper; + // if (appHelper && addonKey) { + // appHelper.off(`${addonKey}.dialog.show`, this.handleShow); + // appHelper.off(`${addonKey}.dialog.close`, this.handleClose); + // } + } + + handleShow = () => { + const { disabled, config, onClick } = this.props; + const addonKey = config && config.addonKey; + if (disabled || !addonKey) return; + //考虑到弹窗情况,延时发送消息 + setTimeout(() => this.appHelper.emit(`${addonKey}.addon.activate`), 0); + this.handleOpen(); + onClick && onClick(); + }; + + handleClose = () => { + const addonKey = this.props.config && this.props.config.addonKey; + const currentAddon = + this.appHelper.addons && this.appHelper.addons[addonKey]; + if (currentAddon) { + this.utils.transformToPromise(currentAddon.close()).then(() => { + this.setState({ + dialogVisible: false, + }); + }); + } + }; + + handleOpen = () => { + // todo dialog类型的插件初始时拿不动插件实例 + this.setState({ + dialogVisible: true, + }); + }; + + renderIcon = clickCallback => { + const { active, disabled, dotted, locked, config, onClick } = this.props; + const { pluginKey, props } = config || {}; + const { icon, title } = props || {}; + const node = ( + { + if (disabled) return; + //考虑到弹窗情况,延时发送消息 + setTimeout( + () => this.appHelper.emit(`${pluginKey}.addon.activate`), + 0, + ); + clickCallback && clickCallback(); + onClick && onClick(); + }} + /> + ); + return dotted ? {node} : node; + }; + + render() { + const { active, dotted, locked, disabled, config, editor, pluginClass: Comp } = this.props; + const { pluginKey, pluginProps, props, type } = config || {}; + const { onClick, title } = props || {}; + const { dialogVisible } = this.state; + if (!pluginKey || !type || !Comp) return null; + const node = { + onClick && onClick.call(null, editor); + }} + {...pluginProps} + />; + + switch (type) { + case 'LinkIcon': + return ( + + {this.renderIcon(() => { + onClick && onClick.call(null, editor); + })} + + ); + case 'Icon': + return this.renderIcon(() => { + onClick && onClick.call(null, editor); + }); + case 'DialogIcon': + return ( + + {this.renderIcon(() => { + onClick && onClick.call(null, editor); + this.handleOpen(); + })} + { + editor.emit(`${pluginKey}.dialog.onOk`); + this.handleClose(); + }} + onCancel={this.handleClose} + onClose={this.handleClose} + title={title} + {...props.dialogProps} + visible={dialogVisible} + > + {node} + + + ); + case 'BalloonIcon': + return ( + { + onClick && onClick.call(null, editor); + })} + triggerType={['click', 'hover']} + {...props.balloonProps} + > + {node} + + ); + case 'Custom': + return dotted ? {node} : node; + default: + return null; + } + } +} diff --git a/packages/editor-skeleton/src/config/skeleton.ts b/packages/editor-skeleton/src/config/skeleton.ts index 5d63e83cc..9e3f6898f 100644 --- a/packages/editor-skeleton/src/config/skeleton.ts +++ b/packages/editor-skeleton/src/config/skeleton.ts @@ -13,7 +13,7 @@ const routerConfig = [ { path: '/', redirect: '/dashboard', - } + }, ], }, ]; diff --git a/packages/editor-skeleton/src/global.scss b/packages/editor-skeleton/src/global.scss index 68689a592..0a710b895 100644 --- a/packages/editor-skeleton/src/global.scss +++ b/packages/editor-skeleton/src/global.scss @@ -1,3 +1,13 @@ +body { + font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, + Arial, PingFang SC-Light, Microsoft YaHei; + font-size: 12px; + padding: 0; + margin: 0; + * { + box-sizing: border-box; + } +} .next-loading { .next-loading-wrap { height: 100%; @@ -6,17 +16,17 @@ .lowcode-editor { .lowcode-main-content { position: absolute; - top: 54px; + top: 48px; left: 0; right: 0; bottom: 0; display: flex; + background-color: #d8d8d8; } .lowcode-center-area { flex: 1; display: flex; flex-direction: column; - background: #f7f7f7; padding: 10px; overflow: auto; } diff --git a/packages/editor-skeleton/src/index.tsx b/packages/editor-skeleton/src/index.tsx index 2a9643b69..3f1c89522 100644 --- a/packages/editor-skeleton/src/index.tsx +++ b/packages/editor-skeleton/src/index.tsx @@ -1,4 +1,4 @@ -import React, {PureComponent} from 'react-dom'; +import React, { PureComponent } from 'react'; // import Editor from '@ali/lowcode-engine-editor'; import { Loading, ConfigProvider } from '@alifd/next'; @@ -17,6 +17,12 @@ export default class Skeleton extends PureComponent { constructor(props) { super(props); // this.editor = new Editor(props.config, props.utils); + this.editor = { + on: () => {}, + off: () => {}, + config: props.config, + pluginComponents: props.pluginComponents + }; } componentWillUnmount() { @@ -26,24 +32,26 @@ export default class Skeleton extends PureComponent { render() { const { location, history, messages } = this.props; - + this.editor.location = location; + this.editor.history = history; + this.editor.messages = messages; return ( - +
- {/* +
- - - - -
*/} + + + + +
diff --git a/packages/editor-skeleton/src/layouts/CenterArea/index.scss b/packages/editor-skeleton/src/layouts/CenterArea/index.scss index e69de29bb..b2584ed2b 100644 --- a/packages/editor-skeleton/src/layouts/CenterArea/index.scss +++ b/packages/editor-skeleton/src/layouts/CenterArea/index.scss @@ -0,0 +1,3 @@ +.lowcode-center-area { + padding: 12px; +} diff --git a/packages/editor-skeleton/src/layouts/CenterArea/index.tsx b/packages/editor-skeleton/src/layouts/CenterArea/index.tsx index 36263969c..dc2b38d25 100644 --- a/packages/editor-skeleton/src/layouts/CenterArea/index.tsx +++ b/packages/editor-skeleton/src/layouts/CenterArea/index.tsx @@ -1,4 +1,4 @@ -import React, {PureComponent} from 'react'; +import React, { PureComponent } from 'react'; import './index.scss'; @@ -10,8 +10,6 @@ export default class CenterArea extends PureComponent { } render() { - return ( -
- ); + return
; } -} \ No newline at end of file +} diff --git a/packages/editor-skeleton/src/layouts/LeftArea/index.scss b/packages/editor-skeleton/src/layouts/LeftArea/index.scss index e69de29bb..dac1b6b0a 100644 --- a/packages/editor-skeleton/src/layouts/LeftArea/index.scss +++ b/packages/editor-skeleton/src/layouts/LeftArea/index.scss @@ -0,0 +1,21 @@ +.lowcode-left-area-nav { + width: 48px; + height: 100%; + background: #ffffff; + border-right: 1px solid #e8ebee; + position: relative; + .top-area { + position: absolute; + top: 0; + width: 100%; + background: #ffffff; + max-height: 100%; + } + .bottom-area { + position: absolute; + bottom: 20px; + width: 100%; + background: #ffffff; + max-height: calc(100% - 20px); + } +} diff --git a/packages/editor-skeleton/src/layouts/LeftArea/index.tsx b/packages/editor-skeleton/src/layouts/LeftArea/index.tsx index f77a9b830..805a5e014 100644 --- a/packages/editor-skeleton/src/layouts/LeftArea/index.tsx +++ b/packages/editor-skeleton/src/layouts/LeftArea/index.tsx @@ -3,5 +3,5 @@ import Panel from './panel'; export default { Nav, - Panel -}; \ No newline at end of file + Panel, +}; diff --git a/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx b/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx index 5c1fbea92..6c58c12ef 100644 --- a/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx +++ b/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx @@ -1,4 +1,4 @@ -import React, {PureComponent} from 'react'; +import React, { PureComponent } from 'react'; import './index.scss'; @@ -10,8 +10,6 @@ export default class LeftAreaPanel extends PureComponent { } render() { - return ( -
- ); + return
; } -} \ No newline at end of file +} diff --git a/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx b/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx index c378ad929..ab41860fb 100644 --- a/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx +++ b/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx @@ -1,4 +1,4 @@ -import React, {PureComponent} from 'react'; +import React, { PureComponent } from 'react'; import './index.scss'; @@ -10,8 +10,6 @@ export default class LeftAreaPanel extends PureComponent { } render() { - return ( -
- ); + return
; } -} \ No newline at end of file +} diff --git a/packages/editor-skeleton/src/layouts/RightArea/index.scss b/packages/editor-skeleton/src/layouts/RightArea/index.scss index e69de29bb..120ef4f11 100644 --- a/packages/editor-skeleton/src/layouts/RightArea/index.scss +++ b/packages/editor-skeleton/src/layouts/RightArea/index.scss @@ -0,0 +1,157 @@ +.lowcode-right-area { + width: 300px; + height: 100%; + background-color: #ffffff; + border-left: 1px solid #e8ebee; + .right-plugin-title { + &.locked { + color: red !important; + } + &.active { + color: $color-brand1-9 !important; + } + &.disabled { + cursor: not-allowed; + color: $color-text1-1; + } + } + + //tab定义 + .next-tabs-wrapped.right-tabs { + display: flex; + flex-direction: column; + margin-top: -1px; + .next-tabs-bar { + z-index: 1; + } + .next-tabs-nav { + display: block; + .next-tabs-tab { + &:first-child { + border-left: none; + } + font-size: 14px; + text-align: center; + border-right: none !important; + margin-right: 0 !important; + width: 25%; + &.active { + background: none; + border-bottom-color: #f7f7f7 !important; + } + } + } + } + .next-tabs-content { + flex: 1; + .next-tabs-tabpane.active { + height: 100%; + overflow-y: auto; + } + } + //组件 + .select-comp { + padding: 10px 16px; + line-height: 16px; + color: #989a9c; + & > span { + font-size: 12px; + line-height: 16px; + font-weight: 400; + } + & > .btn-wrap, + & > .next-btn { + width: auto; + margin: 0 5px; + float: right; + } + } + + .unselected { + padding: 60px 0; + text-align: center; + } + //右侧属性面板样式调整; + .offset-56 { + padding-left: 56px; + margin-bottom: 16px; + overflow: hidden; + } + .fixedSpan.next-form-item { + & > .next-form-item-label { + width: 56px; + flex: none; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + & > .next-form-item-control { + padding-right: 24px; + } + } + .fixedSpan.next-form-item, + .offset-56 .next-form-item { + display: flex; + & > .next-form-item-control { + width: auto; + flex: 1; + max-width: none; + .next-input, + .next-select, + .next-radio-group, + .next-number-picker, + .luna-reactnode-btn, + .luna-monaco-button button, + .luna-object-button button { + width: 100%; + } + .next-number-picker { + width: 100%; + .next-after { + padding-right: 5px; + } + } + .next-radio-group { + display: flex; + label { + flex: 1; + text-align: center; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + } + } + } + .topSpan.next-form-item { + margin-bottom: 4px; + & > .next-form-item-control { + padding-right: 24px; + .next-input, + .next-select, + .next-radio-group, + .next-number-picker, + .luna-reactnode-btn, + .luna-monaco-button button, + .luna-object-button button { + width: 100%; + } + .next-number-picker { + width: 100%; + .next-after { + padding-right: 5px; + } + } + .next-radio-group { + display: flex; + label { + flex: 1; + text-align: center; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + } + } + } +} diff --git a/packages/editor-skeleton/src/layouts/RightArea/index.tsx b/packages/editor-skeleton/src/layouts/RightArea/index.tsx index 449a273e5..31273a4e2 100644 --- a/packages/editor-skeleton/src/layouts/RightArea/index.tsx +++ b/packages/editor-skeleton/src/layouts/RightArea/index.tsx @@ -1,4 +1,4 @@ -import React, {PureComponent} from 'react'; +import React, { PureComponent } from 'react'; import './index.scss'; @@ -10,8 +10,6 @@ export default class RightArea extends PureComponent { } render() { - return ( -
- ); + return
; } -} \ No newline at end of file +} diff --git a/packages/editor-skeleton/src/layouts/TopArea/index.scss b/packages/editor-skeleton/src/layouts/TopArea/index.scss index e69de29bb..ca8bbd825 100644 --- a/packages/editor-skeleton/src/layouts/TopArea/index.scss +++ b/packages/editor-skeleton/src/layouts/TopArea/index.scss @@ -0,0 +1,5 @@ +.lowcode-top-area { + height: 48px; + background-color: #ffffff; + border-bottom: 1px solid #e8ebee; +} diff --git a/packages/editor-skeleton/src/layouts/TopArea/index.tsx b/packages/editor-skeleton/src/layouts/TopArea/index.tsx index 3bc7e3562..c2f60a637 100644 --- a/packages/editor-skeleton/src/layouts/TopArea/index.tsx +++ b/packages/editor-skeleton/src/layouts/TopArea/index.tsx @@ -1,17 +1,79 @@ -import React, {PureComponent} from 'react'; - +import React, { PureComponent } from 'react'; +import { Grid } from '@alifd/next'; +import TopPlugin from '../../components/TopPlugin'; import './index.scss'; +const { Row, Col } = Grid; + export default class TopArea extends PureComponent { static displayName = 'lowcodeTopArea'; constructor(props) { super(props); + this.editor = props.editor; + this.config = this.editor.config.plugins && this.editor.config.plugins.topArea; } + componentDidMount() { + } + componentWillUnmount() { + } + + handlePluginStatusChange = () => {}; + + renderPluginList = (list = []) => { + return list.map((item, idx) => { + const isDivider = item.type === 'Divider'; + + return ( + + {!isDivider && ( + + )} + + ); + }); + }; + render() { + if (!this.config) return null; + const leftList = []; + const rightList = []; + this.config.forEach(item => { + const align = + item.props && item.props.align === 'right' ? 'right' : 'left'; + // 分隔符不允许相邻 + if (item.type === 'Divider') { + const currentList = align === 'right' ? rightList : leftList; + if ( + currList.length === 0 || + currList[currList.length - 1].type === 'Divider' + ) + return; + } + if (align === 'right') { + rightList.push(item); + } else { + leftList.push(item); + } + }); + return ( -
+
+
{this.renderPluginList(leftList)}
+
{this.renderPluginList(rightList)}
+
); } -} \ No newline at end of file +} diff --git a/packages/editor-skeleton/src/locale/en-US.js b/packages/editor-skeleton/src/locale/en-US.js index 936701e33..36e3b219c 100644 --- a/packages/editor-skeleton/src/locale/en-US.js +++ b/packages/editor-skeleton/src/locale/en-US.js @@ -6,5 +6,5 @@ export default { pageNotExist: 'The current Page not exist', enterFromAppCenter: 'Please enter from the app center', noPermission: 'Sorry, you do not have the develop permission', - getPermission: 'Please connect the app owners {owners} to get the permission' + getPermission: 'Please connect the app owners {owners} to get the permission', }; diff --git a/packages/editor-skeleton/src/locale/zh-CN.js b/packages/editor-skeleton/src/locale/zh-CN.js index efe4ea898..2d5229d2c 100644 --- a/packages/editor-skeleton/src/locale/zh-CN.js +++ b/packages/editor-skeleton/src/locale/zh-CN.js @@ -6,5 +6,5 @@ export default { pageNotExist: '当前访问地址不存在', enterFromAppCenter: '请从应用中心入口重新进入', noPermission: '抱歉,您暂无开发权限', - getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限' + getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限', }; diff --git a/packages/editor/ice.config.js b/packages/editor/ice.config.js index f905f88e8..b40ce581a 100644 --- a/packages/editor/ice.config.js +++ b/packages/editor/ice.config.js @@ -8,7 +8,7 @@ module.exports = { }, plugins: [ ['ice-plugin-fusion', { - themePackage: '@icedesign/theme', + themePackage: '@alife/dpl-iceluna', }], ['ice-plugin-moment-locales', { locales: ['zh-cn'], diff --git a/packages/editor/package.json b/packages/editor/package.json index 5f31e94e0..df3f52b9c 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -4,9 +4,11 @@ "description": "低代码编辑器", "dependencies": { "@alifd/next": "^1.x", + "@alife/dpl-iceluna": "^2.3.2", "@icedesign/theme": "^1.x", "@types/react": "^16.8.3", "@types/react-dom": "^16.8.2", + "keymaster": "^1.6.2", "moment": "^2.23.0", "prop-types": "^15.5.8", "react": "^16.4.1", @@ -20,6 +22,7 @@ "ice-plugin-fusion": "^0.1.4", "ice-plugin-moment-locales": "^0.1.0", "ice-scripts": "^2.0.0", + "prettier": "^1.19.1", "stylelint": "^10.1.0" }, "scripts": { @@ -27,7 +30,8 @@ "build": "ice-scripts build", "lint": "npm run eslint && npm run stylelint", "eslint": "eslint --cache --ext .js,.jsx ./", - "stylelint": "stylelint ./**/*.scss" + "stylelint": "stylelint ./**/*.scss", + "prettier": "prettier --write \"./src/**/*.{ts,tsx,js,jsx,ejs,less,css,scss,json}\" " }, "engines": { "node": ">=8.0.0" diff --git a/packages/editor/src/config/components.js b/packages/editor/src/config/components.js index 96acc32a7..901075ab6 100644 --- a/packages/editor/src/config/components.js +++ b/packages/editor/src/config/components.js @@ -1,3 +1,24 @@ +import topBalloonIcon from '@ali/iceluna-addon-2'; +import topDialogIcon from '@ali/iceluna-addon-2'; +import leftPanelIcon from '@ali/iceluna-addon-2'; +import leftBalloonIcon from '@ali/iceluna-addon-2'; +import leftDialogIcon from '@ali/iceluna-addon-2'; +import rightPanel1 from '@ali/iceluna-addon-2'; +import rightPanel2 from '@ali/iceluna-addon-2'; +import rightPanel3 from '@ali/iceluna-addon-2'; +import rightPanel4 from '@ali/iceluna-addon-2'; + +import PluginFactory from '../framework/plugin'; + export default { -}; \ No newline at end of file + topBalloonIcon: PluginFactory(topBalloonIcon), + topDialogIcon: PluginFactory(topDialogIcon), + leftPanelIcon: PluginFactory(leftPanelIcon), + leftBalloonIcon:PluginFactory(leftBalloonIcon), + leftDialogIcon:PluginFactory(leftDialogIcon), + rightPanel1:PluginFactory(rightPanel1), + rightPanel2:PluginFactory(rightPanel2), + rightPanel3: PluginFactory(rightPanel3), + rightPanel4: PluginFactory(rightPanel4), +}; diff --git a/packages/editor/src/config/constants.js b/packages/editor/src/config/constants.js index 1c713fc4e..d20e24f41 100644 --- a/packages/editor/src/config/constants.js +++ b/packages/editor/src/config/constants.js @@ -1,3 +1,3 @@ export default { - "namespace": "page" -} \ No newline at end of file + namespace: 'page', +}; diff --git a/packages/editor/src/config/locale/en-US.js b/packages/editor/src/config/locale/en-US.js index 7c645e42f..ff8b4c563 100644 --- a/packages/editor/src/config/locale/en-US.js +++ b/packages/editor/src/config/locale/en-US.js @@ -1 +1 @@ -export default {}; \ No newline at end of file +export default {}; diff --git a/packages/editor/src/config/locale/index.js b/packages/editor/src/config/locale/index.js index eaa4da099..5bc4373a4 100644 --- a/packages/editor/src/config/locale/index.js +++ b/packages/editor/src/config/locale/index.js @@ -6,5 +6,5 @@ export default { 'en-US': en_us, 'zh-CN': zh_cn, 'zh-TW': zh_tw, - 'ja-JP': ja_jp -}; \ No newline at end of file + 'ja-JP': ja_jp, +}; diff --git a/packages/editor/src/config/locale/ja-JP.js b/packages/editor/src/config/locale/ja-JP.js index 7c645e42f..ff8b4c563 100644 --- a/packages/editor/src/config/locale/ja-JP.js +++ b/packages/editor/src/config/locale/ja-JP.js @@ -1 +1 @@ -export default {}; \ No newline at end of file +export default {}; diff --git a/packages/editor/src/config/locale/zh-CN.js b/packages/editor/src/config/locale/zh-CN.js index 7c645e42f..ff8b4c563 100644 --- a/packages/editor/src/config/locale/zh-CN.js +++ b/packages/editor/src/config/locale/zh-CN.js @@ -1 +1 @@ -export default {}; \ No newline at end of file +export default {}; diff --git a/packages/editor/src/config/locale/zh-TW.js b/packages/editor/src/config/locale/zh-TW.js index 7c645e42f..ff8b4c563 100644 --- a/packages/editor/src/config/locale/zh-TW.js +++ b/packages/editor/src/config/locale/zh-TW.js @@ -1 +1 @@ -export default {}; \ No newline at end of file +export default {}; diff --git a/packages/editor/src/config/skeleton.js b/packages/editor/src/config/skeleton.js index 3b3841521..87d035f4a 100644 --- a/packages/editor/src/config/skeleton.js +++ b/packages/editor/src/config/skeleton.js @@ -1,435 +1,215 @@ export default { - "skeleton": { - "config": { - "package": "@ali/lowcode-skeleton", - "version": "0.0.1" + version: '^1.0.2', + theme: { + dpl: { + package: '@alife/dpl-iceluna', + version: '^2.3.0', }, + scss: '', }, - "theme": { - "fusion": { - "package": "@alife/dpl-iceluna", - "version": "^2.3.0" - }, - "scss": "" + constants: { + namespace: 'page', }, - "constants": { - "namespace": "page" + utils: [], + plugins: { + topArea: [ + { + pluginKey: 'topBalloonIcon', + type: 'BalloonIcon', + props: { + align: 'left', + title: 'balloon', + icon: 'dengpao', + balloonProps: { + triggerType: 'click', + }, + }, + config: { + package: '@ali/iceluna-addon-2', + version: '^1.0.0', + }, + pluginProps: {}, + }, + { + pluginKey: 'divider', + type: 'Divider', + props: { + align: 'left', + }, + }, + { + pluginKey: 'topDialogIcon', + type: 'DialogIcon', + props: { + align: 'left', + title: 'dialog', + icon: 'dengpao', + }, + config: { + package: '@ali/iceluna-addon-2', + version: '^1.0.0', + }, + pluginProps: {}, + }, + { + pluginKey: 'topLinkIcon', + type: 'LinkIcon', + props: { + align: 'right', + title: 'link', + icon: 'dengpao', + linkProps: { + href: '//www.taobao.com', + target: 'blank', + }, + }, + config: {}, + pluginProps: {}, + }, + { + pluginKey: 'topIcon', + type: 'Icon', + props: { + align: 'right', + title: 'icon', + icon: 'dengpao', + onClick: function(editor) { + alert('icon addon invoke, current activeKey: ' + editor.activeKey); + }, + }, + config: {}, + pluginProps: {}, + }, + ], + leftArea: [ + { + pluginKey: 'leftPanelIcon', + type: 'PanelIcon', + props: { + align: 'top', + title: 'panel', + icon: 'dengpao', + }, + config: { + package: '@ali/iceluna-addon-2', + version: '^1.0.0', + }, + pluginProps: {}, + }, + { + pluginKey: 'leftBalloonIcon', + type: 'BalloonIcon', + props: { + align: 'top', + title: 'balloon', + icon: 'dengpao', + }, + config: { + package: '@ali/iceluna-addon-2', + version: '^1.0.0', + }, + pluginProps: {}, + }, + { + pluginKey: 'leftDialogIcon', + type: 'DialogIcon', + props: { + align: 'bottom', + title: 'dialog', + icon: 'dengpao', + }, + config: { + package: '@ali/iceluna-addon-2', + version: '^1.0.0', + }, + pluginProps: {}, + }, + { + pluginKey: 'leftLinkIcon', + type: 'LinkIcon', + props: { + align: 'bottom', + title: 'link', + icon: 'dengpao', + linkProps: { + href: '//www.taobao.com', + target: 'blank', + }, + }, + config: {}, + pluginProps: {}, + }, + { + pluginKey: 'leftIcon', + type: 'Icon', + props: { + align: 'bottom', + title: 'icon', + icon: 'dengpao', + onClick: function(editor) { + alert('icon addon invoke, current activeKey: ' + editor.activeKey); + }, + }, + config: {}, + pluginProps: {}, + }, + ], + rightArea: [ + { + pluginKey: 'rightPanel1', + type: 'Panel', + props: { + title: 'panel1', + icon: 'dengpao', + }, + config: { + package: '@ali/iceluna-addon-2', + version: '^1.0.0', + }, + pluginProps: {}, + }, + { + pluginKey: 'rightPanel2', + type: 'Panel', + props: { + title: 'panel2', + icon: 'dengpao', + }, + config: { + package: '@ali/iceluna-addon-2', + version: '^1.0.0', + }, + pluginProps: {}, + }, + { + pluginKey: 'rightPanel3', + type: 'Panel', + props: { + title: 'panel3', + icon: 'dengpao', + }, + config: { + package: '@ali/iceluna-addon-2', + version: '^1.0.0', + }, + pluginProps: {}, + }, + { + pluginKey: 'rightPanel4', + type: 'Panel', + props: { + title: 'panel4', + icon: 'dengpao', + }, + config: { + package: '@ali/iceluna-addon-2', + version: '^1.0.0', + }, + pluginProps: {}, + }, + ], + centerArea: [], }, - "utils": [], - "plugins": { - "topArea": [{ - "pluginKey": "logo", - "type": "Custom", - "props": { - "width": 110, - "align": "left" - }, - "config": { - "package": "@ali/iceluna-addon-logo", - "version": "^1.0.2" - }, - "pluginProps": {} - }, { - "pluginKey": "divider", - "type": "Divider", - "props": { - "align": "left" - } - }, { - "pluginKey": "pageList", - "type": "Custom", - "props": { - "align": "left", - "width": 360 - }, - "config": { - "package": "@ali/iceluna-addon-page-list", - "version": "^1.0.11" - }, - "pluginProps": {} - }, { - "pluginKey": "partner", - "type": "Custom", - "props": { - "align": "right", - "width": 200 - }, - "config": { - "package": "@ali/iceluna-addon-partner", - "version": "^1.0.3" - }, - "pluginProps": {} - }, { - "pluginKey": "divider", - "type": "Divider", - "props": { - "align": "right" - } - }, { - "pluginKey": "designMode", - "type": "Custom", - "props": { - "align": "right", - "width": 144 - }, - "config": { - "package": "@ali/iceluna-addon-design-mode", - "version": "^1.0.3" - }, - "pluginProps": {} - }, { - "pluginKey": "divider", - "type": "Divider", - "props": { - "align": "right" - } - }, { - "pluginKey": "undoRedo", - "type": "Custom", - "props": { - "align": "right", - "width": 88 - }, - "config": { - "package": "@ali/iceluna-addon-undo-redo", - "version": "^1.0.3" - }, - "pluginProps": {} - }, { - "pluginKey": "d2c", - "type": "Custom", - "props": { - "align": "right", - "width": 44 - }, - "config": { - "package": "@ali/iceluna-addon-d2c", - "version": "^1.0.1" - }, - "pluginProps": {} - }, { - "pluginKey": "history", - "type": "DialogIcon", - "props": { - "align": "right", - "icon": "lishijilu1", - "title": "历史", - "dialogProps": { - "title": "历史记录", - "footer": false, - "shouldUpdatePosition": true - } - }, - "config": { - "package": "@ali/iceluna-addon-history", - "version": "^1.0.3" - }, - "pluginProps": {} - }, { - "pluginKey": "refresh", - "type": "Icon", - "props": { - "align": "right", - "icon": "shuaxin", - "title": "刷新", - "onClick": function(appHelper) { - appHelper.emit('ide.reset'); - } - } - }, { - "pluginKey": "divider", - "type": "Divider", - "props": { - "align": "right" - } - }, { - "pluginKey": "save", - "type": "Custom", - "props": { - "align": "right", - "width": 86 - }, - "config": { - "package": "@ali/iceluna-addon-save", - "version": "^1.0.3" - }, - "pluginProps": {} - }, { - "pluginKey": "preview", - "type": "Custom", - "props": { - "align": "right", - "width": 86 - }, - "config": { - "package": "@ali/iceluna-addon-preview", - "version": "^1.0.1" - }, - "pluginProps": {} - }, { - "pluginKey": "publish", - "type": "Custom", - "props": { - "align": "right", - "width": 104 - }, - "config": { - "package": "@ali/iceluna-addon-publish", - "version": "^1.0.1" - }, - "pluginProps": {} - }], - "leftArea": [{ - "pluginKey": "componentTree", - "type": "PanelIcon", - "props": { - "align": "top", - "icon": "shuxingkongjian", - "title": "组件树", - "panelProps": { - "minWidth": 100, - "maxWidth": 500 - } - }, - "config": { - "package": "@ali/iceluna-addon-component-tree", - "version": "^1.0.5" - }, - "pluginProps": {} - }, { - "pluginKey": "componentList", - "type": "PanelIcon", - "props": { - "align": "top", - "icon": "zujianku", - "title": "组件库" - }, - "config": { - "package": "@ali/iceluna-addon-component-list", - "version": "^1.0.4" - }, - "pluginProps": {} - }, { - "pluginKey": "blockList", - "type": "PanelIcon", - "props": { - "align": "top", - "icon": "jihe", - "title": "区块库" - }, - "config": { - "package": "@ali/iceluna-addon-block-list", - "version": "^1.0.2" - }, - "pluginProps": {} - }, { - "pluginKey": "schema", - "type": "PanelIcon", - "props": { - "align": "bottom", - "icon": "ceshi", - "title": "schema 源码开发", - "panelProps": { - "defaultWidth": 480 - } - }, - "config": { - "package": "@ali/iceluna-addon-schema", - "version": "^1.0.3" - }, - "pluginProps": {} - }, { - "pluginKey": "style", - "type": "PanelIcon", - "props": { - "align": "bottom", - "icon": "SCSS", - "title": "scss 全局样式设置", - "panelProps": { - "defaultWidth": 480 - } - }, - "config": { - "package": "@ali/iceluna-addon-style", - "version": "^1.0.2" - }, - "pluginProps": {} - }, { - "pluginKey": "utils", - "type": "PanelIcon", - "props": { - "align": "bottom", - "icon": "funcsgaiban", - "title": "utils 全局公共函数设置", - "panelProps": { - "defaultWidth": 540 - } - }, - "config": { - "package": "@ali/iceluna-addon-utils", - "version": "^1.0.7" - }, - "pluginProps": {} - }, { - "pluginKey": "constants", - "type": "PanelIcon", - "props": { - "align": "bottom", - "icon": "constgaiban", - "title": "constants 全局常量设置", - "panelProps": { - "defaultWidth": 480 - } - }, - "config": { - "package": "@ali/iceluna-addon-constants", - "version": "^1.0.3" - }, - "pluginProps": {} - }, { - "pluginKey": "package", - "type": "PanelIcon", - "props": { - "align": "bottom", - "icon": "packagegaiban", - "title": "package.json 应用设置", - "panelProps": { - "defaultWidth": 480 - } - }, - "config": { - "package": "@ali/iceluna-addon-package", - "version": "^1.0.2" - }, - "pluginProps": {} - }, { - "pluginKey": "canvasSetting", - "type": "PanelIcon", - "props": { - "align": "bottom", - "icon": "huabushezhi", - "title": "canvas 画布配置", - "panelProps": { - "defaultWidth": 300 - } - }, - "config": { - "package": "@ali/iceluna-addon-canvas-setting", - "version": "^1.0.2" - }, - "pluginProps": {} - }, { - "pluginKey": "issue", - "type": "LinkIcon", - "props": { - "align": "bottom", - "icon": "chongzi", - "title": "issue 问题反馈", - "linkProps": { - "href": "//work.aone.alibaba-inc.com/project/860698/issue/new", - "target": "blank" - } - } - }, { - "pluginKey": "document", - "type": "LinkIcon", - "props": { - "align": "bottom", - "icon": "wendangzhongxin", - "title": "docs 文档中心", - "linkProps": { - "href": "https://iceluna.alibaba-inc.com/#/document", - "target": "blank" - } - } - }], - "rightArea": [{ - "pluginKey": "componentStyle", - "props": { - "title": "样式" - }, - "config": { - "package": "@ali/iceluna-addon-component-style", - "version": "^1.0.8" - } - }, { - "pluginKey": "componentAttr", - "props": { - "title": "属性" - }, - "config": { - "package": "@ali/iceluna-addon-component-attr", - "version": "^1.0.3" - }, - "pluginProps": {} - }, { - "pluginKey": "componentEvent", - "props": { - "title": "事件" - }, - "config": { - "package": "@ali/iceluna-addon-component-event", - "version": "^1.0.4" - }, - "pluginProps": {} - }, { - "pluginKey": "componentData", - "props": { - "title": "数据" - }, - "config": { - "package": "@ali/iceluna-addon-component-data", - "version": "^1.0.3" - }, - "pluginProps": {} - }], - "centerArea": [{ - "pluginKey": "canvas", - "config": { - "package": "@ali/iceluna-addon-canvas", - "version": "^1.0.8" - } - }, { - "pluginKey": "guide", - "config": { - "package": "@ali/iceluna-addon-guide", - "version": "^1.0.1" - } - }] - }, - "hooks": [{ - "message": "wsHelper.result.updateInfo", - "type": "on", - "handler": function(appHelper, data) { - const pageInfo = appHelper.pageInfo; - if (data && data.code > 0 && pageInfo) { - const { - clientLocks, - entityLocks, - entityUsers, - entityPubInfo - } = data.data; - if (JSON.stringify(clientLocks || {}) !== JSON.stringify(appHelper.clientLocks || {})) { - clientLocks.schema = clientLocks[pageInfo.id]; - appHelper.set('clientLocks', clientLocks); - appHelper.emit('wsHelper.update.clientLocks', clientLocks); - } - if (JSON.stringify(entityLocks || {}) !== JSON.stringify(appHelper.entityLocks || {})) { - entityLocks.schema = entityLocks[pageInfo.id]; - appHelper.set('entityLocks', entityLocks); - appHelper.emit('wsHelper.update.entityLocks', entityLocks); - } - if (JSON.stringify(entityUsers || {}) !== JSON.stringify(appHelper.entityUsers || {})) { - appHelper.set('entityUsers', entityUsers); - appHelper.emit('wsHelper.update.entityUsers', entityUsers); - } - if (JSON.stringify(entityPubInfo || {}) !== JSON.stringify(appHelper.entityPubInfo || {})) { - appHelper.set('entityPubInfo', entityPubInfo); - appHelper.emit('wsHelper.update.entityPubInfo', entityPubInfo); - } - } - } - }], - "shortCuts": [], - "lifeCycles": {} -}; \ No newline at end of file + hooks: [], + shortCuts: [], +}; diff --git a/packages/editor/src/config/utils.js b/packages/editor/src/config/utils.js index dd651ca49..ff8b4c563 100644 --- a/packages/editor/src/config/utils.js +++ b/packages/editor/src/config/utils.js @@ -1,3 +1 @@ -export default { - -}; \ No newline at end of file +export default {}; diff --git a/packages/editor/src/framework/context.ts b/packages/editor/src/framework/context.ts new file mode 100644 index 000000000..78d3ce177 --- /dev/null +++ b/packages/editor/src/framework/context.ts @@ -0,0 +1,3 @@ +import { createContext } from 'react'; +const context = createContext({}); +export default context; diff --git a/packages/editor/src/framework/definitions.ts b/packages/editor/src/framework/definitions.ts new file mode 100644 index 000000000..73655b11e --- /dev/null +++ b/packages/editor/src/framework/definitions.ts @@ -0,0 +1,41 @@ +export interface EditorConfig {} + +export interface NpmConfig { + version: string; + package: string; + main?: string; + exportName?: string; + subName?: string; + destructuring?: boolean; +} + +export interface SkeletonConfig { + config: NpmConfig; + props?: object; + handler?: (EditorConfig) => EditorConfig; +} + +export interface FusionTheme { + package: string; + version: string; +} + +export interface ThemeConfig { + fusion?: FusionTheme; +} + +export interface PluginsConfig { + [key]: Array; +} + +export interface PluginConfig { + pluginKey: string; + type: string; + props: object; + config: NpmConfig; + pluginProps: object; +} + +export type HooksConfig = Array; + +export interface HookConfig {} diff --git a/packages/editor/src/framework/editor.ts b/packages/editor/src/framework/editor.ts new file mode 100644 index 000000000..78f1209ab --- /dev/null +++ b/packages/editor/src/framework/editor.ts @@ -0,0 +1,186 @@ +import EventEmitter from 'events'; +import Debug from 'debug'; +import store from 'store'; + +import { + unRegistShortCuts, + registShortCuts, + transformToPromise, + generateI18n, +} from './utils'; + +// 根据url参数设置debug选项 +const res = /_?debug=(.*?)(&|$)/.exec(location.search); +if (res && res[1]) { + window.__isDebug = true; + store.storage.write('debug', res[1] === 'true' ? '*' : res[1]); +} else { + window.__isDebug = false; + store.remove('debug'); +} + +//重要,用于矫正画布执行new Function的window对象上下文 +window.__newFunc = funContext => { + return new Function(funContext); +}; + +//关闭浏览器前提醒,只有产生过交互才会生效 +window.onbeforeunload = function(e) { + e = e || window.event; + // 本地调试不生效 + if (location.href.indexOf('localhost') > 0) return; + var msg = '您确定要离开此页面吗?'; + e.cancelBubble = true; + e.returnValue = msg; + if (e.stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } + return msg; +}; + +let instance = null; +const debug = Debug('editor'); +EventEmitter.defaultMaxListeners = 100; + + +export default class Editor extends EventEmitter { + static getInstance = () => { + if (!instance) { + instance = new Editor(); + } + return instance; + }; + + constructor(config, utils, components) { + super(); + instance = this; + this.config = config; + this.utils = utils; + this.components = components; + this.init(); + } + + init() { + const { hooks, shortCuts, lifeCycles } = this.config || {}; + this.locale = store.get('lowcode-editor-locale') || 'zh-CN'; + // this.messages = this.messagesSet[this.locale]; + // this.i18n = generateI18n(this.locale, this.messages); + this.pluginStatus = this.initPluginStatus(); + this.initHooks(hooks); + + this.emit('editor.beforeInit'); + const init = (lifeCycles && lifeCycles.init) || (() => {}); + // 用户可以通过设置extensions.init自定义初始化流程; + return transformToPromise(init(this)) + .then(() => { + // 注册快捷键 + registShortCuts(shortCuts, this); + this.emit('editor.afterInit'); + }) + .catch(err => { + console.error(err); + }); + } + + destroy() { + try { + const { hooks = [], shortCuts = [], lifeCycles = {} } = this.config; + unRegistShortCuts(shortCuts); + this.destroyHooks(hooks); + lifeCycles.destroy && lifeCycles.destroy(); + } catch (err) { + console.warn(err); + return; + } + } + + get(key: string): any { + return this[key]; + } + + set(key: string | object, val: any): void { + if (typeof key === 'string') { + if ( + [ + 'init', + 'destroy', + 'get', + 'set', + 'batchOn', + 'batchOff', + 'batchOnce', + ].includes(key) + ) { + console.warning( + 'init, destroy, get, set, batchOn, batchOff, batchOnce is private attribute', + ); + return; + } + this[key] = val; + } else if (typeof key === 'object') { + Object.keys(key).forEach(item => { + this[item] = key[item]; + }); + } + } + + batchOn(events: Array, lisenter: function): void { + if (!Array.isArray(events)) return; + events.forEach(event => this.on(event, lisenter)); + } + + batchOnce(events: Array, lisenter: function): void { + if (!Array.isArray(events)) return; + events.forEach(event => this.once(event, lisenter)); + } + + batchOff(events: Array, lisenter: function): void { + if (!Array.isArray(events)) return; + events.forEach(event => this.off(event, lisenter)); + } + + //销毁hooks中的消息监听 + private destroyHooks(hooks = []) { + hooks.forEach((item, idx) => { + if (typeof this.__hooksFuncs[idx] === 'function') { + this.off(item.message, this.__hooksFuncs[idx]); + } + }); + delete this.__hooksFuncs; + } + + //初始化hooks中的消息监听 + private initHooks(hooks = []) { + this.__hooksFuncs = hooks.map(item => { + const func = (...args) => { + item.handler(this, ...args); + }; + this[item.type](item.message, func); + return func; + }); + } + + private initPluginStatus() { + const { plugins = {} } = this.config; + const pluginAreas = Object.keys(plugins); + const res = {}; + pluginAreas.forEach(area => { + (plugins[area] || []).forEach(plugin => { + if (plugin.type === 'Divider') return; + const { visible, disabled, dotted } = plugin.props || {}; + res[plugin.pluginKey] = { + visible: typeof visible === 'boolean' ? visible : true, + disabled: typeof disabled === 'boolean' ? disabled : false, + dotted: typeof dotted === 'boolean' ? dotted : false, + }; + const pluginClass = this.components[plugin.pluginKey]; + // 判断如果编辑器插件有init静态方法,则在此执行init方法 + if (pluginClass && pluginClass.init) { + pluginClass.init(this); + } + }); + }); + return res; + } +} diff --git a/packages/editor/src/framework/index.ts b/packages/editor/src/framework/index.ts new file mode 100644 index 000000000..8b82edf2e --- /dev/null +++ b/packages/editor/src/framework/index.ts @@ -0,0 +1,3 @@ +import Editor from './editor'; + +export default Editor; diff --git a/packages/editor/src/framework/plugin.js b/packages/editor/src/framework/plugin.js new file mode 100644 index 000000000..43bf5ef30 --- /dev/null +++ b/packages/editor/src/framework/plugin.js @@ -0,0 +1,54 @@ +import React, { PureComponent, creatRef} from 'react'; + +import EditorContext from './context'; +import { isEmpty, generateI18n, goldlog } from './utils'; + + +export default function plugin(Comp) { + class LowcodePlugin extends PureComponent { + static displayName = 'LowcodeEditorPlugin'; + static defaultProps = { + config: {}, + }; + static contextType = EditorContext; + constructor(props, context) { + super(props, context); + if (isEmpty(props.config) || !props.config.pluginKey) { + console.warn('lowcode editor plugin has wrong config'); + return; + } + this.ref = React.createRef(); + const { locale, messages, editor } = props; + // 注册插件 + this.editor = editor; + this.i18n = generateI18n(locale, messages); + this.pluginKey = props.config.pluginKey; + editor.plugins = editor.plugins || {}; + editor.plugins[this.pluginKey] = this; + } + + componentWillUnmount() { + // 销毁插件 + if (this.editor && this.editor.plugins) { + delete this.editor.plugins[this.pluginKey]; + } + } + + render() { + const { config } = this.props; + return ( + + ); + } + } + + LowcodePlugin.init = Comp.init; + + return LowcodePlugin; +} diff --git a/packages/editor/src/framework/utils.ts b/packages/editor/src/framework/utils.ts new file mode 100644 index 000000000..59a32d270 --- /dev/null +++ b/packages/editor/src/framework/utils.ts @@ -0,0 +1,320 @@ +import IntlMessageFormat from 'intl-messageformat'; +import keymaster from 'keymaster'; +import _isEmpty from 'lodash/isEmpty'; + +export const isEmpty = _isEmpty; + +/** + * 用于构造国际化字符串处理函数 + * @param {*} locale 国际化标识,例如 zh-CN、en-US + * @param {*} messages 国际化语言包 + */ +export function generateI18n(locale = 'zh-CN', messages = {}) { + return (key, values = {}) => { + if (!messages || !messages[key]) return ''; + const formater = new IntlMessageFormat(messages[key], locale); + return formater.format(values); + }; +} + +/** + * 序列化参数 + * @param {*} obj 参数 + */ +export function serializeParams(obj: object): string { + if (typeof obj !== 'object') return ''; + + const res: Array = []; + Object.entries(obj).forEach(([key, val]) => { + if (val === null || val === undefined || val === '') return; + if (typeof val === 'object') { + res.push( + `${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`, + ); + } else { + res.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`); + } + }); + return res.join('&'); +} + +/** + * 黄金令箭埋点 + * @param {String} gmKey 为黄金令箭业务类型 + * @param {Object} params 参数 + * @param {String} logKey 属性串 + */ +export function goldlog(gmKey, params = {}, logKey = 'other') { + const sendIDEMessage = window.sendIDEMessage || window.parent.sendIDEMessage; + const goKey = serializeParams({ + sdkVersion: pkg.version, + env: getEnv(), + ...params, + }); + if (sendIDEMessage) { + sendIDEMessage({ + action: 'goldlog', + data: { + logKey: `/iceluna.core.${logKey}`, + gmKey, + goKey, + }, + }); + } + window.goldlog && + window.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST'); +} + +/** + * 获取当前编辑器环境 + */ +export function getEnv() { + const userAgent = navigator.userAgent; + const isVscode = /Electron\//.test(userAgent); + if (isVscode) return ENV.VSCODE; + const isTheia = window.is_theia === true; + if (isTheia) return ENV.WEBIDE; + return ENV.WEB; +} + +// 注册快捷键 +export function registShortCuts(config, editor) { + const keyboardFilter = (keymaster.filter = event => { + let eTarget = event.target || event.srcElement; + let tagName = eTarget.tagName; + let isInput = !!( + tagName == 'INPUT' || + tagName == 'SELECT' || + tagName == 'TEXTAREA' + ); + let isContenteditable = !!eTarget.getAttribute('contenteditable'); + if (isInput || isContenteditable) { + if (event.metaKey === true && [70, 83].includes(event.keyCode)) + event.preventDefault(); //禁止触发chrome原生的页面保存或查找 + return false; + } else { + return true; + } + }); + + const ideMessage = editor.utils && editor.utils.ideMessage; + + //复制 + if (!document.copyListener) { + document.copyListener = e => { + if (!keyboardFilter(e) || editor.isCopying) return; + const schema = + editor.schemaHelper && + editor.schemaHelper.schemaMap[editor.activeKey]; + if (!schema || !isSchema(schema)) return; + editor.isCopying = true; + const schemaStr = serialize(transformSchemaToPure(schema), { + unsafe: true, + }); + setClipboardData(schemaStr) + .then(() => { + ideMessage && + ideMessage( + 'success', + '当前内容已复制到剪贴板,请使用快捷键Command+v进行粘贴', + ); + editor.emit('schema.copy', schemaStr, schema); + editor.isCopying = false; + }) + .catch(errMsg => { + ideMessage && ideMessage('error', errMsg); + editor.isCopying = false; + }); + }; + document.addEventListener('copy', document.copyListener); + if (window.parent.vscode) { + keymaster('command+c', document.copyListener); + } + } + + //粘贴 + if (!document.pasteListener) { + const doPaste = (e, text) => { + if (!keyboardFilter(e) || editor.isPasting) return; + const schemaHelper = editor.schemaHelper; + let targetKey = editor.activeKey; + let direction = 'after'; + const topKey = + schemaHelper.schema && + schemaHelper.schema.__ctx && + schemaHelper.schema.__ctx.lunaKey; + if (!targetKey || topKey === targetKey) { + const schemaHelper = editor.schemaHelper; + const topKey = + schemaHelper.schema && + schemaHelper.schema.__ctx && + schemaHelper.schema.__ctx.lunaKey; + if (!topKey) return; + targetKey = topKey; + direction = 'in'; + } + editor.isPasting = true; + const schema = parseObj(text); + if (!isSchema(schema)) { + editor.emit('illegalSchema.paste', text); + // ideMessage && ideMessage('error', '当前内容不是模型结构,不能粘贴进来!'); + console.warn('paste schema illegal'); + editor.isPasting = false; + return; + } + editor.emit('material.add', { + schema, + targetKey, + direction, + }); + editor.isPasting = false; + editor.emit('schema.paste', schema); + }; + document.pasteListener = e => { + const clipboardData = e.clipboardData || window.clipboardData; + const text = clipboardData && clipboardData.getData('text'); + doPaste(e, text); + }; + document.addEventListener('paste', document.pasteListener); + if (window.parent.vscode) { + keymaster('command+v', e => { + const sendIDEMessage = window.parent.sendIDEMessage; + sendIDEMessage && + sendIDEMessage({ + action: 'readClipboard', + }) + .then(text => { + doPaste(e, text); + }) + .catch(err => { + console.warn(err); + }); + }); + } + } + + (config || []).forEach(item => { + keymaster(item.keyboard, ev => { + ev.preventDefault(); + item.handler(ev, editor, keymaster); + }); + }); +} + +// 取消注册快捷 +export function unRegistShortCuts(config) { + (config || []).forEach(item => { + keymaster.unbind(item.keyboard); + }); + if (window.parent.vscode) { + keymaster.unbind('command+c'); + keymaster.unbind('command+v'); + } + if (document.copyListener) { + document.removeEventListener('copy', document.copyListener); + delete document.copyListener; + } + if (document.pasteListener) { + document.removeEventListener('paste', document.pasteListener); + delete document.pasteListener; + } +} + +// 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve +export function transformToPromise(input) { + if (input instanceof Promise) return input; + return new Promise((resolve, reject) => { + if (input || input === undefined) { + resolve(); + } else { + reject(); + } + }); +} + +export function transformArrayToMap(arr, key, overwrite = true) { + if (isEmpty(arr) || !Array.isArray(arr)) return {}; + const res = {}; + arr.forEach(item => { + const curKey = item[key]; + if (item[key] === undefined) return; + if (res[curKey] && !overwrite) return; + res[curKey] = item; + }); + return res; +} + +export function parseSearch(search) { + if (!search || typeof search !== 'string') return {}; + const str = search.replace(/^\?/, ''); + let paramStr = str.split('&'); + let res = {}; + for (let i = 0; i < paramStr.length; i++) { + let regRes = paramStr[i].split('='); + if (regRes[0] && regRes[1]) { + res[regRes[0]] = decodeURIComponent(regRes[1]); + } + } + return res; +} + +export function comboEditorConfig(defaultConfig = {}, customConfig = {}) { + const { + skeleton, + theme, + plugins, + hooks, + shortCuts, + lifeCycles, + constants, + utils, + i18n, + } = customConfig || {}; + + if (skeleton && skeleton.handler && typeof skeleton.handler === 'function') { + return skeleton.handler({ + skeleton, + ...defaultConfig, + }); + } + + const defaultShortCuts = transformArrayToMap( + defaultConfig.shortCuts, + 'keyboard', + ); + const customShortCuts = transformArrayToMap(shortCuts, 'keyboard'); + const localeList = ['zh-CN', 'zh-TW', 'en-US', 'ja-JP']; + const i18nConfig = {}; + localeList.forEach(key => { + i18nConfig[key] = { + ...(defaultConfig.i18n && defaultConfig.i18n[key]), + ...(i18n && i18n[key]), + }; + }); + return { + skeleton, + theme: { + ...defaultConfig.theme, + ...theme, + }, + plugins: { + ...defaultConfig.plugins, + ...plugins, + }, + hooks: [...(defaultConfig.hooks || []), ...(hooks || [])], + shortCuts: Object.values({ + ...defaultShortCuts, + ...customShortCuts, + }), + lifeCycles: { + ...defaultConfig.lifeCycles, + ...lifeCycles, + }, + constants: { + ...defaultConfig.constants, + ...constants, + }, + utils: [...(defaultConfig.utils || []), ...(utils || [])], + i18n: i18nConfig, + }; +} diff --git a/packages/editor/src/global.scss b/packages/editor/src/global.scss index e827d91b7..9b8705bdc 100644 --- a/packages/editor/src/global.scss +++ b/packages/editor/src/global.scss @@ -1,8 +1,8 @@ body { - font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, Arial, PingFang SC-Light, - Microsoft YaHei; + font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, + Arial, PingFang SC-Light, Microsoft YaHei; font-size: 12px; * { box-sizing: border-box; } -} \ No newline at end of file +} diff --git a/packages/editor/src/index.tsx b/packages/editor/src/index.tsx index f4b8bc855..43efc5e8e 100644 --- a/packages/editor/src/index.tsx +++ b/packages/editor/src/index.tsx @@ -1,10 +1,9 @@ +import React from 'react'; import ReactDOM from 'react-dom'; -import { HashRouter as Router, Route } from 'react-router-dom'; -import Skeleton from '@ali/lowcode-engine-skeleton'; - +// import Skeleton from '@ali/lowcode-engine-skeleton'; +import Skeleton from './skeleton'; import config from './config/skeleton'; import components from './config/components'; -import componentsMap from './config/componentsMap'; import utils from './config/utils'; import constants from './config/constants'; import messages from './config/locale'; @@ -22,21 +21,12 @@ if (!ICE_CONTAINER) { } ReactDOM.render( - - { - return ( - - ); - }} - /> - , ICE_CONTAINER); + , + ICE_CONTAINER, +); diff --git a/packages/editor/src/skeleton/components/LeftPlugin/index.scss b/packages/editor/src/skeleton/components/LeftPlugin/index.scss new file mode 100644 index 000000000..06b6ef63a --- /dev/null +++ b/packages/editor/src/skeleton/components/LeftPlugin/index.scss @@ -0,0 +1,59 @@ +.lowcode-left-plugin { + font-size: 16px; + text-align: center; + line-height: 36px; + height: 36px; + position: relative; + cursor: pointer; + transition: all 0.3s ease; + color: #777; + &.collapse { + height: 40px; + color: #8c8c8c; + border-bottom: 1px solid #bfbfbf; + } + &.locked { + color: red !important; + } + &.active { + color: #fff !important; + background-color: $color-brand1-9 !important; + &.disabled { + color: #fff; + background-color: $color-fill1-7; + } + } + &.disabled { + cursor: not-allowed; + color: $color-text1-1; + } + &:hover { + background-color: $color-brand1-1; + color: $color-brand1-6; + &:before { + content: attr(data-tooltip); + display: block; + position: absolute; + left: 50px; + top: 5px; + line-height: 18px; + font-size: 12px; + white-space: nowrap; + padding: 6px 8px; + border-radius: 4px; + background: rgba(0, 0, 0, 0.75); + color: #fff; + z-index: 100; + } + &:after { + content: ''; + display: block; + position: absolute; + left: 40px; + top: 15px; + border: 5px solid transparent; + border-right-color: rgba(0, 0, 0, 0.75); + z-index: 100; + } + } +} diff --git a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx b/packages/editor/src/skeleton/components/LeftPlugin/index.tsx new file mode 100644 index 000000000..e32a4dcc0 --- /dev/null +++ b/packages/editor/src/skeleton/components/LeftPlugin/index.tsx @@ -0,0 +1,205 @@ +import React, { PureComponent, Fragment } from 'react'; + +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { Balloon, Dialog, Icon, Badge } from '@alife/next'; + +import './index.scss'; +export default class LeftPlugin extends PureComponent { + static displayName = 'lowcodeLeftPlugin'; + + static defaultProps = { + active: false, + config: {}, + disabled: false, + dotted: false, + locked: false, + onClick: () => {}, + }; + + constructor(props, context) { + super(props, context); + this.state = { + dialogVisible: false, + }; + } + + componentDidMount() { + // const { config } = this.props; + // const addonKey = config && config.addonKey; + // const appHelper = this.appHelper; + // if (appHelper && addonKey) { + // appHelper.on(`${addonKey}.dialog.show`, this.handleShow); + // appHelper.on(`${addonKey}.dialog.close`, this.handleClose); + // } + } + + componentWillUnmount() { + // const { config } = this.props; + // const appHelper = this.appHelper; + // const addonKey = config && config.addonKey; + // if (appHelper && addonKey) { + // appHelper.off(`${addonKey}.dialog.show`, this.handleShow); + // appHelper.off(`${addonKey}.dialog.close`, this.handleClose); + // } + } + + handleClose = () => { + // const addonKey = this.props.config && this.props.config.addonKey; + // const currentAddon = + // this.appHelper.addons && this.appHelper.addons[addonKey]; + // if (currentAddon) { + // this.utils.transformToPromise(currentAddon.close()).then(() => { + // this.setState({ + // dialogVisible: false, + // }); + // }); + // } + }; + + handleOpen = () => { + // todo 对话框类型的插件初始时拿不到插件实例 + this.setState({ + dialogVisible: true, + }); + }; + + handleShow = () => { + // const { disabled, config, onClick } = this.props; + // const addonKey = config && config.addonKey; + // if (disabled || !addonKey) return; + // //考虑到弹窗情况,延时发送消息 + // setTimeout(() => this.appHelper.emit(`${addonKey}.addon.activate`), 0); + // this.handleOpen(); + // onClick && onClick(); + }; + + renderIcon = clickCallback => { + const { + active, + disabled, + dotted, + locked, + onClick, + config, + editor, + } = this.props; + const { pluginKey, props } = config || {}; + const { icon, title } = props || {}; + return ( +
{ + if (disabled) return; + //考虑到弹窗情况,延时发送消息 + clickCallback && clickCallback(); + onClick && onClick(); + }} + > + {dotted ? ( + + + + ) : ( + + )} +
+ ); + }; + + render() { + const { + dotted, + locked, + active, + disabled, + config, + editor, + pluginClass: Comp, + } = this.props; + const { pluginKey, props, type, pluginProps } = config || {}; + const { onClick, title } = props || {}; + const { dialogVisible } = this.state; + if (!pluginKey || !type || !props) return null; + + const node = + (Comp && ( + { + onClick && onClick.call(null, editor); + }} + {...pluginProps} + /> + )) || + null; + + switch (type) { + case 'LinkIcon': + return ( + + {this.renderIcon(() => { + onClick && onClick.call(null, editor); + })} + + ); + case 'Icon': + return this.renderIcon(() => { + onClick && onClick.call(null, editor); + }); + case 'DialogIcon': + return ( + + {this.renderIcon(() => { + onClick && onClick.call(null, editor); + this.handleOpen(); + })} + { + editor.emit(`${pluginKey}.dialog.onOk`); + this.handleClose(); + }} + onCancel={this.handleClose} + onClose={this.handleClose} + title={title} + {...(props.dialogProps || {})} + visible={dialogVisible} + > + {node} + + + ); + case 'BalloonIcon': + return ( + { + onClick && onClick.call(null, editor); + })} + align="r" + triggerType={['click', 'hover']} + {...(props.balloonProps || {})} + > + {node} + + ); + case 'PanelIcon': + return this.renderIcon(() => { + onClick && onClick.call(null, editor); + this.handleOpen(); + }); + case 'Custom': + return dotted ? {node} : node; + default: + return null; + } + } +} diff --git a/packages/editor/src/skeleton/components/Panel/index.scss b/packages/editor/src/skeleton/components/Panel/index.scss new file mode 100644 index 000000000..cd3211ab4 --- /dev/null +++ b/packages/editor/src/skeleton/components/Panel/index.scss @@ -0,0 +1,52 @@ +.lowcode-panel { + user-select: none; + overflow: hidden; + position: relative; + background: #ffffff; + transition: width 0.3s ease; + transform: translate3d(0, 0, 0); + height: 100%; + &.visible { + border-right: 1px solid #bfbfbf; + } + .drag-area { + display: none; + } + &.floatable { + position: absolute; + top: 0; + bottom: 0; + z-index: 999; + } + &.draggable { + .drag-area { + display: block; + width: 10px; + position: absolute; + top: 0; + bottom: 0; + cursor: col-resize; + z-index: 9999; + } + &.left { + .drag-area { + right: 0; + } + } + &.right { + .drag-area { + left: 0; + } + } + } + &.left { + &.floatable { + left: 44px; + } + } + &.right { + &.floatable { + right: 44px; + } + } +} diff --git a/packages/editor/src/skeleton/components/Panel/index.tsx b/packages/editor/src/skeleton/components/Panel/index.tsx new file mode 100644 index 000000000..12b44c51b --- /dev/null +++ b/packages/editor/src/skeleton/components/Panel/index.tsx @@ -0,0 +1,23 @@ +import React, { PureComponent } from 'react'; + +import './index.scss'; +export default class Panel extends PureComponent { + static displayName = 'Panel'; + + constructor(props) { + super(props); + } + + render() { + return ( +
+ {this.props.children} +
+ ); + } +} diff --git a/packages/editor/src/skeleton/components/TopIcon/index.scss b/packages/editor/src/skeleton/components/TopIcon/index.scss new file mode 100644 index 000000000..a75e4cbd5 --- /dev/null +++ b/packages/editor/src/skeleton/components/TopIcon/index.scss @@ -0,0 +1,32 @@ +.next-btn.next-large.lowcode-top-btn { + width: 44px; + height: 44px; + padding: 0; + margin: 2px -2px; + text-align: center; + border-radius: 8px; + border: 1px solid transparent; + color: #777; + &.disabled { + cursor: not-allowed; + color: $color-text1-1; + } + &.locked { + color: red !important; + } + i.next-icon { + &:before { + font-size: 17px; + } + margin-right: 0; + line-height: 18px; + } + span { + display: block; + margin: 0px -5px 0; + line-height: 16px; + text-align: center; + font-size: 12px; + transform: scale(0.8); + } +} diff --git a/packages/editor/src/skeleton/components/TopIcon/index.tsx b/packages/editor/src/skeleton/components/TopIcon/index.tsx new file mode 100644 index 000000000..8e81c9c3a --- /dev/null +++ b/packages/editor/src/skeleton/components/TopIcon/index.tsx @@ -0,0 +1,68 @@ +import React, { PureComponent } from 'react'; + +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { Icon, Button } from '@alifd/next'; +import './index.scss'; +export default class TopIcon extends PureComponent { + static displayName = 'TopIcon'; + static propTypes = { + active: PropTypes.bool, + className: PropTypes.string, + disabled: PropTypes.bool, + icon: PropTypes.string, + id: PropTypes.string, + locked: PropTypes.bool, + onClick: PropTypes.func, + showTitle: PropTypes.bool, + style: PropTypes.object, + title: PropTypes.string, + }; + static defaultProps = { + active: false, + className: '', + disabled: false, + icon: '', + id: '', + locked: false, + onClick: () => {}, + showTitle: false, + style: {}, + title: '', + }; + + render() { + const { + active, + disabled, + icon, + locked, + title, + className, + id, + style, + showTitle, + onClick, + } = this.props; + return ( + + ); + } +} diff --git a/packages/editor/src/skeleton/components/TopPlugin/index.scss b/packages/editor/src/skeleton/components/TopPlugin/index.scss new file mode 100644 index 000000000..4bdd7b8d2 --- /dev/null +++ b/packages/editor/src/skeleton/components/TopPlugin/index.scss @@ -0,0 +1,2 @@ +.lowcode-top-addon { +} diff --git a/packages/editor/src/skeleton/components/TopPlugin/index.tsx b/packages/editor/src/skeleton/components/TopPlugin/index.tsx new file mode 100644 index 000000000..720efe425 --- /dev/null +++ b/packages/editor/src/skeleton/components/TopPlugin/index.tsx @@ -0,0 +1,192 @@ +import React, { PureComponent, Fragment } from 'react'; + +import PropTypes from 'prop-types'; +import TopIcon from '../TopIcon'; +import { Balloon, Badge, Dialog } from '@alifd/next'; + +import './index.scss'; +export default class TopPlugin extends PureComponent { + static displayName = 'lowcodeTopPlugin'; + + static defaultProps = { + active: false, + config: {}, + disabled: false, + dotted: false, + locked: false, + onClick: () => {}, + }; + + constructor(props, context) { + super(props, context); + this.state = { + dialogVisible: false, + }; + } + + componentDidMount() { + const { config } = this.props; + const pluginKey = config && config.pluginKey; + // const appHelper = this.appHelper; + // if (appHelper && pluginKey) { + // appHelper.on(`${pluginKey}.dialog.show`, this.handleShow); + // appHelper.on(`${pluginKey}.dialog.close`, this.handleClose); + // } + } + + componentWillUnmount() { + // const { config } = this.props; + // const pluginKey = config && config.pluginKey; + // const appHelper = this.appHelper; + // if (appHelper && pluginKey) { + // appHelper.off(`${pluginKey}.dialog.show`, this.handleShow); + // appHelper.off(`${pluginKey}.dialog.close`, this.handleClose); + // } + } + + handleShow = () => { + // const { disabled, config, onClick, editor } = this.props; + // const pluginKey = config && config.pluginKey; + // if (disabled || !pluginKey) return; + // //考虑到弹窗情况,延时发送消息 + // setTimeout(() => editor.emit(`${pluginKey}.addon.activate`), 0); + // this.handleOpen(); + // onClick && onClick(); + }; + + handleClose = () => { + // const pluginKey = this.props.config && this.props.config.pluginKey; + // const currentAddon = + // this.appHelper.addons && this.appHelper.addons[pluginKey]; + // if (currentAddon) { + // this.utils.transformToPromise(currentAddon.close()).then(() => { + // this.setState({ + // dialogVisible: false, + // }); + // }); + // } + }; + + handleOpen = () => { + // todo dialog类型的插件初始时拿不动插件实例 + this.setState({ + dialogVisible: true, + }); + }; + + renderIcon = clickCallback => { + const { + active, + disabled, + dotted, + locked, + config, + onClick, + editor, + } = this.props; + const { pluginKey, props } = config || {}; + const { icon, title } = props || {}; + const node = ( + { + if (disabled) return; + //考虑到弹窗情况,延时发送消息 + setTimeout(() => editor.emit(`${pluginKey}.addon.activate`), 0); + clickCallback && clickCallback(); + onClick && onClick(); + }} + /> + ); + return dotted ? {node} : node; + }; + + render() { + const { + active, + dotted, + locked, + disabled, + config, + editor, + pluginClass: Comp, + } = this.props; + const { pluginKey, pluginProps, props, type } = config || {}; + const { onClick, title } = props || {}; + const { dialogVisible } = this.state; + if (!pluginKey || !type) return null; + const node = + (Comp && ( + { + onClick && onClick.call(null, editor); + }} + {...pluginProps} + /> + )) || + null; + + switch (type) { + case 'LinkIcon': + return ( + + {this.renderIcon(() => { + onClick && onClick.call(null, editor); + })} + + ); + case 'Icon': + return this.renderIcon(() => { + onClick && onClick.call(null, editor); + }); + case 'DialogIcon': + return ( + + {this.renderIcon(() => { + onClick && onClick.call(null, editor); + this.handleOpen(); + })} + { + editor.emit(`${pluginKey}.dialog.onOk`); + this.handleClose(); + }} + onCancel={this.handleClose} + onClose={this.handleClose} + title={title} + {...props.dialogProps} + visible={dialogVisible} + > + {node} + + + ); + case 'BalloonIcon': + return ( + { + onClick && onClick.call(null, editor); + })} + triggerType={['click', 'hover']} + {...props.balloonProps} + > + {node} + + ); + case 'Custom': + return dotted ? {node} : node; + default: + return null; + } + } +} diff --git a/packages/editor/src/skeleton/config/skeleton.ts b/packages/editor/src/skeleton/config/skeleton.ts new file mode 100644 index 000000000..ff8b4c563 --- /dev/null +++ b/packages/editor/src/skeleton/config/skeleton.ts @@ -0,0 +1 @@ +export default {}; diff --git a/packages/editor/src/skeleton/config/utils.ts b/packages/editor/src/skeleton/config/utils.ts new file mode 100644 index 000000000..ff8b4c563 --- /dev/null +++ b/packages/editor/src/skeleton/config/utils.ts @@ -0,0 +1 @@ +export default {}; diff --git a/packages/editor/src/skeleton/global.scss b/packages/editor/src/skeleton/global.scss new file mode 100644 index 000000000..0a710b895 --- /dev/null +++ b/packages/editor/src/skeleton/global.scss @@ -0,0 +1,33 @@ +body { + font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, + Arial, PingFang SC-Light, Microsoft YaHei; + font-size: 12px; + padding: 0; + margin: 0; + * { + box-sizing: border-box; + } +} +.next-loading { + .next-loading-wrap { + height: 100%; + } +} +.lowcode-editor { + .lowcode-main-content { + position: absolute; + top: 48px; + left: 0; + right: 0; + bottom: 0; + display: flex; + background-color: #d8d8d8; + } + .lowcode-center-area { + flex: 1; + display: flex; + flex-direction: column; + padding: 10px; + overflow: auto; + } +} diff --git a/packages/editor/src/skeleton/index.tsx b/packages/editor/src/skeleton/index.tsx new file mode 100644 index 000000000..47ca4ef86 --- /dev/null +++ b/packages/editor/src/skeleton/index.tsx @@ -0,0 +1,129 @@ +import React, { PureComponent } from 'react'; + +import { HashRouter as Router, Route } from 'react-router-dom'; +import Editor from '../framework/editor'; +import { comboEditorConfig, parseSearch } from '../framework/utils'; +import { Loading, ConfigProvider } from '@alifd/next'; +import defaultConfig from './config/skeleton'; +import skeletonUtils from './config/utils'; + +import TopArea from './layouts/TopArea'; +import LeftArea from './layouts/LeftArea'; +import CenterArea from './layouts/CenterArea'; +import RightArea from './layouts/RightArea'; + +import './global.scss'; + +let renderIdx = 0; + +export default class Skeleton extends PureComponent { + static displayName = 'LowcodeEditorSkeleton'; + + static getDerivedStateFromError() { + return { + __hasError: true, + }; + } + constructor(props) { + super(props); + + this.state = { + initReady: false, + skeletonKey: `skeleton${renderIdx}`, + }; + + this.init(); + } + + componentWillUnmount() { + this.editor && this.editor.destroy(); + this.editor = null; + } + + componentDidCatch(err) { + console.error(err); + } + + init = (isReset = false) => { + if (this.editor) { + this.editor.destroy(); + this.editor = null; + } + const { utils, config, components } = this.props; + const editor = (this.editor = new Editor( + comboEditorConfig(defaultConfig, config), + { ...skeletonUtils, ...utils }, + components, + )); + window.__ctx = { + editor, + appHelper: editor + }; + editor.once('editor.reset', () => { + this.setState({ + initReady: false, + }); + editor.emit('editor.beforeReset'); + this.init(true); + }); + + this.editor.init().then(() => { + this.setState( + { + initReady: true, + //刷新IDE时生成新的skeletonKey保证插件生命周期重新执行 + skeletonKey: isReset + ? `skeleton${++renderIdx}` + : this.state.skeletonKey, + }, + () => { + editor.emit('editor.ready'); + isReset && editor.emit('ide.afterReset'); + } + ); + }); + }; + + render() { + const { initReady, skeletonKey, __hasError } = this.state; + if (__hasError) { + return 'error'; + } + + return ( + + { + const { location, history, match } = props; + location.query = parseSearch(location.search); + this.editor.set('location', location); + this.editor.set('history', history); + this.editor.set('match', match); + return ( + + +
+ +
+ + + + +
+
+
+
+ ); + }} + /> +
+ ); + } +} diff --git a/packages/editor/src/skeleton/layouts/CenterArea/index.scss b/packages/editor/src/skeleton/layouts/CenterArea/index.scss new file mode 100644 index 000000000..b2584ed2b --- /dev/null +++ b/packages/editor/src/skeleton/layouts/CenterArea/index.scss @@ -0,0 +1,3 @@ +.lowcode-center-area { + padding: 12px; +} diff --git a/packages/editor/src/skeleton/layouts/CenterArea/index.tsx b/packages/editor/src/skeleton/layouts/CenterArea/index.tsx new file mode 100644 index 000000000..f1a463caf --- /dev/null +++ b/packages/editor/src/skeleton/layouts/CenterArea/index.tsx @@ -0,0 +1,33 @@ +import React, { PureComponent } from 'react'; + +import './index.scss'; + +export default class CenterArea extends PureComponent { + static displayName = 'lowcodeCenterArea'; + + constructor(props) { + super(props); + this.editor = props.editor; + this.config = this.editor.config && this.editor.config.plugins && this.editor.config.plugins.centerArea || []; + } + + render() { + const list = this.config.filter(item => { + return true; + }); + return ( +
+ {list.map(item => { + const Comp = this.editor.components[item.pluginKey]; + return ( + + ) + })} +
+ ); + } +} diff --git a/packages/editor/src/skeleton/layouts/LeftArea/index.scss b/packages/editor/src/skeleton/layouts/LeftArea/index.scss new file mode 100644 index 000000000..dac1b6b0a --- /dev/null +++ b/packages/editor/src/skeleton/layouts/LeftArea/index.scss @@ -0,0 +1,21 @@ +.lowcode-left-area-nav { + width: 48px; + height: 100%; + background: #ffffff; + border-right: 1px solid #e8ebee; + position: relative; + .top-area { + position: absolute; + top: 0; + width: 100%; + background: #ffffff; + max-height: 100%; + } + .bottom-area { + position: absolute; + bottom: 20px; + width: 100%; + background: #ffffff; + max-height: calc(100% - 20px); + } +} diff --git a/packages/editor/src/skeleton/layouts/LeftArea/index.tsx b/packages/editor/src/skeleton/layouts/LeftArea/index.tsx new file mode 100644 index 000000000..805a5e014 --- /dev/null +++ b/packages/editor/src/skeleton/layouts/LeftArea/index.tsx @@ -0,0 +1,7 @@ +import Nav from './nav'; +import Panel from './panel'; + +export default { + Nav, + Panel, +}; diff --git a/packages/editor/src/skeleton/layouts/LeftArea/nav.tsx b/packages/editor/src/skeleton/layouts/LeftArea/nav.tsx new file mode 100644 index 000000000..84d54f3ce --- /dev/null +++ b/packages/editor/src/skeleton/layouts/LeftArea/nav.tsx @@ -0,0 +1,51 @@ +import React, { PureComponent } from 'react'; +import LeftPlugin from '../../components/LeftPlugin'; +import './index.scss'; + +export default class LeftAreaPanel extends PureComponent { + static displayName = 'lowcodeLeftAreaNav'; + + constructor(props) { + super(props); + this.editor = props.editor; + this.config = + this.editor.config.plugins && this.editor.config.plugins.leftArea; + } + + handlePluginClick = item => {}; + + renderPluginList = (list = []) => { + return list.map((item, idx) => { + return ( + this.handlePluginClick(item)} + /> + ); + }); + }; + + render() { + const topList = []; + const bottomList = []; + this.config.forEach(item => { + const align = + item.props && item.props.align === 'bottom' ? 'bottom' : 'top'; + if (align === 'bottom') { + bottomList.push(item); + } else { + topList.push(item); + } + }); + + return ( +
+
{this.renderPluginList(bottomList)}
+
{this.renderPluginList(topList)}
+
+ ); + } +} diff --git a/packages/editor/src/skeleton/layouts/LeftArea/panel.tsx b/packages/editor/src/skeleton/layouts/LeftArea/panel.tsx new file mode 100644 index 000000000..10d8348ad --- /dev/null +++ b/packages/editor/src/skeleton/layouts/LeftArea/panel.tsx @@ -0,0 +1,39 @@ +import React, { PureComponent, Fragment } from 'react'; +import Panel from '../../components/Panel'; +import './index.scss'; + +export default class LeftAreaPanel extends PureComponent { + static displayName = 'lowcodeLeftAreaPanel'; + + constructor(props) { + super(props); + this.editor = props.editor; + this.config = + this.editor.config.plugins && this.editor.config.plugins.leftArea; + + this.state = { + activeKey: 'leftPanelIcon', + }; + } + + render() { + const list = this.config.filter(item => { + return item.type === 'PanelIcon'; + }); + return ( + + {list.map((item, idx) => { + const Comp = this.editor.components[item.pluginKey]; + return ( + + + + ); + })} + + ); + } +} diff --git a/packages/editor/src/skeleton/layouts/RightArea/index.scss b/packages/editor/src/skeleton/layouts/RightArea/index.scss new file mode 100644 index 000000000..120ef4f11 --- /dev/null +++ b/packages/editor/src/skeleton/layouts/RightArea/index.scss @@ -0,0 +1,157 @@ +.lowcode-right-area { + width: 300px; + height: 100%; + background-color: #ffffff; + border-left: 1px solid #e8ebee; + .right-plugin-title { + &.locked { + color: red !important; + } + &.active { + color: $color-brand1-9 !important; + } + &.disabled { + cursor: not-allowed; + color: $color-text1-1; + } + } + + //tab定义 + .next-tabs-wrapped.right-tabs { + display: flex; + flex-direction: column; + margin-top: -1px; + .next-tabs-bar { + z-index: 1; + } + .next-tabs-nav { + display: block; + .next-tabs-tab { + &:first-child { + border-left: none; + } + font-size: 14px; + text-align: center; + border-right: none !important; + margin-right: 0 !important; + width: 25%; + &.active { + background: none; + border-bottom-color: #f7f7f7 !important; + } + } + } + } + .next-tabs-content { + flex: 1; + .next-tabs-tabpane.active { + height: 100%; + overflow-y: auto; + } + } + //组件 + .select-comp { + padding: 10px 16px; + line-height: 16px; + color: #989a9c; + & > span { + font-size: 12px; + line-height: 16px; + font-weight: 400; + } + & > .btn-wrap, + & > .next-btn { + width: auto; + margin: 0 5px; + float: right; + } + } + + .unselected { + padding: 60px 0; + text-align: center; + } + //右侧属性面板样式调整; + .offset-56 { + padding-left: 56px; + margin-bottom: 16px; + overflow: hidden; + } + .fixedSpan.next-form-item { + & > .next-form-item-label { + width: 56px; + flex: none; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + & > .next-form-item-control { + padding-right: 24px; + } + } + .fixedSpan.next-form-item, + .offset-56 .next-form-item { + display: flex; + & > .next-form-item-control { + width: auto; + flex: 1; + max-width: none; + .next-input, + .next-select, + .next-radio-group, + .next-number-picker, + .luna-reactnode-btn, + .luna-monaco-button button, + .luna-object-button button { + width: 100%; + } + .next-number-picker { + width: 100%; + .next-after { + padding-right: 5px; + } + } + .next-radio-group { + display: flex; + label { + flex: 1; + text-align: center; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + } + } + } + .topSpan.next-form-item { + margin-bottom: 4px; + & > .next-form-item-control { + padding-right: 24px; + .next-input, + .next-select, + .next-radio-group, + .next-number-picker, + .luna-reactnode-btn, + .luna-monaco-button button, + .luna-object-button button { + width: 100%; + } + .next-number-picker { + width: 100%; + .next-after { + padding-right: 5px; + } + } + .next-radio-group { + display: flex; + label { + flex: 1; + text-align: center; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + } + } + } +} diff --git a/packages/editor/src/skeleton/layouts/RightArea/index.tsx b/packages/editor/src/skeleton/layouts/RightArea/index.tsx new file mode 100644 index 000000000..0304929c0 --- /dev/null +++ b/packages/editor/src/skeleton/layouts/RightArea/index.tsx @@ -0,0 +1,53 @@ +import React, { PureComponent } from 'react'; +import { Tab } from '@alifd/next'; +import './index.scss'; + +export default class RightArea extends PureComponent { + static displayName = 'lowcodeRightArea'; + + constructor(props) { + super(props); + this.editor = props.editor; + this.state = { + activeKey: 'rightPanel1', + }; + } + + handleTabChange = key => { + this.setState({ + activeKey: key, + }); + }; + + render() { + const list = + (this.editor && + this.editor.config && + this.editor.config.plugins && + this.editor.config.plugins.rightArea) || + []; + return ( +
+ + {list.map((item, idx) => { + const Comp = this.editor.components[item.pluginKey]; + return ( + + + + ); + })} + +
+ ); + } +} diff --git a/packages/editor/src/skeleton/layouts/TopArea/index.scss b/packages/editor/src/skeleton/layouts/TopArea/index.scss new file mode 100644 index 000000000..783d48092 --- /dev/null +++ b/packages/editor/src/skeleton/layouts/TopArea/index.scss @@ -0,0 +1,27 @@ +.lowcode-top-area { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 48px; + background-color: #ffffff; + border-bottom: 1px solid #e8ebee; + overflow: hidden; + user-select: none; + .divider { + max-width: 0; + margin: 8px 12px; + height: 30px; + border-right: 1px solid rgba(191, 191, 191, 0.3); + } + .next-col { + text-align: center; + } + .right-area { + position: absolute; + right: 12px; + top: 0; + height: 100%; + background: #ffffff; + } +} diff --git a/packages/editor/src/skeleton/layouts/TopArea/index.tsx b/packages/editor/src/skeleton/layouts/TopArea/index.tsx new file mode 100644 index 000000000..a59ca6990 --- /dev/null +++ b/packages/editor/src/skeleton/layouts/TopArea/index.tsx @@ -0,0 +1,80 @@ +import React, { PureComponent } from 'react'; +import { Grid } from '@alifd/next'; +import TopPlugin from '../../components/TopPlugin'; +import './index.scss'; + +const { Row, Col } = Grid; + +export default class TopArea extends PureComponent { + static displayName = 'lowcodeTopArea'; + + constructor(props) { + super(props); + this.editor = props.editor; + this.config = + this.editor.config.plugins && this.editor.config.plugins.topArea; + } + + componentDidMount() {} + componentWillUnmount() {} + + handlePluginStatusChange = () => {}; + + renderPluginList = (list = []) => { + return list.map((item, idx) => { + const isDivider = item.type === 'Divider'; + return ( + + {!isDivider && ( + + )} + + ); + }); + }; + + render() { + if (!this.config) return null; + const leftList = []; + const rightList = []; + this.config.forEach(item => { + const align = + item.props && item.props.align === 'right' ? 'right' : 'left'; + // 分隔符不允许相邻 + if (item.type === 'Divider') { + const currList = align === 'right' ? rightList : leftList; + if ( + currList.length === 0 || + currList[currList.length - 1].type === 'Divider' + ) + return; + } + if (align === 'right') { + rightList.push(item); + } else { + leftList.push(item); + } + }); + return ( +
+
+ {this.renderPluginList(leftList)} +
+
+ {this.renderPluginList(rightList)} +
+
+ ); + } +} diff --git a/packages/editor/src/skeleton/locale/en-US.js b/packages/editor/src/skeleton/locale/en-US.js new file mode 100644 index 000000000..36e3b219c --- /dev/null +++ b/packages/editor/src/skeleton/locale/en-US.js @@ -0,0 +1,10 @@ +export default { + loading: 'loading...', + rejectRedirect: 'Redirect is not allowed', + expand: 'Unfold', + fold: 'Fold', + pageNotExist: 'The current Page not exist', + enterFromAppCenter: 'Please enter from the app center', + noPermission: 'Sorry, you do not have the develop permission', + getPermission: 'Please connect the app owners {owners} to get the permission', +}; diff --git a/packages/editor/src/skeleton/locale/ja-JP.js b/packages/editor/src/skeleton/locale/ja-JP.js new file mode 100644 index 000000000..ff8b4c563 --- /dev/null +++ b/packages/editor/src/skeleton/locale/ja-JP.js @@ -0,0 +1 @@ +export default {}; diff --git a/packages/editor/src/skeleton/locale/zh-CN.js b/packages/editor/src/skeleton/locale/zh-CN.js new file mode 100644 index 000000000..2d5229d2c --- /dev/null +++ b/packages/editor/src/skeleton/locale/zh-CN.js @@ -0,0 +1,10 @@ +export default { + loading: '加载中...', + rejectRedirect: '开发中,已阻止发生跳转', + expand: '展开', + fold: '收起', + pageNotExist: '当前访问地址不存在', + enterFromAppCenter: '请从应用中心入口重新进入', + noPermission: '抱歉,您暂无开发权限', + getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限', +}; diff --git a/packages/editor/src/skeleton/locale/zh-TW.js b/packages/editor/src/skeleton/locale/zh-TW.js new file mode 100644 index 000000000..ff8b4c563 --- /dev/null +++ b/packages/editor/src/skeleton/locale/zh-TW.js @@ -0,0 +1 @@ +export default {}; From baa6adb19999107f30df0dea0c22214f181a7b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8B=E7=BE=8A?= Date: Mon, 9 Mar 2020 15:58:46 +0800 Subject: [PATCH 6/9] daily tag --- packages/editor/.prettierrc | 4 + packages/editor/package.json | 12 +- packages/editor/src/config/components.js | 11 +- packages/editor/src/config/constants.js | 2 +- packages/editor/src/config/locale/index.js | 2 +- packages/editor/src/config/skeleton.js | 96 +++---- packages/editor/src/framework/definitions.ts | 100 +++++++- packages/editor/src/framework/editor.ts | 82 +++--- packages/editor/src/framework/index.ts | 7 + .../src/framework/{plugin.js => plugin.tsx} | 42 +-- packages/editor/src/framework/utils.ts | 242 +++++------------- packages/editor/src/global.scss | 3 +- packages/editor/src/index.tsx | 2 +- .../skeleton/components/LeftPlugin/index.tsx | 60 ++--- .../src/skeleton/components/Panel/index.tsx | 11 +- .../src/skeleton/components/TopIcon/index.tsx | 53 ++-- .../skeleton/components/TopPlugin/index.tsx | 55 ++-- packages/editor/src/skeleton/global.scss | 3 +- packages/editor/src/skeleton/index.tsx | 57 +++-- .../src/skeleton/layouts/CenterArea/index.tsx | 28 +- .../src/skeleton/layouts/LeftArea/index.tsx | 2 +- .../src/skeleton/layouts/LeftArea/nav.tsx | 25 +- .../src/skeleton/layouts/LeftArea/panel.tsx | 27 +- .../src/skeleton/layouts/RightArea/index.tsx | 34 ++- .../src/skeleton/layouts/TopArea/index.tsx | 39 ++- packages/editor/src/skeleton/locale/en-US.js | 2 +- packages/editor/src/skeleton/locale/zh-CN.js | 2 +- 27 files changed, 519 insertions(+), 484 deletions(-) create mode 100644 packages/editor/.prettierrc rename packages/editor/src/framework/{plugin.js => plugin.tsx} (52%) diff --git a/packages/editor/.prettierrc b/packages/editor/.prettierrc new file mode 100644 index 000000000..d3c963559 --- /dev/null +++ b/packages/editor/.prettierrc @@ -0,0 +1,4 @@ +{ + "printWidth": 120, + "singleQuote": true +} \ No newline at end of file diff --git a/packages/editor/package.json b/packages/editor/package.json index df3f52b9c..f9f4648af 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -8,15 +8,21 @@ "@icedesign/theme": "^1.x", "@types/react": "^16.8.3", "@types/react-dom": "^16.8.2", + "events": "^3.1.0", + "intl-messageformat": "^8.2.1", "keymaster": "^1.6.2", "moment": "^2.23.0", "prop-types": "^15.5.8", - "react": "^16.4.1", - "react-dom": "^16.4.1", - "react-router-dom": "^5.1.2" + "react": "^16.8.1", + "react-dom": "^16.8.1", + "react-router-dom": "^5.1.2", + "store": "^2.0.12" }, "devDependencies": { "@ice/spec": "^0.1.1", + "@types/debug": "^4.1.5", + "@types/events": "^3.0.0", + "@types/store": "^2.0.2", "css-modules-typescript-loader": "^2.0.4", "eslint": "^6.0.1", "ice-plugin-fusion": "^0.1.4", diff --git a/packages/editor/src/config/components.js b/packages/editor/src/config/components.js index 901075ab6..938c7c842 100644 --- a/packages/editor/src/config/components.js +++ b/packages/editor/src/config/components.js @@ -1,4 +1,3 @@ - import topBalloonIcon from '@ali/iceluna-addon-2'; import topDialogIcon from '@ali/iceluna-addon-2'; import leftPanelIcon from '@ali/iceluna-addon-2'; @@ -15,10 +14,10 @@ export default { topBalloonIcon: PluginFactory(topBalloonIcon), topDialogIcon: PluginFactory(topDialogIcon), leftPanelIcon: PluginFactory(leftPanelIcon), - leftBalloonIcon:PluginFactory(leftBalloonIcon), - leftDialogIcon:PluginFactory(leftDialogIcon), - rightPanel1:PluginFactory(rightPanel1), - rightPanel2:PluginFactory(rightPanel2), + leftBalloonIcon: PluginFactory(leftBalloonIcon), + leftDialogIcon: PluginFactory(leftDialogIcon), + rightPanel1: PluginFactory(rightPanel1), + rightPanel2: PluginFactory(rightPanel2), rightPanel3: PluginFactory(rightPanel3), - rightPanel4: PluginFactory(rightPanel4), + rightPanel4: PluginFactory(rightPanel4) }; diff --git a/packages/editor/src/config/constants.js b/packages/editor/src/config/constants.js index d20e24f41..d7e16fed3 100644 --- a/packages/editor/src/config/constants.js +++ b/packages/editor/src/config/constants.js @@ -1,3 +1,3 @@ export default { - namespace: 'page', + namespace: 'page' }; diff --git a/packages/editor/src/config/locale/index.js b/packages/editor/src/config/locale/index.js index 5bc4373a4..f4ad6c5da 100644 --- a/packages/editor/src/config/locale/index.js +++ b/packages/editor/src/config/locale/index.js @@ -6,5 +6,5 @@ export default { 'en-US': en_us, 'zh-CN': zh_cn, 'zh-TW': zh_tw, - 'ja-JP': ja_jp, + 'ja-JP': ja_jp }; diff --git a/packages/editor/src/config/skeleton.js b/packages/editor/src/config/skeleton.js index 87d035f4a..7c59d154b 100644 --- a/packages/editor/src/config/skeleton.js +++ b/packages/editor/src/config/skeleton.js @@ -3,12 +3,12 @@ export default { theme: { dpl: { package: '@alife/dpl-iceluna', - version: '^2.3.0', + version: '^2.3.0' }, - scss: '', + scss: '' }, constants: { - namespace: 'page', + namespace: 'page' }, utils: [], plugins: { @@ -21,21 +21,21 @@ export default { title: 'balloon', icon: 'dengpao', balloonProps: { - triggerType: 'click', - }, + triggerType: 'click' + } }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'divider', type: 'Divider', props: { - align: 'left', - }, + align: 'left' + } }, { pluginKey: 'topDialogIcon', @@ -43,13 +43,13 @@ export default { props: { align: 'left', title: 'dialog', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'topLinkIcon', @@ -60,11 +60,11 @@ export default { icon: 'dengpao', linkProps: { href: '//www.taobao.com', - target: 'blank', - }, + target: 'blank' + } }, config: {}, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'topIcon', @@ -75,11 +75,11 @@ export default { icon: 'dengpao', onClick: function(editor) { alert('icon addon invoke, current activeKey: ' + editor.activeKey); - }, + } }, config: {}, - pluginProps: {}, - }, + pluginProps: {} + } ], leftArea: [ { @@ -88,13 +88,13 @@ export default { props: { align: 'top', title: 'panel', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'leftBalloonIcon', @@ -102,13 +102,13 @@ export default { props: { align: 'top', title: 'balloon', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'leftDialogIcon', @@ -116,13 +116,13 @@ export default { props: { align: 'bottom', title: 'dialog', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'leftLinkIcon', @@ -133,11 +133,11 @@ export default { icon: 'dengpao', linkProps: { href: '//www.taobao.com', - target: 'blank', - }, + target: 'blank' + } }, config: {}, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'leftIcon', @@ -148,11 +148,11 @@ export default { icon: 'dengpao', onClick: function(editor) { alert('icon addon invoke, current activeKey: ' + editor.activeKey); - }, + } }, config: {}, - pluginProps: {}, - }, + pluginProps: {} + } ], rightArea: [ { @@ -160,56 +160,56 @@ export default { type: 'Panel', props: { title: 'panel1', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'rightPanel2', type: 'Panel', props: { title: 'panel2', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'rightPanel3', type: 'Panel', props: { title: 'panel3', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'rightPanel4', type: 'Panel', props: { title: 'panel4', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, - }, + pluginProps: {} + } ], - centerArea: [], + centerArea: [] }, hooks: [], - shortCuts: [], + shortCuts: [] }; diff --git a/packages/editor/src/framework/definitions.ts b/packages/editor/src/framework/definitions.ts index 73655b11e..e406cd6fa 100644 --- a/packages/editor/src/framework/definitions.ts +++ b/packages/editor/src/framework/definitions.ts @@ -1,4 +1,17 @@ -export interface EditorConfig {} +import * as React from 'react'; +import Editor from './editor'; + +export interface EditorConfig { + skeleton?: SkeletonConfig; + theme?: ThemeConfig; + plugins?: PluginsConfig; + hooks?: HooksConfig; + shortCuts?: ShortCutsConfig; + utils?: UtilsConfig; + constants?: ConstantsConfig; + lifeCycles?: lifeCyclesConfig; + i18n?: I18nConfig; +} export interface NpmConfig { version: string; @@ -25,17 +38,92 @@ export interface ThemeConfig { } export interface PluginsConfig { - [key]: Array; + [propName: string]: Array; } export interface PluginConfig { pluginKey: string; type: string; - props: object; - config: NpmConfig; - pluginProps: object; + props: { + icon?: string; + title?: string; + width?: number; + height?: number; + visible?: boolean; + disabled?: boolean; + marked?: boolean; + align?: 'left' | 'right' | 'top' | 'bottom'; + onClick?: () => void; + dialogProps?: object; + balloonProps?: object; + panelProps?: object; + linkProps?: object; + }; + config?: NpmConfig; + pluginProps?: object; } export type HooksConfig = Array; -export interface HookConfig {} +export interface HookConfig { + message: string; + type: 'on' | 'once'; + handler: (editor: Editor, ...args) => void; +} + +export type ShortCutsConfig = Array; + +export interface ShortCutConfig { + keyboard: string; + handler: (editor: Editor, ev: React.KeyboardEventHandler, keymaster: any) => void; +} + +export type UtilsConfig = Array; + +export interface UtilConfig { + name: string; + type: 'npm' | 'function'; + content: NpmConfig | ((...args) => any); +} + +export type ConstantsConfig = object; + +export interface lifeCyclesConfig { + init?: (editor: Editor) => any; + destroy?: (editor: Editor) => any; +} + +export type LocaleType = 'zh-CN' | 'zh-TW' | 'en-US' | 'ja-JP'; + +export interface I18nMessages { + [propName: string]: string; +} + +export interface I18nConfig { + 'zh-CN'?: I18nMessages; + 'zh-TW'?: I18nMessages; + 'en-US'?: I18nMessages; + 'ja-JP'?: I18nMessages; +} + +export type I18nFunction = (key: string, params: object) => string; + +export interface Utils { + [propName: string]: (...args) => any; +} + +export interface PluginClass extends React.Component { + init?: (editor: Editor) => void; +} + +export interface PluginComponents { + [propName: string]: PluginClass; +} + +export interface PluginStatus { + [propName: string]: { + disabled: boolean; + visible: boolean; + marked: boolean; + }; +} diff --git a/packages/editor/src/framework/editor.ts b/packages/editor/src/framework/editor.ts index 78f1209ab..4918a4f4b 100644 --- a/packages/editor/src/framework/editor.ts +++ b/packages/editor/src/framework/editor.ts @@ -1,13 +1,9 @@ import EventEmitter from 'events'; import Debug from 'debug'; import store from 'store'; +import { EditorConfig, Utils, PluginComponents, PluginStatus, LocaleType, HooksConfig } from './definitions'; -import { - unRegistShortCuts, - registShortCuts, - transformToPromise, - generateI18n, -} from './utils'; +import { unRegistShortCuts, registShortCuts, transformToPromise } from './utils'; // 根据url参数设置debug选项 const res = /_?debug=(.*?)(&|$)/.exec(location.search); @@ -39,35 +35,47 @@ window.onbeforeunload = function(e) { return msg; }; -let instance = null; +let instance: Editor; + const debug = Debug('editor'); EventEmitter.defaultMaxListeners = 100; +export interface HooksFuncs { + [idx: number]: (msg: string, handler: (...args) => void) => void; +} export default class Editor extends EventEmitter { - static getInstance = () => { + static getInstance = (config: EditorConfig, components: PluginComponents, utils?: Utils): Editor => { if (!instance) { - instance = new Editor(); + instance = new Editor(config, components, utils); } return instance; }; - constructor(config, utils, components) { + private hooksFuncs: HooksFuncs; + + public pluginStatus: PluginStatus; + public plugins: PluginComponents; + public locale: LocaleType; + + public emit: (msg: string, ...args) => void; + public on: (msg: string, handler: (...args) => void) => void; + public once: (msg: string, handler: (...args) => void) => void; + public off: (msg: string, handler: (...args) => void) => void; + + constructor(public config: EditorConfig, public components: PluginComponents, public utils?: Utils) { super(); instance = this; - this.config = config; - this.utils = utils; - this.components = components; this.init(); } - init() { - const { hooks, shortCuts, lifeCycles } = this.config || {}; + init(): Promise { + const { hooks, shortCuts = [], lifeCycles } = this.config || {}; this.locale = store.get('lowcode-editor-locale') || 'zh-CN'; // this.messages = this.messagesSet[this.locale]; // this.i18n = generateI18n(this.locale, this.messages); this.pluginStatus = this.initPluginStatus(); - this.initHooks(hooks); + this.initHooks(hooks || []); this.emit('editor.beforeInit'); const init = (lifeCycles && lifeCycles.init) || (() => {}); @@ -77,6 +85,7 @@ export default class Editor extends EventEmitter { // 注册快捷键 registShortCuts(shortCuts, this); this.emit('editor.afterInit'); + return true; }) .catch(err => { console.error(err); @@ -84,11 +93,12 @@ export default class Editor extends EventEmitter { } destroy() { + debug('destroy'); try { const { hooks = [], shortCuts = [], lifeCycles = {} } = this.config; unRegistShortCuts(shortCuts); this.destroyHooks(hooks); - lifeCycles.destroy && lifeCycles.destroy(); + lifeCycles.destroy && lifeCycles.destroy(this); } catch (err) { console.warn(err); return; @@ -101,20 +111,8 @@ export default class Editor extends EventEmitter { set(key: string | object, val: any): void { if (typeof key === 'string') { - if ( - [ - 'init', - 'destroy', - 'get', - 'set', - 'batchOn', - 'batchOff', - 'batchOnce', - ].includes(key) - ) { - console.warning( - 'init, destroy, get, set, batchOn, batchOff, batchOnce is private attribute', - ); + if (['init', 'destroy', 'get', 'set', 'batchOn', 'batchOff', 'batchOnce'].includes(key)) { + console.error('init, destroy, get, set, batchOn, batchOff, batchOnce is private attribute'); return; } this[key] = val; @@ -125,34 +123,34 @@ export default class Editor extends EventEmitter { } } - batchOn(events: Array, lisenter: function): void { + batchOn(events: Array, lisenter: (...args) => void): void { if (!Array.isArray(events)) return; events.forEach(event => this.on(event, lisenter)); } - batchOnce(events: Array, lisenter: function): void { + batchOnce(events: Array, lisenter: (...args) => void): void { if (!Array.isArray(events)) return; events.forEach(event => this.once(event, lisenter)); } - batchOff(events: Array, lisenter: function): void { + batchOff(events: Array, lisenter: (...args) => void): void { if (!Array.isArray(events)) return; events.forEach(event => this.off(event, lisenter)); } //销毁hooks中的消息监听 - private destroyHooks(hooks = []) { + private destroyHooks(hooks: HooksConfig = []) { hooks.forEach((item, idx) => { - if (typeof this.__hooksFuncs[idx] === 'function') { - this.off(item.message, this.__hooksFuncs[idx]); + if (typeof this.hooksFuncs[idx] === 'function') { + this.off(item.message, this.hooksFuncs[idx]); } }); - delete this.__hooksFuncs; + delete this.hooksFuncs; } //初始化hooks中的消息监听 - private initHooks(hooks = []) { - this.__hooksFuncs = hooks.map(item => { + private initHooks(hooks: HooksConfig = []): void { + this.hooksFuncs = hooks.map(item => { const func = (...args) => { item.handler(this, ...args); }; @@ -168,11 +166,11 @@ export default class Editor extends EventEmitter { pluginAreas.forEach(area => { (plugins[area] || []).forEach(plugin => { if (plugin.type === 'Divider') return; - const { visible, disabled, dotted } = plugin.props || {}; + const { visible, disabled, marked } = plugin.props || {}; res[plugin.pluginKey] = { visible: typeof visible === 'boolean' ? visible : true, disabled: typeof disabled === 'boolean' ? disabled : false, - dotted: typeof dotted === 'boolean' ? dotted : false, + marked: typeof marked === 'boolean' ? marked : false }; const pluginClass = this.components[plugin.pluginKey]; // 判断如果编辑器插件有init静态方法,则在此执行init方法 diff --git a/packages/editor/src/framework/index.ts b/packages/editor/src/framework/index.ts index 8b82edf2e..547d9db52 100644 --- a/packages/editor/src/framework/index.ts +++ b/packages/editor/src/framework/index.ts @@ -1,3 +1,10 @@ import Editor from './editor'; +export { default as PluginFactory } from './plugin'; +export { default as EditorContext } from './context'; +import * as editorUtils from './utils'; +import * as editorDefinitions from './definitions'; export default Editor; + +export const utils = editorUtils; +export const definitions = editorDefinitions; diff --git a/packages/editor/src/framework/plugin.js b/packages/editor/src/framework/plugin.tsx similarity index 52% rename from packages/editor/src/framework/plugin.js rename to packages/editor/src/framework/plugin.tsx index 43bf5ef30..c89a386bb 100644 --- a/packages/editor/src/framework/plugin.js +++ b/packages/editor/src/framework/plugin.tsx @@ -1,23 +1,41 @@ -import React, { PureComponent, creatRef} from 'react'; +import React, { PureComponent, createRef } from 'react'; import EditorContext from './context'; -import { isEmpty, generateI18n, goldlog } from './utils'; +import Editor from './editor'; +import { isEmpty, generateI18n } from './utils'; +import { PluginConfig, I18nFunction } from './definitions'; +export interface PluginProps { + editor: Editor; + config: PluginConfig; +} -export default function plugin(Comp) { - class LowcodePlugin extends PureComponent { +export interface InjectedPluginProps { + i18n?: I18nFunction; +} + +export default function plugin( + Comp: React.ComponentType +): React.ComponentType { + class LowcodePlugin extends PureComponent { static displayName = 'LowcodeEditorPlugin'; static defaultProps = { - config: {}, + config: {} }; static contextType = EditorContext; + static init = Comp.init; + public ref = createRef(); + private editor: Editor; + private pluginKey: string; + private i18n: I18nFunction; + constructor(props, context) { super(props, context); + if (isEmpty(props.config) || !props.config.pluginKey) { console.warn('lowcode editor plugin has wrong config'); return; } - this.ref = React.createRef(); const { locale, messages, editor } = props; // 注册插件 this.editor = editor; @@ -36,19 +54,9 @@ export default function plugin(Comp) { render() { const { config } = this.props; - return ( - - ); + return ; } } - LowcodePlugin.init = Comp.init; - return LowcodePlugin; } diff --git a/packages/editor/src/framework/utils.ts b/packages/editor/src/framework/utils.ts index 59a32d270..dd6ace7d4 100644 --- a/packages/editor/src/framework/utils.ts +++ b/packages/editor/src/framework/utils.ts @@ -1,15 +1,41 @@ import IntlMessageFormat from 'intl-messageformat'; import keymaster from 'keymaster'; import _isEmpty from 'lodash/isEmpty'; +import { EditorConfig, LocaleType, I18nMessages, I18nFunction, ShortCutsConfig } from './definitions'; +import Editor from './editor'; export const isEmpty = _isEmpty; +const ENV = { + TBE: 'TBE', + WEBIDE: 'WEB-IDE', + VSCODE: 'VSCODE', + WEB: 'WEB' +}; + +export interface IDEMessageParams { + action: string; + data: { + logKey: string; + gmKey: string; + goKey: string; + }; +} + +export interface Window { + sendIDEMessage: (IDEMessageParams) => void; + goldlog: { + record: (logKey: string, gmKey: string, goKey: string, method: 'GET' | 'POST') => void; + }; + parent: Window; + is_theia: boolean; + vscode: boolean; +} + /** * 用于构造国际化字符串处理函数 - * @param {*} locale 国际化标识,例如 zh-CN、en-US - * @param {*} messages 国际化语言包 */ -export function generateI18n(locale = 'zh-CN', messages = {}) { +export function generateI18n(locale: LocaleType = 'zh-CN', messages: I18nMessages = {}): I18nFunction { return (key, values = {}) => { if (!messages || !messages[key]) return ''; const formater = new IntlMessageFormat(messages[key], locale); @@ -19,18 +45,14 @@ export function generateI18n(locale = 'zh-CN', messages = {}) { /** * 序列化参数 - * @param {*} obj 参数 */ export function serializeParams(obj: object): string { if (typeof obj !== 'object') return ''; - const res: Array = []; Object.entries(obj).forEach(([key, val]) => { if (val === null || val === undefined || val === '') return; if (typeof val === 'object') { - res.push( - `${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`, - ); + res.push(`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`); } else { res.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`); } @@ -44,12 +66,12 @@ export function serializeParams(obj: object): string { * @param {Object} params 参数 * @param {String} logKey 属性串 */ -export function goldlog(gmKey, params = {}, logKey = 'other') { - const sendIDEMessage = window.sendIDEMessage || window.parent.sendIDEMessage; +export function goldlog(gmKey: string, params: object = {}, logKey: string = 'other'): void { + const global = window as Window; + const sendIDEMessage = global.sendIDEMessage || global.parent.sendIDEMessage; const goKey = serializeParams({ - sdkVersion: pkg.version, env: getEnv(), - ...params, + ...params }); if (sendIDEMessage) { sendIDEMessage({ @@ -57,18 +79,17 @@ export function goldlog(gmKey, params = {}, logKey = 'other') { data: { logKey: `/iceluna.core.${logKey}`, gmKey, - goKey, - }, + goKey + } }); } - window.goldlog && - window.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST'); + global.goldlog && global.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST'); } /** * 获取当前编辑器环境 */ -export function getEnv() { +export function getEnv(): string { const userAgent = navigator.userAgent; const isVscode = /Electron\//.test(userAgent); if (isVscode) return ENV.VSCODE; @@ -78,131 +99,17 @@ export function getEnv() { } // 注册快捷键 -export function registShortCuts(config, editor) { - const keyboardFilter = (keymaster.filter = event => { - let eTarget = event.target || event.srcElement; - let tagName = eTarget.tagName; - let isInput = !!( - tagName == 'INPUT' || - tagName == 'SELECT' || - tagName == 'TEXTAREA' - ); - let isContenteditable = !!eTarget.getAttribute('contenteditable'); - if (isInput || isContenteditable) { - if (event.metaKey === true && [70, 83].includes(event.keyCode)) - event.preventDefault(); //禁止触发chrome原生的页面保存或查找 - return false; - } else { - return true; - } - }); - - const ideMessage = editor.utils && editor.utils.ideMessage; - - //复制 - if (!document.copyListener) { - document.copyListener = e => { - if (!keyboardFilter(e) || editor.isCopying) return; - const schema = - editor.schemaHelper && - editor.schemaHelper.schemaMap[editor.activeKey]; - if (!schema || !isSchema(schema)) return; - editor.isCopying = true; - const schemaStr = serialize(transformSchemaToPure(schema), { - unsafe: true, - }); - setClipboardData(schemaStr) - .then(() => { - ideMessage && - ideMessage( - 'success', - '当前内容已复制到剪贴板,请使用快捷键Command+v进行粘贴', - ); - editor.emit('schema.copy', schemaStr, schema); - editor.isCopying = false; - }) - .catch(errMsg => { - ideMessage && ideMessage('error', errMsg); - editor.isCopying = false; - }); - }; - document.addEventListener('copy', document.copyListener); - if (window.parent.vscode) { - keymaster('command+c', document.copyListener); - } - } - - //粘贴 - if (!document.pasteListener) { - const doPaste = (e, text) => { - if (!keyboardFilter(e) || editor.isPasting) return; - const schemaHelper = editor.schemaHelper; - let targetKey = editor.activeKey; - let direction = 'after'; - const topKey = - schemaHelper.schema && - schemaHelper.schema.__ctx && - schemaHelper.schema.__ctx.lunaKey; - if (!targetKey || topKey === targetKey) { - const schemaHelper = editor.schemaHelper; - const topKey = - schemaHelper.schema && - schemaHelper.schema.__ctx && - schemaHelper.schema.__ctx.lunaKey; - if (!topKey) return; - targetKey = topKey; - direction = 'in'; - } - editor.isPasting = true; - const schema = parseObj(text); - if (!isSchema(schema)) { - editor.emit('illegalSchema.paste', text); - // ideMessage && ideMessage('error', '当前内容不是模型结构,不能粘贴进来!'); - console.warn('paste schema illegal'); - editor.isPasting = false; - return; - } - editor.emit('material.add', { - schema, - targetKey, - direction, - }); - editor.isPasting = false; - editor.emit('schema.paste', schema); - }; - document.pasteListener = e => { - const clipboardData = e.clipboardData || window.clipboardData; - const text = clipboardData && clipboardData.getData('text'); - doPaste(e, text); - }; - document.addEventListener('paste', document.pasteListener); - if (window.parent.vscode) { - keymaster('command+v', e => { - const sendIDEMessage = window.parent.sendIDEMessage; - sendIDEMessage && - sendIDEMessage({ - action: 'readClipboard', - }) - .then(text => { - doPaste(e, text); - }) - .catch(err => { - console.warn(err); - }); - }); - } - } - +export function registShortCuts(config: ShortCutsConfig, editor: Editor): void { (config || []).forEach(item => { keymaster(item.keyboard, ev => { ev.preventDefault(); - item.handler(ev, editor, keymaster); + item.handler(editor, ev, keymaster); }); }); } // 取消注册快捷 -export function unRegistShortCuts(config) { +export function unRegistShortCuts(config: ShortCutsConfig): void { (config || []).forEach(item => { keymaster.unbind(item.keyboard); }); @@ -210,18 +117,12 @@ export function unRegistShortCuts(config) { keymaster.unbind('command+c'); keymaster.unbind('command+v'); } - if (document.copyListener) { - document.removeEventListener('copy', document.copyListener); - delete document.copyListener; - } - if (document.pasteListener) { - document.removeEventListener('paste', document.pasteListener); - delete document.pasteListener; - } } -// 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve -export function transformToPromise(input) { +/** + * 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve + */ +export function transformToPromise(input: any): Promise<{}> { if (input instanceof Promise) return input; return new Promise((resolve, reject) => { if (input || input === undefined) { @@ -232,7 +133,13 @@ export function transformToPromise(input) { }); } -export function transformArrayToMap(arr, key, overwrite = true) { +/** + * 将数组类型转换为Map类型 + */ +interface MapOf { + [propName: string]: T; +} +export function transformArrayToMap(arr: Array, key: string, overwrite: boolean = true): MapOf { if (isEmpty(arr) || !Array.isArray(arr)) return {}; const res = {}; arr.forEach(item => { @@ -244,7 +151,13 @@ export function transformArrayToMap(arr, key, overwrite = true) { return res; } -export function parseSearch(search) { +/** + * 解析url的查询参数 + */ +interface Query { + [propName: string]: string; +} +export function parseSearch(search: string): Query { if (!search || typeof search !== 'string') return {}; const str = search.replace(/^\?/, ''); let paramStr = str.split('&'); @@ -258,63 +171,50 @@ export function parseSearch(search) { return res; } -export function comboEditorConfig(defaultConfig = {}, customConfig = {}) { - const { - skeleton, - theme, - plugins, - hooks, - shortCuts, - lifeCycles, - constants, - utils, - i18n, - } = customConfig || {}; +export function comboEditorConfig(defaultConfig: EditorConfig = {}, customConfig: EditorConfig): EditorConfig { + const { skeleton, theme, plugins, hooks, shortCuts, lifeCycles, constants, utils, i18n } = customConfig || {}; if (skeleton && skeleton.handler && typeof skeleton.handler === 'function') { return skeleton.handler({ skeleton, - ...defaultConfig, + ...defaultConfig }); } - const defaultShortCuts = transformArrayToMap( - defaultConfig.shortCuts, - 'keyboard', - ); - const customShortCuts = transformArrayToMap(shortCuts, 'keyboard'); + const defaultShortCuts = transformArrayToMap(defaultConfig.shortCuts || [], 'keyboard'); + const customShortCuts = transformArrayToMap(shortCuts || [], 'keyboard'); const localeList = ['zh-CN', 'zh-TW', 'en-US', 'ja-JP']; const i18nConfig = {}; localeList.forEach(key => { i18nConfig[key] = { ...(defaultConfig.i18n && defaultConfig.i18n[key]), - ...(i18n && i18n[key]), + ...(i18n && i18n[key]) }; }); return { skeleton, theme: { ...defaultConfig.theme, - ...theme, + ...theme }, plugins: { ...defaultConfig.plugins, - ...plugins, + ...plugins }, hooks: [...(defaultConfig.hooks || []), ...(hooks || [])], shortCuts: Object.values({ ...defaultShortCuts, - ...customShortCuts, + ...customShortCuts }), lifeCycles: { ...defaultConfig.lifeCycles, - ...lifeCycles, + ...lifeCycles }, constants: { ...defaultConfig.constants, - ...constants, + ...constants }, utils: [...(defaultConfig.utils || []), ...(utils || [])], - i18n: i18nConfig, + i18n: i18nConfig }; } diff --git a/packages/editor/src/global.scss b/packages/editor/src/global.scss index 9b8705bdc..4802a89d4 100644 --- a/packages/editor/src/global.scss +++ b/packages/editor/src/global.scss @@ -1,6 +1,5 @@ body { - font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, - Arial, PingFang SC-Light, Microsoft YaHei; + font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, Arial, PingFang SC-Light, Microsoft YaHei; font-size: 12px; * { box-sizing: border-box; diff --git a/packages/editor/src/index.tsx b/packages/editor/src/index.tsx index 43efc5e8e..1716f05a8 100644 --- a/packages/editor/src/index.tsx +++ b/packages/editor/src/index.tsx @@ -28,5 +28,5 @@ ReactDOM.render( constants={constants} components={components} />, - ICE_CONTAINER, + ICE_CONTAINER ); diff --git a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx b/packages/editor/src/skeleton/components/LeftPlugin/index.tsx index e32a4dcc0..7721ad3c2 100644 --- a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx +++ b/packages/editor/src/skeleton/components/LeftPlugin/index.tsx @@ -1,26 +1,42 @@ import React, { PureComponent, Fragment } from 'react'; - -import PropTypes from 'prop-types'; import classNames from 'classnames'; -import { Balloon, Dialog, Icon, Badge } from '@alife/next'; +import { Balloon, Dialog, Icon, Badge } from '@alifd/next'; import './index.scss'; -export default class LeftPlugin extends PureComponent { - static displayName = 'lowcodeLeftPlugin'; +import Editor from '../../../framework/editor'; +import { PluginConfig, PluginClass } from '../../../framework/definitions'; + +export interface LeftPluginProps { + active?: boolean; + config: PluginConfig; + disabled?: boolean; + editor: Editor; + locked?: boolean; + marked?: boolean; + onClick?: () => void; + pluginClass: PluginClass; +} + +export interface LeftPluginState { + dialogVisible: boolean; +} + +export default class LeftPlugin extends PureComponent { + static displayName = 'LowcodeLeftPlugin'; static defaultProps = { active: false, config: {}, disabled: false, - dotted: false, + marked: false, locked: false, - onClick: () => {}, + onClick: () => {} }; constructor(props, context) { super(props, context); this.state = { - dialogVisible: false, + dialogVisible: false }; } @@ -60,7 +76,7 @@ export default class LeftPlugin extends PureComponent { handleOpen = () => { // todo 对话框类型的插件初始时拿不到插件实例 this.setState({ - dialogVisible: true, + dialogVisible: true }); }; @@ -75,15 +91,7 @@ export default class LeftPlugin extends PureComponent { }; renderIcon = clickCallback => { - const { - active, - disabled, - dotted, - locked, - onClick, - config, - editor, - } = this.props; + const { active, disabled, marked, locked, onClick, config, editor } = this.props; const { pluginKey, props } = config || {}; const { icon, title } = props || {}; return ( @@ -91,7 +99,7 @@ export default class LeftPlugin extends PureComponent { className={classNames('lowcode-left-plugin', pluginKey, { active, disabled, - locked, + locked })} data-tooltip={title} onClick={() => { @@ -101,7 +109,7 @@ export default class LeftPlugin extends PureComponent { onClick && onClick(); }} > - {dotted ? ( + {marked ? ( @@ -113,15 +121,7 @@ export default class LeftPlugin extends PureComponent { }; render() { - const { - dotted, - locked, - active, - disabled, - config, - editor, - pluginClass: Comp, - } = this.props; + const { marked, locked, active, disabled, config, editor, pluginClass: Comp } = this.props; const { pluginKey, props, type, pluginProps } = config || {}; const { onClick, title } = props || {}; const { dialogVisible } = this.state; @@ -197,7 +197,7 @@ export default class LeftPlugin extends PureComponent { this.handleOpen(); }); case 'Custom': - return dotted ? {node} : node; + return marked ? {node} : node; default: return null; } diff --git a/packages/editor/src/skeleton/components/Panel/index.tsx b/packages/editor/src/skeleton/components/Panel/index.tsx index 12b44c51b..f057e2a20 100644 --- a/packages/editor/src/skeleton/components/Panel/index.tsx +++ b/packages/editor/src/skeleton/components/Panel/index.tsx @@ -1,8 +1,13 @@ import React, { PureComponent } from 'react'; import './index.scss'; -export default class Panel extends PureComponent { - static displayName = 'Panel'; + +export interface PanelProps { + children: Plugin; +} + +export default class Panel extends PureComponent { + static displayName = 'LowcodePanel'; constructor(props) { super(props); @@ -13,7 +18,7 @@ export default class Panel extends PureComponent {
{this.props.children} diff --git a/packages/editor/src/skeleton/components/TopIcon/index.tsx b/packages/editor/src/skeleton/components/TopIcon/index.tsx index 8e81c9c3a..afd1955f5 100644 --- a/packages/editor/src/skeleton/components/TopIcon/index.tsx +++ b/packages/editor/src/skeleton/components/TopIcon/index.tsx @@ -1,23 +1,25 @@ import React, { PureComponent } from 'react'; - -import PropTypes from 'prop-types'; import classNames from 'classnames'; import { Icon, Button } from '@alifd/next'; + import './index.scss'; -export default class TopIcon extends PureComponent { - static displayName = 'TopIcon'; - static propTypes = { - active: PropTypes.bool, - className: PropTypes.string, - disabled: PropTypes.bool, - icon: PropTypes.string, - id: PropTypes.string, - locked: PropTypes.bool, - onClick: PropTypes.func, - showTitle: PropTypes.bool, - style: PropTypes.object, - title: PropTypes.string, - }; + +export interface TopIconProps { + active?: boolean; + className?: string; + disabled?: boolean; + icon: string; + id?: string; + locked?: boolean; + marked?: boolean; + onClick?: () => void; + showTitle?: boolean; + style?: React.CSSProperties; + title?: string; +} + +export default class TopIcon extends PureComponent { + static displayName = 'LowcodeTopIcon'; static defaultProps = { active: false, className: '', @@ -28,22 +30,11 @@ export default class TopIcon extends PureComponent { onClick: () => {}, showTitle: false, style: {}, - title: '', + title: '' }; render() { - const { - active, - disabled, - icon, - locked, - title, - className, - id, - style, - showTitle, - onClick, - } = this.props; + const { active, disabled, icon, locked, title, className, id, style, showTitle, onClick } = this.props; return (