From 9df6769016d4c3814dcc15a92331a3fddce62236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8B=E7=BE=8A?= Date: Wed, 25 Mar 2020 11:00:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:editor-framework,editor-skeleton=E6=8B=86?= =?UTF-8?q?=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/designer/package.json | 1 + packages/editor-framework/.eslintrc.js | 17 +- packages/editor-framework/es/areaManager.d.ts | 15 + packages/editor-framework/es/areaManager.js | 77 ++++ packages/editor-framework/es/definitions.d.ts | 130 +++++++ .../es/definitions.js} | 0 packages/editor-framework/es/editor.d.ts | 33 ++ packages/editor-framework/es/editor.js | 283 ++++++++++++++ packages/editor-framework/es/index.d.ts | 8 + packages/editor-framework/es/index.js | 8 + .../editor-framework/es/pluginFactory.d.ts | 3 + packages/editor-framework/es/pluginFactory.js | 98 +++++ packages/editor-framework/es/style.js | 1 + packages/editor-framework/es/utils.d.ts | 71 ++++ packages/editor-framework/es/utils.js | 270 +++++++++++++ packages/editor-framework/package.json | 6 +- .../src}/areaManager.ts | 33 +- packages/editor-framework/src/context.ts | 1 + packages/editor-framework/src/definitions.ts | 162 ++++++-- packages/editor-framework/src/editor.ts | 226 ++++++----- packages/editor-framework/src/index.ts | 10 +- packages/editor-framework/src/plugin.ts | 129 ------- .../src}/pluginFactory.tsx | 15 +- packages/editor-framework/src/utils.ts | 360 ++++++++++-------- packages/editor-skeleton/.eslintrc.js | 13 +- .../es/components/LeftIcon/index.scss | 0 .../es/components/LeftPlugin/index.d.ts | 34 +- .../es/components/LeftPlugin/index.js | 198 +++++----- .../es/components/LeftPlugin/index.scss | 61 +-- .../es/components/Panel/index.d.ts | 27 ++ .../es/components/Panel/index.js | 55 +++ .../es}/components/Panel/index.scss | 0 .../es/components/TopIcon/index.d.ts | 31 +- .../es/components/TopIcon/index.js | 33 +- .../es/components/TopIcon/index.scss | 64 +++- .../es/components/TopPlugin/index.d.ts | 25 +- .../es/components/TopPlugin/index.js | 115 +++--- .../editor-skeleton/es/config/skeleton.d.ts | 16 +- .../editor-skeleton/es/config/skeleton.js | 15 +- packages/editor-skeleton/es/config/utils.d.ts | 4 +- packages/editor-skeleton/es/config/utils.js | 4 +- packages/editor-skeleton/es/global.scss | 10 +- packages/editor-skeleton/es/index.d.ts | 40 +- packages/editor-skeleton/es/index.js | 138 +++++-- .../es/layouts/CenterArea/index.d.ts | 19 +- .../es/layouts/CenterArea/index.js | 46 ++- .../es/layouts/CenterArea/index.scss | 2 +- .../es/layouts/LeftArea/index.scss | 14 +- .../es/layouts/LeftArea/nav.d.ts | 27 +- .../es/layouts/LeftArea/nav.js | 150 +++++++- .../es/layouts/LeftArea/panel.d.ts | 23 +- .../es/layouts/LeftArea/panel.js | 65 +++- .../es/layouts/RightArea/index.d.ts | 27 +- .../es/layouts/RightArea/index.js | 200 +++++++++- .../es/layouts/RightArea/index.scss | 166 ++------ .../es/layouts/TopArea/index.d.ts | 17 +- .../es/layouts/TopArea/index.js | 45 ++- .../es/layouts/TopArea/index.scss | 31 +- packages/editor-skeleton/es/locale/en-US.js | 11 +- packages/editor-skeleton/es/locale/zh-CN.js | 11 +- packages/editor-skeleton/es/style.js | 5 +- packages/editor-skeleton/package.json | 14 +- .../src/components/LeftIcon/index.scss | 0 .../src/components/LeftIcon/index.tsx | 0 .../src/components/LeftPlugin/index.scss | 61 +-- .../src/components/LeftPlugin/index.tsx | 222 ++++++----- .../src/components/Panel/index.scss | 52 +++ .../src/components/Panel/index.tsx | 60 +++ .../src/components/TopIcon/index.scss | 64 +++- .../src/components/TopIcon/index.tsx | 59 ++- .../src/components/TopPlugin/index.tsx | 173 +++++---- .../editor-skeleton/src/config/skeleton.ts | 22 +- packages/editor-skeleton/src/config/utils.ts | 6 +- packages/editor-skeleton/src/global.scss | 7 +- packages/editor-skeleton/src/index.tsx | 187 +++++++-- .../src/layouts/CenterArea/index.scss | 2 +- .../src/layouts/CenterArea/index.tsx | 55 ++- .../src/layouts/LeftArea/index.scss | 14 +- .../src/layouts/LeftArea/nav.tsx | 141 ++++++- .../src/layouts/LeftArea/panel.tsx | 83 +++- .../src/layouts/RightArea/index.scss | 166 ++------ .../src/layouts/RightArea/index.tsx | 209 +++++++++- .../src/layouts/TopArea/index.scss | 31 +- .../src/layouts/TopArea/index.tsx | 101 +++-- packages/editor-skeleton/src/locale/en-US.js | 11 +- packages/editor-skeleton/src/locale/zh-CN.js | 11 +- packages/editor/package.json | 2 + packages/editor/src/config/skeleton.js | 2 +- packages/editor/src/framework/context.ts | 4 - packages/editor/src/framework/definitions.ts | 154 -------- packages/editor/src/framework/editor.ts | 230 ----------- packages/editor/src/framework/index.ts | 12 - packages/editor/src/framework/utils.ts | 272 ------------- packages/editor/src/index.d.ts | 8 - packages/editor/src/index.tsx | 22 +- .../skeleton/components/LeftPlugin/index.scss | 54 --- .../skeleton/components/LeftPlugin/index.tsx | 210 ---------- .../src/skeleton/components/Panel/index.tsx | 60 --- .../skeleton/components/TopIcon/index.scss | 73 ---- .../src/skeleton/components/TopIcon/index.tsx | 53 --- .../skeleton/components/TopPlugin/index.scss | 2 - .../skeleton/components/TopPlugin/index.tsx | 195 ---------- .../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 | 142 ------- .../skeleton/layouts/CenterArea/index.scss | 3 - .../src/skeleton/layouts/CenterArea/index.tsx | 52 --- .../src/skeleton/layouts/LeftArea/index.scss | 23 -- .../src/skeleton/layouts/LeftArea/index.tsx | 7 - .../src/skeleton/layouts/LeftArea/nav.tsx | 178 --------- .../src/skeleton/layouts/LeftArea/panel.tsx | 78 ---- .../src/skeleton/layouts/RightArea/index.scss | 39 -- .../src/skeleton/layouts/RightArea/index.tsx | 187 --------- .../src/skeleton/layouts/TopArea/index.scss | 30 -- .../src/skeleton/layouts/TopArea/index.tsx | 93 ----- 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 - packages/editor/src/skeleton/skeleton.tsx | 137 ------- packages/material-parser/package.json | 1 + packages/plugin-setters/package.json | 1 + packages/plugin-settings/package.json | 5 +- packages/react-renderer/package.json | 5 +- packages/runtime-framework/package.json | 1 + packages/utils/package.json | 1 + 127 files changed, 3925 insertions(+), 3918 deletions(-) create mode 100644 packages/editor-framework/es/areaManager.d.ts create mode 100644 packages/editor-framework/es/areaManager.js create mode 100644 packages/editor-framework/es/definitions.d.ts rename packages/{editor-skeleton/es/components/LeftIcon/index.js => editor-framework/es/definitions.js} (100%) create mode 100644 packages/editor-framework/es/editor.d.ts create mode 100644 packages/editor-framework/es/editor.js 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/pluginFactory.d.ts create mode 100644 packages/editor-framework/es/pluginFactory.js create mode 100644 packages/editor-framework/es/style.js create mode 100644 packages/editor-framework/es/utils.d.ts create mode 100644 packages/editor-framework/es/utils.js rename packages/{editor/src/framework => editor-framework/src}/areaManager.ts (52%) delete mode 100644 packages/editor-framework/src/plugin.ts rename packages/{editor/src/framework => editor-framework/src}/pluginFactory.tsx (82%) delete mode 100644 packages/editor-skeleton/es/components/LeftIcon/index.scss create mode 100644 packages/editor-skeleton/es/components/Panel/index.d.ts rename packages/{editor/src/skeleton => editor-skeleton/es}/components/Panel/index.scss (100%) delete mode 100644 packages/editor-skeleton/src/components/LeftIcon/index.scss delete mode 100644 packages/editor-skeleton/src/components/LeftIcon/index.tsx create mode 100644 packages/editor-skeleton/src/components/Panel/index.scss delete mode 100644 packages/editor/src/framework/context.ts delete mode 100644 packages/editor/src/framework/definitions.ts delete mode 100644 packages/editor/src/framework/editor.ts delete mode 100644 packages/editor/src/framework/index.ts delete mode 100644 packages/editor/src/framework/utils.ts delete mode 100644 packages/editor/src/index.d.ts delete mode 100644 packages/editor/src/skeleton/components/LeftPlugin/index.scss delete mode 100644 packages/editor/src/skeleton/components/LeftPlugin/index.tsx delete mode 100644 packages/editor/src/skeleton/components/Panel/index.tsx delete mode 100644 packages/editor/src/skeleton/components/TopIcon/index.scss delete mode 100644 packages/editor/src/skeleton/components/TopIcon/index.tsx delete mode 100644 packages/editor/src/skeleton/components/TopPlugin/index.scss delete mode 100644 packages/editor/src/skeleton/components/TopPlugin/index.tsx delete mode 100644 packages/editor/src/skeleton/config/skeleton.ts delete mode 100644 packages/editor/src/skeleton/config/utils.ts delete mode 100644 packages/editor/src/skeleton/global.scss delete mode 100644 packages/editor/src/skeleton/index.tsx delete mode 100644 packages/editor/src/skeleton/layouts/CenterArea/index.scss delete mode 100644 packages/editor/src/skeleton/layouts/CenterArea/index.tsx delete mode 100644 packages/editor/src/skeleton/layouts/LeftArea/index.scss delete mode 100644 packages/editor/src/skeleton/layouts/LeftArea/index.tsx delete mode 100644 packages/editor/src/skeleton/layouts/LeftArea/nav.tsx delete mode 100644 packages/editor/src/skeleton/layouts/LeftArea/panel.tsx delete mode 100644 packages/editor/src/skeleton/layouts/RightArea/index.scss delete mode 100644 packages/editor/src/skeleton/layouts/RightArea/index.tsx delete mode 100644 packages/editor/src/skeleton/layouts/TopArea/index.scss delete mode 100644 packages/editor/src/skeleton/layouts/TopArea/index.tsx delete mode 100644 packages/editor/src/skeleton/locale/en-US.js delete mode 100644 packages/editor/src/skeleton/locale/ja-JP.js delete mode 100644 packages/editor/src/skeleton/locale/zh-CN.js delete mode 100644 packages/editor/src/skeleton/locale/zh-TW.js delete mode 100644 packages/editor/src/skeleton/skeleton.tsx diff --git a/packages/designer/package.json b/packages/designer/package.json index 95d06f83c..b9d63b32d 100644 --- a/packages/designer/package.json +++ b/packages/designer/package.json @@ -7,6 +7,7 @@ "license": "MIT", "dependencies": { "@ali/iceluna-sdk": "^1.0.5-beta.12", + "@ali/lowcode-editor-framework": "^0.0.1", "@recore/obx": "^1.0.8", "@recore/obx-react": "^1.0.7", "@types/medium-editor": "^5.0.3", diff --git a/packages/editor-framework/.eslintrc.js b/packages/editor-framework/.eslintrc.js index ebda54735..4d6be3c66 100644 --- a/packages/editor-framework/.eslintrc.js +++ b/packages/editor-framework/.eslintrc.js @@ -1,5 +1,16 @@ -const { eslint, deepmerge } = require('@ice/spec'); +const { tslint, deepmerge } = require('@ice/spec'); -module.exports = deepmerge(eslint, { - rules: {}, +module.exports = deepmerge(tslint, { + rules: { + "global-require": 0, + "comma-dangle": 0, + "no-unused-expressions": 0, + "object-shorthand": 0, + "jsx-a11y/anchor-has-content": 0, + "react/sort-comp": 0, + "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".tsx", "ts"] }], + "@typescript-eslint/interface-name-prefix": 0, + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/explicit-member-accessibility": 0 + }, }); diff --git a/packages/editor-framework/es/areaManager.d.ts b/packages/editor-framework/es/areaManager.d.ts new file mode 100644 index 000000000..6d058306f --- /dev/null +++ b/packages/editor-framework/es/areaManager.d.ts @@ -0,0 +1,15 @@ +import { PluginConfig, PluginStatus, PluginClass, HOCPlugin } from './definitions'; +import Editor from './index'; +export default class AreaManager { + private pluginStatus; + private config; + private editor; + private area; + constructor(editor: Editor, area: string); + isPluginStatusUpdate(pluginType?: string, notUpdateStatus?: boolean): boolean; + getVisiblePluginList(pluginType?: string): PluginConfig[]; + getPlugin(pluginKey: string): HOCPlugin | void; + getPluginConfig(pluginKey?: string): PluginConfig[] | PluginConfig | undefined; + getPluginClass(pluginKey: string): PluginClass | void; + getPluginStatus(pluginKey: string): PluginStatus | void; +} diff --git a/packages/editor-framework/es/areaManager.js b/packages/editor-framework/es/areaManager.js new file mode 100644 index 000000000..38913e1bc --- /dev/null +++ b/packages/editor-framework/es/areaManager.js @@ -0,0 +1,77 @@ +import { clone, deepEqual } from './utils'; + +var AreaManager = /*#__PURE__*/function () { + function AreaManager(editor, area) { + this.pluginStatus = void 0; + this.config = void 0; + this.editor = void 0; + this.area = void 0; + this.editor = editor; + this.area = area; + this.config = editor && editor.config && editor.config.plugins && editor.config.plugins[this.area] || []; + this.pluginStatus = clone(editor.pluginStatus); + } + + var _proto = AreaManager.prototype; + + _proto.isPluginStatusUpdate = function isPluginStatusUpdate(pluginType, notUpdateStatus) { + var _this = this; + + var pluginStatus = this.editor.pluginStatus; + var list = pluginType ? this.config.filter(function (item) { + return item.type === pluginType; + }) : this.config; + var isUpdate = list.some(function (item) { + return !deepEqual(pluginStatus[item.pluginKey], _this.pluginStatus[item.pluginKey]); + }); + + if (!notUpdateStatus) { + this.pluginStatus = clone(pluginStatus); + } + + return isUpdate; + }; + + _proto.getVisiblePluginList = function getVisiblePluginList(pluginType) { + var _this2 = this; + + var res = this.config.filter(function (item) { + return !!(!_this2.pluginStatus[item.pluginKey] || _this2.pluginStatus[item.pluginKey].visible); + }); + return pluginType ? res.filter(function (item) { + return item.type === pluginType; + }) : res; + }; + + _proto.getPlugin = function getPlugin(pluginKey) { + if (pluginKey) { + return this.editor && this.editor.plugins && this.editor.plugins[pluginKey]; + } + }; + + _proto.getPluginConfig = function getPluginConfig(pluginKey) { + if (pluginKey) { + return this.config.find(function (item) { + return item.pluginKey === pluginKey; + }); + } + + return this.config; + }; + + _proto.getPluginClass = function getPluginClass(pluginKey) { + if (pluginKey) { + return this.editor && this.editor.components && this.editor.components[pluginKey]; + } + }; + + _proto.getPluginStatus = function getPluginStatus(pluginKey) { + if (pluginKey) { + return this.editor && this.editor.pluginStatus && this.editor.pluginStatus[pluginKey]; + } + }; + + return AreaManager; +}(); + +export { AreaManager as default }; \ No newline at end of file diff --git a/packages/editor-framework/es/definitions.d.ts b/packages/editor-framework/es/definitions.d.ts new file mode 100644 index 000000000..7d4b7e740 --- /dev/null +++ b/packages/editor-framework/es/definitions.d.ts @@ -0,0 +1,130 @@ +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; + package: string; + main?: string; + exportName?: string; + subName?: string; + destructuring?: boolean; +} +export interface SkeletonConfig { + config: NpmConfig; + props?: object; + handler?: (config: EditorConfig) => EditorConfig; +} +export interface FusionTheme { + package: string; + version: string; +} +export interface ThemeConfig { + fusion?: FusionTheme; +} +export interface PluginsConfig { + [key: string]: PluginConfig[]; +} +export interface PluginConfig { + pluginKey: string; + type: string; + 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 declare type HooksConfig = HookConfig[]; +export interface HookConfig { + message: string; + type: 'on' | 'once'; + handler: (editor: Editor, ...args: []) => void; +} +export declare type ShortCutsConfig = ShortCutConfig[]; +export interface ShortCutConfig { + keyboard: string; + handler: (editor: Editor, ev: Event, keymaster: any) => void; +} +export declare type UtilsConfig = UtilConfig[]; +export interface UtilConfig { + name: string; + type: 'npm' | 'function'; + content: NpmConfig | ((...args: []) => any); +} +export declare type ConstantsConfig = object; +export interface LifeCyclesConfig { + init?: (editor: Editor) => any; + destroy?: (editor: Editor) => any; +} +export declare type LocaleType = 'zh-CN' | 'zh-TW' | 'en-US' | 'ja-JP'; +export interface I18nMessages { + [key: string]: string; +} +export interface I18nConfig { + 'zh-CN'?: I18nMessages; + 'zh-TW'?: I18nMessages; + 'en-US'?: I18nMessages; + 'ja-JP'?: I18nMessages; +} +export declare type I18nFunction = (key: string, params: any) => string; +export interface Utils { + [key: string]: (...args: []) => any; +} +export interface PluginProps { + editor: Editor; + config: PluginConfig; + i18n?: I18nFunction; + ref?: React.RefObject; + [key: string]: any; +} +export declare type Plugin = React.ReactNode & { + open?: () => boolean | void | Promise; + close?: () => boolean | void | Promise; +}; +export declare type HOCPlugin = React.ReactNode & { + open: () => Promise; + close: () => Promise; +}; +export interface PluginSet { + [key: string]: HOCPlugin; +} +export declare type PluginClass = React.ComponentType & { + init?: (editor: Editor) => void; + defaultProps?: { + locale?: LocaleType; + messages?: I18nMessages; + }; +}; +export interface PluginClassSet { + [key: string]: PluginClass; +} +export interface PluginStatus { + disabled?: boolean; + visible?: boolean; + marked?: boolean; + locked?: boolean; +} +export interface PluginStatusSet { + [key: string]: PluginStatus; +} diff --git a/packages/editor-skeleton/es/components/LeftIcon/index.js b/packages/editor-framework/es/definitions.js similarity index 100% rename from packages/editor-skeleton/es/components/LeftIcon/index.js rename to packages/editor-framework/es/definitions.js diff --git a/packages/editor-framework/es/editor.d.ts b/packages/editor-framework/es/editor.d.ts new file mode 100644 index 000000000..3db69aa66 --- /dev/null +++ b/packages/editor-framework/es/editor.d.ts @@ -0,0 +1,33 @@ +/// +import { EventEmitter } from 'events'; +import { EditorConfig, LocaleType, PluginStatusSet, Utils, PluginClassSet, PluginSet } from './definitions'; +declare global { + interface Window { + __isDebug?: boolean; + __newFunc?: (funcStr: string) => (...args: any[]) => any; + } +} +export interface HooksFuncs { + [idx: number]: (msg: string, handler: (...args: any[]) => void) => void; +} +export default class Editor extends EventEmitter { + static getInstance: (config: EditorConfig, components: PluginClassSet, utils?: Utils) => Editor; + config: EditorConfig; + components: PluginClassSet; + utils: Utils; + pluginStatus: PluginStatusSet; + plugins: PluginSet; + locale: LocaleType; + private hooksFuncs; + constructor(config: EditorConfig, components: PluginClassSet, utils?: Utils); + init(): Promise; + destroy(): void; + get(key: string): any; + set(key: string | object, val: any): void; + batchOn(events: string[], lisenter: (...args: any[]) => void): void; + batchOnce(events: string[], lisenter: (...args: any[]) => void): void; + batchOff(events: string[], lisenter: (...args: any[]) => void): void; + private destroyHooks; + private initHooks; + private initPluginStatus; +} diff --git a/packages/editor-framework/es/editor.js b/packages/editor-framework/es/editor.js new file mode 100644 index 000000000..4ec0549bc --- /dev/null +++ b/packages/editor-framework/es/editor.js @@ -0,0 +1,283 @@ +import _extends from "@babel/runtime/helpers/extends"; +import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized"; +import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; +import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; + +function _createSuper(Derived) { return function () { var Super = _getPrototypeOf(Derived), result; if (_isNativeReflectConstruct()) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } + +import Debug from 'debug'; +import { EventEmitter } from 'events'; +import store from 'store'; +import * as editorUtils from './utils'; +var registShortCuts = editorUtils.registShortCuts, + transformToPromise = editorUtils.transformToPromise, + unRegistShortCuts = editorUtils.unRegistShortCuts; +// 根据url参数设置debug选项 +var debugRegRes = /_?debug=(.*?)(&|$)/.exec(location.search); + +if (debugRegRes && debugRegRes[1]) { + // eslint-disable-next-line no-underscore-dangle + window.__isDebug = true; + store.storage.write('debug', debugRegRes[1] === 'true' ? '*' : debugRegRes[1]); +} else { + // eslint-disable-next-line no-underscore-dangle + window.__isDebug = false; + store.remove('debug'); +} // 重要,用于矫正画布执行new Function的window对象上下文 +// eslint-disable-next-line no-underscore-dangle + + +window.__newFunc = function (funContext) { + // eslint-disable-next-line no-new-func + return new Function(funContext); +}; // 关闭浏览器前提醒,只有产生过交互才会生效 + + +window.onbeforeunload = function (e) { + var ev = e || window.event; // 本地调试不生效 + + if (location.href.indexOf('localhost') > 0) { + return; + } + + var msg = '您确定要离开此页面吗?'; + ev.cancelBubble = true; + ev.returnValue = true; + + if (e.stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } + + return msg; +}; + +var instance; +var debug = Debug('editor'); +EventEmitter.defaultMaxListeners = 100; + +var Editor = /*#__PURE__*/function (_EventEmitter) { + _inheritsLoose(Editor, _EventEmitter); + + var _super = _createSuper(Editor); + + function Editor(config, components, utils) { + var _this; + + _this = _EventEmitter.call(this) || this; + _this.config = void 0; + _this.components = void 0; + _this.utils = void 0; + _this.pluginStatus = void 0; + _this.plugins = void 0; + _this.locale = void 0; + _this.hooksFuncs = void 0; + _this.config = config; + _this.components = components; + _this.utils = _extends({}, editorUtils, {}, utils); + instance = _assertThisInitialized(_this); + + _this.init(); + + return _this; + } + + var _proto = Editor.prototype; + + _proto.init = function init() { + var _this2 = this; + + var _ref = this.config || {}, + hooks = _ref.hooks, + _ref$shortCuts = _ref.shortCuts, + shortCuts = _ref$shortCuts === void 0 ? [] : _ref$shortCuts, + lifeCycles = _ref.lifeCycles; + + 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'); + + var init = lifeCycles && lifeCycles.init || function () {}; // 用户可以通过设置extensions.init自定义初始化流程; + + + return transformToPromise(init(this)).then(function () { + // 注册快捷键 + registShortCuts(shortCuts, _this2); + + _this2.emit('editor.afterInit'); + + return true; + })["catch"](function (err) { + console.error(err); + }); + }; + + _proto.destroy = function destroy() { + debug('destroy'); + + try { + var _this$config = this.config, + _this$config$hooks = _this$config.hooks, + hooks = _this$config$hooks === void 0 ? [] : _this$config$hooks, + _this$config$shortCut = _this$config.shortCuts, + shortCuts = _this$config$shortCut === void 0 ? [] : _this$config$shortCut, + _this$config$lifeCycl = _this$config.lifeCycles, + lifeCycles = _this$config$lifeCycl === void 0 ? {} : _this$config$lifeCycl; + unRegistShortCuts(shortCuts); + this.destroyHooks(hooks); + + if (lifeCycles.destroy) { + lifeCycles.destroy(this); + } + } catch (err) { + console.warn(err); + } + }; + + _proto.get = function get(key) { + return this[key]; + }; + + _proto.set = function set(key, val) { + var _this3 = this; + + if (typeof key === 'string') { + 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; + } else if (typeof key === 'object') { + Object.keys(key).forEach(function (item) { + _this3[item] = key[item]; + }); + } + }; + + _proto.batchOn = function batchOn(events, lisenter) { + var _this4 = this; + + if (!Array.isArray(events)) { + return; + } + + events.forEach(function (event) { + _this4.on(event, lisenter); + }); + }; + + _proto.batchOnce = function batchOnce(events, lisenter) { + var _this5 = this; + + if (!Array.isArray(events)) { + return; + } + + events.forEach(function (event) { + _this5.once(event, lisenter); + }); + }; + + _proto.batchOff = function batchOff(events, lisenter) { + var _this6 = this; + + if (!Array.isArray(events)) { + return; + } + + events.forEach(function (event) { + _this6.off(event, lisenter); + }); + } // 销毁hooks中的消息监听 + ; + + _proto.destroyHooks = function destroyHooks(hooks) { + var _this7 = this; + + if (hooks === void 0) { + hooks = []; + } + + hooks.forEach(function (item, idx) { + if (typeof _this7.hooksFuncs[idx] === 'function') { + _this7.off(item.message, _this7.hooksFuncs[idx]); + } + }); + delete this.hooksFuncs; + } // 初始化hooks中的消息监听 + ; + + _proto.initHooks = function initHooks(hooks) { + var _this8 = this; + + if (hooks === void 0) { + hooks = []; + } + + this.hooksFuncs = hooks.map(function (item) { + var func = function func() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + item.handler.apply(item, [_this8].concat(args)); + }; + + _this8[item.type](item.message, func); + + return func; + }); + }; + + _proto.initPluginStatus = function initPluginStatus() { + var _this9 = this; + + var _this$config$plugins = this.config.plugins, + plugins = _this$config$plugins === void 0 ? {} : _this$config$plugins; + var pluginAreas = Object.keys(plugins); + var res = {}; + pluginAreas.forEach(function (area) { + (plugins[area] || []).forEach(function (plugin) { + if (plugin.type === 'Divider') { + return; + } + + var _ref2 = plugin.props || {}, + visible = _ref2.visible, + disabled = _ref2.disabled, + marked = _ref2.marked; + + res[plugin.pluginKey] = { + visible: typeof visible === 'boolean' ? visible : true, + disabled: typeof disabled === 'boolean' ? disabled : false, + marked: typeof marked === 'boolean' ? marked : false + }; + var pluginClass = _this9.components[plugin.pluginKey]; // 判断如果编辑器插件有init静态方法,则在此执行init方法 + + if (pluginClass && pluginClass.init) { + pluginClass.init(_this9); + } + }); + }); + return res; + }; + + return Editor; +}(EventEmitter); + +Editor.getInstance = function (config, components, utils) { + if (!instance) { + instance = new Editor(config, components, utils); + } + + return instance; +}; + +export { Editor as default }; \ No newline at end of file diff --git a/packages/editor-framework/es/index.d.ts b/packages/editor-framework/es/index.d.ts new file mode 100644 index 000000000..31cf0e019 --- /dev/null +++ b/packages/editor-framework/es/index.d.ts @@ -0,0 +1,8 @@ +import Editor from './editor'; +import * as utils from './utils'; +import * as definitions from './definitions'; +export { default as PluginFactory } from './pluginFactory'; +export { default as EditorContext } from './context'; +export { default as AreaManager } from './areaManager'; +export default Editor; +export { utils, definitions }; diff --git a/packages/editor-framework/es/index.js b/packages/editor-framework/es/index.js new file mode 100644 index 000000000..2ca7bbbe6 --- /dev/null +++ b/packages/editor-framework/es/index.js @@ -0,0 +1,8 @@ +import Editor from './editor'; +import * as utils from './utils'; +import * as definitions from './definitions'; +export { default as PluginFactory } from './pluginFactory'; +export { default as EditorContext } from './context'; +export { default as AreaManager } from './areaManager'; +export default Editor; +export { utils, definitions }; \ No newline at end of file diff --git a/packages/editor-framework/es/pluginFactory.d.ts b/packages/editor-framework/es/pluginFactory.d.ts new file mode 100644 index 000000000..26f6c3116 --- /dev/null +++ b/packages/editor-framework/es/pluginFactory.d.ts @@ -0,0 +1,3 @@ +import React from 'react'; +import { PluginProps, PluginClass } from './definitions'; +export default function pluginFactory(Comp: PluginClass): React.ComponentType; diff --git a/packages/editor-framework/es/pluginFactory.js b/packages/editor-framework/es/pluginFactory.js new file mode 100644 index 000000000..46937ed10 --- /dev/null +++ b/packages/editor-framework/es/pluginFactory.js @@ -0,0 +1,98 @@ +import _extends from "@babel/runtime/helpers/extends"; +import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized"; +import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; +import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; + +function _createSuper(Derived) { return function () { var Super = _getPrototypeOf(Derived), result; if (_isNativeReflectConstruct()) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } + +import React, { createRef, PureComponent } from 'react'; +import EditorContext from './context'; +import { acceptsRef, generateI18n, isEmpty, transformToPromise } from './utils'; +export default function pluginFactory(Comp) { + var LowcodePlugin = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(LowcodePlugin, _PureComponent); + + var _super = _createSuper(LowcodePlugin); + + function LowcodePlugin(props, context) { + var _extends2; + + var _this; + + _this = _PureComponent.call(this, props, context) || this; + _this.ref = void 0; + _this.editor = void 0; + _this.pluginKey = void 0; + _this.i18n = void 0; + + _this.open = function () { + if (_this.ref && _this.ref.open && typeof _this.ref.open === 'function') { + return transformToPromise(_this.ref.open()); + } + + return Promise.resolve(); + }; + + _this.close = function () { + if (_this.ref && _this.ref.close && typeof _this.ref.close === 'function') { + return transformToPromise(_this.ref.close()); + } + + return Promise.resolve(); + }; + + if (isEmpty(props.config) || !props.config.pluginKey) { + console.warn('lowcode editor plugin has wrong config'); + return _assertThisInitialized(_this); + } + + var editor = props.editor; + _this.ref = createRef(); // 注册插件 + + _this.editor = editor; + _this.pluginKey = props.config.pluginKey; + var defaultProps = Comp.defaultProps || {}; + var locale = _this.editor.get('locale') || defaultProps.locale || 'zh-CN'; + var editorMessages = _this.editor.get('messages') || {}; + var messages = editorMessages[_this.pluginKey] || defaultProps.messages || {}; + _this.i18n = generateI18n(locale, messages); + editor.set('plugins', _extends({}, editor.plugins, (_extends2 = {}, _extends2[_this.pluginKey] = _assertThisInitialized(_this), _extends2))); + return _this; + } + + var _proto = LowcodePlugin.prototype; + + _proto.componentWillUnmount = function componentWillUnmount() { + // 销毁插件 + if (this.pluginKey && this.editor && this.editor.plugins) { + delete this.editor.plugins[this.pluginKey]; + } + }; + + _proto.render = function render() { + var config = this.props.config; + + var props = _extends({ + i18n: this.i18n, + editor: this.editor, + config: config + }, config.pluginProps); + + if (acceptsRef(Comp)) { + props.ref = this.ref; + } + + return /*#__PURE__*/React.createElement(Comp, props); + }; + + return LowcodePlugin; + }(PureComponent); + + LowcodePlugin.displayName = 'LowcodeEditorPlugin'; + LowcodePlugin.contextType = EditorContext; + LowcodePlugin.init = Comp.init; + return LowcodePlugin; +} \ No newline at end of file diff --git a/packages/editor-framework/es/style.js b/packages/editor-framework/es/style.js new file mode 100644 index 000000000..f90814d63 --- /dev/null +++ b/packages/editor-framework/es/style.js @@ -0,0 +1 @@ +//empty file \ No newline at end of file diff --git a/packages/editor-framework/es/utils.d.ts b/packages/editor-framework/es/utils.d.ts new file mode 100644 index 000000000..c1d4ff1e8 --- /dev/null +++ b/packages/editor-framework/es/utils.d.ts @@ -0,0 +1,71 @@ +/// +import Editor from './editor'; +import { EditorConfig, I18nFunction, I18nMessages, LocaleType, ShortCutsConfig } from './definitions'; +export declare const pick: any; +export declare const deepEqual: any; +export declare const clone: any; +export declare const isEmpty: any; +export declare const throttle: any; +export declare const debounce: any; +export declare const serialize: any; +declare global { + interface Window { + sendIDEMessage?: (params: IDEMessageParams) => void; + goldlog?: { + record: (logKey: string, gmKey: string, goKey: string, method: 'POST' | 'GET') => (...args: any[]) => any; + }; + is_theia?: boolean; + vscode?: boolean; + } +} +export interface IDEMessageParams { + action: string; + data: { + logKey: string; + gmKey: string; + goKey: string; + }; +} +export declare function generateI18n(locale?: LocaleType, messages?: I18nMessages): I18nFunction; +/** + * 序列化参数 + */ +export declare function serializeParams(obj: object): string; +/** + * 黄金令箭埋点 + * @param {String} gmKey 为黄金令箭业务类型 + * @param {Object} params 参数 + * @param {String} logKey 属性串 + */ +export declare function goldlog(gmKey: string, params?: object, logKey?: string): void; +/** + * 获取当前编辑器环境 + */ +export declare function getEnv(): string; +export declare function registShortCuts(config: ShortCutsConfig, editor: Editor): void; +export declare function unRegistShortCuts(config: ShortCutsConfig): void; +/** + * 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve + */ +export declare function transformToPromise(input: any): Promise<{}>; +/** + * 将数组类型转换为Map类型 + */ +interface MapOf { + [propName: string]: T; +} +export declare function transformArrayToMap(arr: T[], key: string, overwrite?: boolean): MapOf; +/** + * 解析url的查询参数 + */ +interface Query { + [propName: string]: string; +} +export declare function parseSearch(search: string): Query; +export declare function comboEditorConfig(defaultConfig: EditorConfig, customConfig: EditorConfig): EditorConfig; +/** + * 判断当前组件是否能够设置ref + * @param {*} Comp 需要判断的组件 + */ +export declare function acceptsRef(Comp: React.ReactNode): boolean; +export {}; diff --git a/packages/editor-framework/es/utils.js b/packages/editor-framework/es/utils.js new file mode 100644 index 000000000..b2a677d4c --- /dev/null +++ b/packages/editor-framework/es/utils.js @@ -0,0 +1,270 @@ +import _extends from "@babel/runtime/helpers/extends"; +import IntlMessageFormat from 'intl-messageformat'; +import keymaster from 'keymaster'; +import _clone from 'lodash/cloneDeep'; +import _debounce from 'lodash/debounce'; +import _isEmpty from 'lodash/isEmpty'; +import _deepEqual from 'lodash/isEqualWith'; +import _pick from 'lodash/pick'; +import _throttle from 'lodash/throttle'; +import _serialize from 'serialize-javascript'; +export var pick = _pick; +export var deepEqual = _deepEqual; +export var clone = _clone; +export var isEmpty = _isEmpty; +export var throttle = _throttle; +export var debounce = _debounce; +export var serialize = _serialize; +var ENV = { + TBE: 'TBE', + WEBIDE: 'WEB-IDE', + VSCODE: 'VSCODE', + WEB: 'WEB' +}; + +/* + * 用于构造国际化字符串处理函数 + */ +export function generateI18n(locale, messages) { + if (locale === void 0) { + locale = 'zh-CN'; + } + + if (messages === void 0) { + messages = {}; + } + + return function (key, values) { + if (!messages || !messages[key]) { + return ''; + } + + var formater = new IntlMessageFormat(messages[key], locale); + return formater.format(values); + }; +} +/** + * 序列化参数 + */ + +export function serializeParams(obj) { + if (typeof obj !== 'object') { + return ''; + } + + var res = []; + Object.entries(obj).forEach(function (_ref) { + var key = _ref[0], + val = _ref[1]; + + 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) { + if (params === void 0) { + params = {}; + } + + if (logKey === void 0) { + logKey = 'other'; + } + + var sendIDEMessage = window.sendIDEMessage || window.parent.sendIDEMessage; + var goKey = serializeParams(_extends({ + env: getEnv() + }, params)); + + if (sendIDEMessage) { + sendIDEMessage({ + action: 'goldlog', + data: { + logKey: "/iceluna.core." + logKey, + gmKey: gmKey, + goKey: goKey + } + }); + } + + if (window.goldlog) { + window.goldlog.record("/iceluna.core." + logKey, gmKey, goKey, 'POST'); + } +} +/** + * 获取当前编辑器环境 + */ + +export function getEnv() { + var userAgent = navigator.userAgent; + var isVscode = /Electron\//.test(userAgent); + + if (isVscode) { + return ENV.VSCODE; + } + + var isTheia = window.is_theia === true; + + if (isTheia) { + return ENV.WEBIDE; + } + + return ENV.WEB; +} // 注册快捷键 + +export function registShortCuts(config, editor) { + (config || []).forEach(function (item) { + keymaster(item.keyboard, function (ev) { + ev.preventDefault(); + item.handler(editor, ev, keymaster); + }); + }); +} // 取消注册快捷 + +export function unRegistShortCuts(config) { + (config || []).forEach(function (item) { + keymaster.unbind(item.keyboard); + }); + + if (window.parent.vscode) { + keymaster.unbind('command+c'); + keymaster.unbind('command+v'); + } +} +/** + * 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve + */ + +export function transformToPromise(input) { + if (input instanceof Promise) { + return input; + } + + return new Promise(function (resolve, reject) { + if (input || input === undefined) { + resolve(); + } else { + reject(); + } + }); +} +/** + * 将数组类型转换为Map类型 + */ + +export function transformArrayToMap(arr, key, overwrite) { + if (overwrite === void 0) { + overwrite = true; + } + + if (isEmpty(arr) || !Array.isArray(arr)) { + return {}; + } + + var res = {}; + arr.forEach(function (item) { + var curKey = item[key]; + + if (item[key] === undefined) { + return; + } + + if (res[curKey] && !overwrite) { + return; + } + + res[curKey] = item; + }); + return res; +} +/** + * 解析url的查询参数 + */ + +export function parseSearch(search) { + if (!search || typeof search !== 'string') { + return {}; + } + + var str = search.replace(/^\?/, ''); + var paramStr = str.split('&'); + var res = {}; + paramStr.forEach(function (item) { + var regRes = item.split('='); + + if (regRes[0] && regRes[1]) { + res[regRes[0]] = decodeURIComponent(regRes[1]); + } + }); + return res; +} +export function comboEditorConfig(defaultConfig, customConfig) { + if (defaultConfig === void 0) { + defaultConfig = {}; + } + + var _ref2 = customConfig || {}, + skeleton = _ref2.skeleton, + theme = _ref2.theme, + plugins = _ref2.plugins, + hooks = _ref2.hooks, + shortCuts = _ref2.shortCuts, + lifeCycles = _ref2.lifeCycles, + constants = _ref2.constants, + utils = _ref2.utils, + i18n = _ref2.i18n; + + if (skeleton && skeleton.handler && typeof skeleton.handler === 'function') { + return skeleton.handler(_extends({ + skeleton: skeleton + }, defaultConfig)); + } + + var defaultShortCuts = transformArrayToMap(defaultConfig.shortCuts || [], 'keyboard'); + var customShortCuts = transformArrayToMap(shortCuts || [], 'keyboard'); + var localeList = ['zh-CN', 'zh-TW', 'en-US', 'ja-JP']; + var i18nConfig = {}; + localeList.forEach(function (key) { + i18nConfig[key] = _extends({}, defaultConfig.i18n && defaultConfig.i18n[key], {}, i18n && i18n[key]); + }); + return { + skeleton: skeleton, + theme: _extends({}, defaultConfig.theme, {}, theme), + plugins: _extends({}, defaultConfig.plugins, {}, plugins), + hooks: [].concat(defaultConfig.hooks || [], hooks || []), + shortCuts: Object.values(_extends({}, defaultShortCuts, {}, customShortCuts)), + lifeCycles: _extends({}, defaultConfig.lifeCycles, {}, lifeCycles), + constants: _extends({}, defaultConfig.constants, {}, constants), + utils: [].concat(defaultConfig.utils || [], utils || []), + i18n: i18nConfig + }; +} +/** + * 判断当前组件是否能够设置ref + * @param {*} Comp 需要判断的组件 + */ + +export function acceptsRef(Comp) { + var hasSymbol = typeof Symbol === 'function' && Symbol["for"]; + var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol["for"]('react.forward_ref') : 0xead0; + + if (!Comp || typeof Comp !== 'object' || isEmpty(Comp)) { + return false; + } + + return Comp.$$typeof && Comp.$$typeof === REACT_FORWARD_REF_TYPE || Comp.prototype && Comp.prototype.isReactComponent; +} \ No newline at end of file diff --git a/packages/editor-framework/package.json b/packages/editor-framework/package.json index 3d1465113..f7cecc059 100644 --- a/packages/editor-framework/package.json +++ b/packages/editor-framework/package.json @@ -1,5 +1,5 @@ { - "name": "@ali/lowcode-engine-editor", + "name": "@ali/lowcode-editor-framework", "version": "0.0.1", "description": "alibaba lowcode editor core", "files": [ @@ -13,7 +13,7 @@ "stylePath": "style.js", "scripts": { "start": "build-scripts start", - "build": "build-scripts build", + "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}\" " @@ -51,5 +51,5 @@ "@alifd/next": "1.x" }, "license": "MIT", - "homepage": "https://unpkg.com/editor-framework@0.0.1/build/index.html" + "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-editor-framework@0.0.1/build/index.html" } diff --git a/packages/editor/src/framework/areaManager.ts b/packages/editor-framework/src/areaManager.ts similarity index 52% rename from packages/editor/src/framework/areaManager.ts rename to packages/editor-framework/src/areaManager.ts index 28f24eb0e..f6ce5e782 100644 --- a/packages/editor/src/framework/areaManager.ts +++ b/packages/editor-framework/src/areaManager.ts @@ -1,4 +1,4 @@ -import { PluginConfig, PluginStatus } from './definitions'; +import { PluginConfig, PluginStatus, PluginClass, HOCPlugin } from './definitions'; import Editor from './index'; import { clone, deepEqual } from './utils'; @@ -18,14 +18,16 @@ export default class AreaManager { this.pluginStatus = clone(editor.pluginStatus); } - public isPluginStatusUpdate(pluginType?: string): boolean { + public isPluginStatusUpdate(pluginType?: string, notUpdateStatus?: boolean): boolean { const { pluginStatus } = this.editor; const list = pluginType ? this.config.filter((item): boolean => item.type === pluginType) : this.config; const isUpdate = list.some( - (item): boolean => !deepEqual(pluginStatus[item.pluginKey], this.pluginStatus[item.pluginKey]) + (item): boolean => !deepEqual(pluginStatus[item.pluginKey], this.pluginStatus[item.pluginKey]), ); - this.pluginStatus = clone(pluginStatus); + if (!notUpdateStatus) { + this.pluginStatus = clone(pluginStatus); + } return isUpdate; } @@ -36,7 +38,28 @@ export default class AreaManager { return pluginType ? res.filter((item): boolean => item.type === pluginType) : res; } - public getPluginConfig(): PluginConfig[] { + public getPlugin(pluginKey: string): HOCPlugin | void { + if (pluginKey) { + return this.editor && this.editor.plugins && this.editor.plugins[pluginKey]; + } + } + + public getPluginConfig(pluginKey?: string): PluginConfig[] | PluginConfig | undefined { + if (pluginKey) { + return this.config.find(item => item.pluginKey === pluginKey); + } return this.config; } + + public getPluginClass(pluginKey: string): PluginClass | void { + if (pluginKey) { + return this.editor && this.editor.components && this.editor.components[pluginKey]; + } + } + + public getPluginStatus(pluginKey: string): PluginStatus | void { + if (pluginKey) { + return this.editor && this.editor.pluginStatus && this.editor.pluginStatus[pluginKey]; + } + } } diff --git a/packages/editor-framework/src/context.ts b/packages/editor-framework/src/context.ts index 78d3ce177..859b79dc4 100644 --- a/packages/editor-framework/src/context.ts +++ b/packages/editor-framework/src/context.ts @@ -1,3 +1,4 @@ import { createContext } from 'react'; + const context = createContext({}); export default context; diff --git a/packages/editor-framework/src/definitions.ts b/packages/editor-framework/src/definitions.ts index 87dd5dca2..f9db0bd01 100644 --- a/packages/editor-framework/src/definitions.ts +++ b/packages/editor-framework/src/definitions.ts @@ -1,48 +1,158 @@ +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, - package: string, - main?: string, - exportName?: string, - subName?: string, - destructuring?: boolean -}; + version: string; + package: string; + main?: string; + exportName?: string; + subName?: string; + destructuring?: boolean; +} export interface SkeletonConfig { - config: NpmConfig, - props?: object, - handler?: (EditorConfig) => EditorConfig -}; + config: NpmConfig; + props?: object; + handler?: (config: EditorConfig) => EditorConfig; +} export interface FusionTheme { - package: string, - version: string -}; + package: string; + version: string; +} export interface ThemeConfig { - fusion?: FusionTheme + fusion?: FusionTheme; } export interface PluginsConfig { - [key]: Array -}; + [key: string]: PluginConfig[]; +} export interface PluginConfig { - pluginKey: string, - type: string, - props: object, - config: NpmConfig, - pluginProps: object -}; + pluginKey: string; + type: string; + 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 type HooksConfig = HookConfig[]; export interface HookConfig { + message: string; + type: 'on' | 'once'; + handler: (editor: Editor, ...args: []) => void; +} +export type ShortCutsConfig = ShortCutConfig[]; + +export interface ShortCutConfig { + keyboard: string; + handler: (editor: Editor, ev: Event, keymaster: any) => void; +} + +export type UtilsConfig = UtilConfig[]; + +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 { + [key: string]: string; +} + +export interface I18nConfig { + 'zh-CN'?: I18nMessages; + 'zh-TW'?: I18nMessages; + 'en-US'?: I18nMessages; + 'ja-JP'?: I18nMessages; +} + +export type I18nFunction = (key: string, params: any) => string; + +export interface Utils { + [key: string]: (...args: []) => any; +} + +export interface PluginProps { + editor: Editor; + config: PluginConfig; + i18n?: I18nFunction; + ref?: React.RefObject; + [key: string]: any; +} + +export type Plugin = React.ReactNode & { + open?: () => boolean | void | Promise; + close?: () => boolean | void | Promise; }; +export type HOCPlugin = React.ReactNode & { + open: () => Promise; + close: () => Promise; +}; +export interface PluginSet { + [key: string]: HOCPlugin; +} + +export type PluginClass = React.ComponentType & { + init?: (editor: Editor) => void; + defaultProps?: { + locale?: LocaleType; + messages?: I18nMessages; + }; +}; + +export interface PluginClassSet { + [key: string]: PluginClass; +} + +export interface PluginStatus { + disabled?: boolean; + visible?: boolean; + marked?: boolean; + locked?: boolean; +} + +export interface PluginStatusSet { + [key: string]: PluginStatus; +} diff --git a/packages/editor-framework/src/editor.ts b/packages/editor-framework/src/editor.ts index b3328df2d..8686fa34a 100644 --- a/packages/editor-framework/src/editor.ts +++ b/packages/editor-framework/src/editor.ts @@ -1,37 +1,56 @@ -import EventEmitter from 'events'; import Debug from 'debug'; +import { EventEmitter } from 'events'; import store from 'store'; - import { - unRegistShortCuts, - registShortCuts, - transformToPromise, - generateI18n -} from './utils'; + EditorConfig, + HooksConfig, + LocaleType, + PluginStatusSet, + Utils, + PluginClassSet, + PluginSet, +} from './definitions'; + +import * as editorUtils from './utils'; + +const { registShortCuts, transformToPromise, unRegistShortCuts } = editorUtils; + +declare global { + interface Window { + __isDebug?: boolean; + __newFunc?: (funcStr: string) => (...args: any[]) => any; + } +} // 根据url参数设置debug选项 -const res = /_?debug=(.*?)(&|$)/.exec(location.search); -if (res && res[1]) { +const debugRegRes = /_?debug=(.*?)(&|$)/.exec(location.search); +if (debugRegRes && debugRegRes[1]) { + // eslint-disable-next-line no-underscore-dangle window.__isDebug = true; - store.storage.write('debug', res[1] === 'true' ? '*' : res[1]); + store.storage.write('debug', debugRegRes[1] === 'true' ? '*' : debugRegRes[1]); } else { + // eslint-disable-next-line no-underscore-dangle window.__isDebug = false; store.remove('debug'); } -//重要,用于矫正画布执行new Function的window对象上下文 -window.__newFunc = funContext => { - return new Function(funContext); +// 重要,用于矫正画布执行new Function的window对象上下文 +// eslint-disable-next-line no-underscore-dangle +window.__newFunc = (funContext: string): ((...args: any[]) => any) => { + // eslint-disable-next-line no-new-func + return new Function(funContext) as (...args: any[]) => any; }; -//关闭浏览器前提醒,只有产生过交互才会生效 -window.onbeforeunload = function(e) { - e = e || window.event; +// 关闭浏览器前提醒,只有产生过交互才会生效 +window.onbeforeunload = function(e: Event): string | void { + const ev = e || window.event; // 本地调试不生效 - if (location.href.indexOf('localhost') > 0) return; - var msg = '您确定要离开此页面吗?'; - e.cancelBubble = true; - e.returnValue = msg; + if (location.href.indexOf('localhost') > 0) { + return; + } + const msg = '您确定要离开此页面吗?'; + ev.cancelBubble = true; + ev.returnValue = true; if (e.stopPropagation) { e.stopPropagation(); e.preventDefault(); @@ -39,142 +58,165 @@ window.onbeforeunload = function(e) { return msg; }; +let instance: Editor; -let instance = null; const debug = Debug('editor'); EventEmitter.defaultMaxListeners = 100; -export interface editor { - -}; +export interface HooksFuncs { + [idx: number]: (msg: string, handler: (...args) => void) => void; +} export default class Editor extends EventEmitter { - static getInstance = () => { + public static getInstance = (config: EditorConfig, components: PluginClassSet, utils?: Utils): Editor => { if (!instance) { - instance = new Editor(); + instance = new Editor(config, components, utils); } return instance; }; - constructor(config) { + public config: EditorConfig; + + public components: PluginClassSet; + + public utils: Utils; + + public pluginStatus: PluginStatusSet; + + public plugins: PluginSet; + + public locale: LocaleType; + + private hooksFuncs: HooksFuncs; + + constructor(config: EditorConfig, components: PluginClassSet, utils?: Utils) { super(); + this.config = config; + this.components = components; + this.utils = { ...editorUtils, ...utils }; instance = this; - Object.assign(this, config); this.init(); } - init() { - const { - hooks, - shortCuts, - lifeCycles - } = this.config || {}; - this.destroy(); + public 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.messages = this.messagesSet[this.locale]; + // this.i18n = generateI18n(this.locale, this.messages); this.pluginStatus = this.initPluginStatus(); - this.initHooks(hooks, appHelper); + this.initHooks(hooks || []); - appHelper.emit('editor.beforeInit'); - const init = lifeCycles && lifeCycles.init || () => {}; + this.emit('editor.beforeInit'); + const init = (lifeCycles && lifeCycles.init) || ((): void => {}); // 用户可以通过设置extensions.init自定义初始化流程; - transformToPromise(init(this)) - .then(() => { + return transformToPromise(init(this)) + .then((): boolean => { // 注册快捷键 registShortCuts(shortCuts, this); this.emit('editor.afterInit'); + return true; }) - .catch(err => { - console.warn(err); + .catch((err): void => { + console.error(err); }); } - destroy() { + public destroy(): void { + debug('destroy'); try { - const { - hooks = [], - shortCuts = [], - lifeCycles = {} - } = this.config; - unRegistShortCuts(shortCuts); - this.destroyHooks(hooks); - lifeCycles.destroy && lifeCycles.destroy(); - } catch (err) { - console.warn(err); - return; + const { hooks = [], shortCuts = [], lifeCycles = {} } = this.config; + unRegistShortCuts(shortCuts); + this.destroyHooks(hooks); + if (lifeCycles.destroy) { + lifeCycles.destroy(this); } + } catch (err) { + console.warn(err); + } } - get(key:string):any { + public get(key: string): any { return this[key]; } - set(key:string|object, val:any):void { + public 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'); + console.error('init, destroy, get, set, batchOn, batchOff, batchOnce is private attribute'); return; } this[key] = val; } else if (typeof key === 'object') { - Object.keys(key).forEach(item => { + Object.keys(key).forEach((item): void => { this[item] = key[item]; }); } } - batchOn(events:Array, lisenter:function):void { - if (!Array.isArray(events)) return; - events.forEach(event => this.on(event, lisenter)); + public batchOn(events: string[], lisenter: (...args) => void): void { + if (!Array.isArray(events)) { + return; + } + events.forEach((event): void => { + this.on(event, lisenter); + }); } - batchOnce(events:Array, lisenter:function):void { - if (!Array.isArray(events)) return; - events.forEach(event => this.once(event, lisenter)); + public batchOnce(events: string[], lisenter: (...args) => void): void { + if (!Array.isArray(events)) { + return; + } + events.forEach((event): void => { + this.once(event, lisenter); + }); } - batchOff(events:Array, lisenter:function):void { - if (!Array.isArray(events)) return; - events.forEach(event => this.off(event, lisenter)); + public batchOff(events: string[], lisenter: (...args) => void): void { + if (!Array.isArray(events)) { + return; + } + events.forEach((event): void => { + 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]); + // 销毁hooks中的消息监听 + private destroyHooks(hooks: HooksConfig = []): void { + hooks.forEach((item, idx): void => { + 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 => { - const func = (...args) => { + // 初始化hooks中的消息监听 + private initHooks(hooks: HooksConfig = []): void { + this.hooksFuncs = hooks.map((item): ((...arg) => void) => { + const func = (...args): void => { item.handler(this, ...args); }; this[item.type](item.message, func); return func; }); - }; + } - - private initPluginStatus () { - const {plugins = {}} = this.config; + private initPluginStatus(): PluginStatusSet { + 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 || {}; + const res: PluginStatusSet = {}; + pluginAreas.forEach((area): void => { + (plugins[area] || []).forEach((plugin): void => { + if (plugin.type === 'Divider') { + return; + } + 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.props.components[skeletonUtils.generateAddonCompName(addon.addonKey)]; + const pluginClass = this.components[plugin.pluginKey]; // 判断如果编辑器插件有init静态方法,则在此执行init方法 if (pluginClass && pluginClass.init) { pluginClass.init(this); @@ -182,5 +224,5 @@ export default class Editor extends EventEmitter { }); }); return res; - }; + } } diff --git a/packages/editor-framework/src/index.ts b/packages/editor-framework/src/index.ts index aac18d138..fba0e30c9 100644 --- a/packages/editor-framework/src/index.ts +++ b/packages/editor-framework/src/index.ts @@ -1,4 +1,12 @@ import Editor from './editor'; +import * as utils from './utils'; +import * as definitions from './definitions'; -export default Editor; \ No newline at end of file +export { default as PluginFactory } from './pluginFactory'; +export { default as EditorContext } from './context'; +export { default as AreaManager } from './areaManager'; + +export default Editor; + +export { utils, definitions }; diff --git a/packages/editor-framework/src/plugin.ts b/packages/editor-framework/src/plugin.ts deleted file mode 100644 index 33d81cc48..000000000 --- a/packages/editor-framework/src/plugin.ts +++ /dev/null @@ -1,129 +0,0 @@ -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 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 { - 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/src/framework/pluginFactory.tsx b/packages/editor-framework/src/pluginFactory.tsx similarity index 82% rename from packages/editor/src/framework/pluginFactory.tsx rename to packages/editor-framework/src/pluginFactory.tsx index f32edd9e9..2eff60e9e 100644 --- a/packages/editor/src/framework/pluginFactory.tsx +++ b/packages/editor-framework/src/pluginFactory.tsx @@ -27,21 +27,26 @@ export default function pluginFactory(Comp: PluginClass): React.ComponentType(); // 注册插件 this.editor = editor; - this.i18n = generateI18n(locale, messages); this.pluginKey = props.config.pluginKey; + const defaultProps = Comp.defaultProps || {}; + const locale = this.editor.get('locale') || defaultProps.locale || 'zh-CN'; + const editorMessages = this.editor.get('messages') || {}; + const messages = editorMessages[this.pluginKey] || defaultProps.messages || {}; + this.i18n = generateI18n(locale, messages); + editor.set('plugins', { ...editor.plugins, - [this.pluginKey]: this + [this.pluginKey]: this, }); } public componentWillUnmount(): void { // 销毁插件 - if (this.editor && this.editor.plugins) { + if (this.pluginKey && this.editor && this.editor.plugins) { delete this.editor.plugins[this.pluginKey]; } } @@ -66,7 +71,7 @@ export default function pluginFactory(Comp: PluginClass): React.ComponentType void; + goldlog?: { + record: (logKey: string, gmKey: string, goKey: string, method: 'POST' | 'GET') => (...args: any[]) => any; + }; + is_theia?: boolean; + vscode?: boolean; + } +} + +export interface IDEMessageParams { + action: string; + data: { + logKey: string; + gmKey: string; + goKey: string; + }; +} + +/* * 用于构造国际化字符串处理函数 - * @param {*} locale 国际化标识,例如 zh-CN、en-US - * @param {*} messages 国际化语言包 */ -export function generateI18n(locale = 'zh-CN', messages = {}) { - return (key, values = {}) => { - if (!messages || !messages[key]) return ''; +export function generateI18n(locale: LocaleType = 'zh-CN', messages: I18nMessages = {}): I18nFunction { + return (key: string, values): string => { + if (!messages || !messages[key]) { + return ''; + } const formater = new IntlMessageFormat(messages[key], locale); return formater.format(values); }; @@ -19,14 +63,16 @@ 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; +export function serializeParams(obj: object): string { + if (typeof obj !== 'object') { + return ''; + } + const res: string[] = []; + Object.entries(obj).forEach(([key, val]): void => { + if (val === null || val === undefined || val === '') { + return; + } if (typeof val === 'object') { res.push(`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`); } else { @@ -42,12 +88,11 @@ export function serializeParams(obj:object):string { * @param {Object} params 参数 * @param {String} logKey 属性串 */ -export function goldlog(gmKey, params = {}, logKey = 'other') { +export function goldlog(gmKey: string, params: object = {}, logKey: string = 'other'): void { const sendIDEMessage = window.sendIDEMessage || window.parent.sendIDEMessage; const goKey = serializeParams({ - sdkVersion: pkg.version, env: getEnv(), - ...params + ...params, }); if (sendIDEMessage) { sendIDEMessage({ @@ -55,155 +100,60 @@ 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'); + if (window.goldlog) { + window.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; + if (isVscode) { + return ENV.VSCODE; + } const isTheia = window.is_theia === true; - if (isTheia) return ENV.WEBIDE; + 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 => { +export function registShortCuts(config: ShortCutsConfig, editor: Editor): void { + (config || []).forEach((item): void => { + keymaster(item.keyboard, (ev: Event): void => { ev.preventDefault(); - item.handler(ev, appHelper, keymaster); + item.handler(editor, ev, keymaster); }); }); } // 取消注册快捷 -export function unRegistShortCuts(config) { - (config || []).forEach(item => { +export function unRegistShortCuts(config: ShortCutsConfig): void { + (config || []).forEach((item): void => { 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) => { +/** + * 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve + */ +export function transformToPromise(input: any): Promise<{}> { + if (input instanceof Promise) { + return input; + } + return new Promise((resolve, reject): void => { if (input || input === undefined) { resolve(); } else { @@ -212,31 +162,111 @@ 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]); +/** + * 将数组类型转换为Map类型 + */ +interface MapOf { + [propName: string]: T; +} +export function transformArrayToMap(arr: T[], key: string, overwrite: boolean = true): MapOf { + if (isEmpty(arr) || !Array.isArray(arr)) { + return {}; + } + const res = {}; + arr.forEach((item): void => { + const curKey = item[key]; + if (item[key] === undefined) { + return; + } + if (res[curKey] && !overwrite) { + return; + } + res[curKey] = item; + }); + return res; +} + +/** + * 解析url的查询参数 + */ +interface Query { + [propName: string]: string; +} +export function parseSearch(search: string): Query { + if (!search || typeof search !== 'string') { + return {}; + } + const str = search.replace(/^\?/, ''); + const paramStr = str.split('&'); + const res = {}; + paramStr.forEach((item): void => { + const regRes = item.split('='); + if (regRes[0] && regRes[1]) { + res[regRes[0]] = decodeURIComponent(regRes[1]); + } + }); + return res; +} + +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, + }); + } + + 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): void => { + i18nConfig[key] = { + ...(defaultConfig.i18n && defaultConfig.i18n[key]), + ...(i18n && i18n[key]), }; - return { - ...ideConfig, - utils: { - ...skeletonUtils, - ...utils - }, - constants: { - ...defaultIdeConfig.constants, - ...ideConfig.constants - }, - extensions: { - ...defaultIdeConfig.extensions, - ...ideConfig.extensions - }, - shortCuts: comboShortCuts() - }; -} \ No newline at end of file + }); + 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, + }; +} + +/** + * 判断当前组件是否能够设置ref + * @param {*} Comp 需要判断的组件 + */ +export function acceptsRef(Comp: React.ReactNode): boolean { + const hasSymbol = typeof Symbol === 'function' && Symbol.for; + const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0; + if (!Comp || typeof Comp !== 'object' || isEmpty(Comp)) { + return false; + } + return ( + (Comp.$$typeof && Comp.$$typeof === REACT_FORWARD_REF_TYPE) || (Comp.prototype && Comp.prototype.isReactComponent) + ); +} diff --git a/packages/editor-skeleton/.eslintrc.js b/packages/editor-skeleton/.eslintrc.js index 18ae6baa7..4d6be3c66 100644 --- a/packages/editor-skeleton/.eslintrc.js +++ b/packages/editor-skeleton/.eslintrc.js @@ -1,7 +1,16 @@ -const { eslint, deepmerge } = require('@ice/spec'); +const { tslint, deepmerge } = require('@ice/spec'); -module.exports = deepmerge(eslint, { +module.exports = deepmerge(tslint, { rules: { "global-require": 0, + "comma-dangle": 0, + "no-unused-expressions": 0, + "object-shorthand": 0, + "jsx-a11y/anchor-has-content": 0, + "react/sort-comp": 0, + "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".tsx", "ts"] }], + "@typescript-eslint/interface-name-prefix": 0, + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/explicit-member-accessibility": 0 }, }); diff --git a/packages/editor-skeleton/es/components/LeftIcon/index.scss b/packages/editor-skeleton/es/components/LeftIcon/index.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/editor-skeleton/es/components/LeftPlugin/index.d.ts b/packages/editor-skeleton/es/components/LeftPlugin/index.d.ts index 312fc40aa..b503d9bae 100644 --- a/packages/editor-skeleton/es/components/LeftPlugin/index.d.ts +++ b/packages/editor-skeleton/es/components/LeftPlugin/index.d.ts @@ -1,30 +1,36 @@ -import { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; +import Editor from '@ali/lowcode-editor-framework'; +import { PluginConfig, PluginClass } from '@ali/lowcode-editor-framework/lib/definitions'; import './index.scss'; -export default class LeftAddon extends PureComponent { +export interface LeftPluginProps { + active?: boolean; + config: PluginConfig; + disabled?: boolean; + editor: Editor; + locked?: boolean; + marked?: boolean; + onClick?: () => void; + pluginClass: PluginClass | undefined; +} +export interface LeftPluginState { + dialogVisible: boolean; +} +export default class LeftPlugin 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; + marked: 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; + renderIcon: (clickCallback: any) => any; + render(): React.ReactNode; } diff --git a/packages/editor-skeleton/es/components/LeftPlugin/index.js b/packages/editor-skeleton/es/components/LeftPlugin/index.js index 517104928..e939943fc 100644 --- a/packages/editor-skeleton/es/components/LeftPlugin/index.js +++ b/packages/editor-skeleton/es/components/LeftPlugin/index.js @@ -1,26 +1,30 @@ +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 _Icon from "@alifd/next/es/icon"; 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); +var LeftPlugin = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(LeftPlugin, _PureComponent); - function LeftAddon(_props, context) { + function LeftPlugin(_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]; + var _this$props = _this.props, + config = _this$props.config, + editor = _this$props.editor; + var pluginKey = config && config.pluginKey; + var plugin = editor.plugins && editor.plugins[pluginKey]; - if (currentAddon) { - _this.utils.transformToPromise(currentAddon.close()).then(function () { + if (plugin) { + plugin.close().then(function () { _this.setState({ dialogVisible: false }); @@ -36,33 +40,37 @@ var LeftAddon = /*#__PURE__*/function (_PureComponent) { }; _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; //考虑到弹窗情况,延时发送消息 + var _this$props2 = _this.props, + disabled = _this$props2.disabled, + config = _this$props2.config, + onClick = _this$props2.onClick, + editor = _this$props2.editor; + var pluginKey = config && config.pluginKey; + if (disabled || !pluginKey) return; + + _this.handleOpen(); // 考虑到弹窗情况,延时发送消息 + setTimeout(function () { - return _this.appHelper.emit(addonKey + ".addon.activate"); + editor.emit(pluginKey + ".plugin.activate"); }, 0); - _this.handleOpen(); - - onClick && onClick(); + if (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 _this$props3 = _this.props, + active = _this$props3.active, + disabled = _this$props3.disabled, + marked = _this$props3.marked, + locked = _this$props3.locked, + _onClick = _this$props3.onClick, + config = _this$props3.config; var _ref = config || {}, - addonKey = _ref.addonKey, + pluginKey = _ref.pluginKey, props = _ref.props; var _ref2 = props || {}, @@ -70,24 +78,24 @@ var LeftAddon = /*#__PURE__*/function (_PureComponent) { title = _ref2.title; return React.createElement("div", { - className: classNames('luna-left-addon', addonKey, { + className: classNames('lowcode-left-plugin', pluginKey, { active: active, disabled: disabled, locked: locked }), "data-tooltip": title, onClick: function onClick() { - if (disabled) return; //考虑到弹窗情况,延时发送消息 + if (disabled) return; // 考虑到弹窗情况,延时发送消息 clickCallback && clickCallback(); _onClick && _onClick(); } - }, dotted ? React.createElement(Badge, { + }, marked ? React.createElement(_Badge, { dot: true - }, React.createElement(Icon, { + }, React.createElement(_Icon, { type: icon, size: "small" - })) : React.createElement(Icon, { + })) : React.createElement(_Icon, { type: icon, size: "small" })); @@ -96,118 +104,106 @@ var LeftAddon = /*#__PURE__*/function (_PureComponent) { _this.state = { dialogVisible: false }; - _this.appHelper = context.appHelper; - _this.utils = _this.appHelper.utils; - _this.constants = _this.appHelper.constants; return _this; } - var _proto = LeftAddon.prototype; + var _proto = LeftPlugin.prototype; _proto.componentDidMount = function componentDidMount() { - var config = this.props.config; - var addonKey = config && config.addonKey; - var appHelper = this.appHelper; + var _this$props4 = this.props, + config = _this$props4.config, + editor = _this$props4.editor; + var pluginKey = config && config.pluginKey; - if (appHelper && addonKey) { - appHelper.on(addonKey + ".dialog.show", this.handleShow); - appHelper.on(addonKey + ".dialog.close", this.handleClose); + if (editor && pluginKey) { + editor.on(pluginKey + ".dialog.show", this.handleShow); + editor.on(pluginKey + ".dialog.close", this.handleClose); } }; _proto.componentWillUnmount = function componentWillUnmount() { - var config = this.props.config; - var appHelper = this.appHelper; - var addonKey = config && config.addonKey; + var _this$props5 = this.props, + config = _this$props5.config, + editor = _this$props5.editor; + var pluginKey = config && config.pluginKey; - if (appHelper && addonKey) { - appHelper.off(addonKey + ".dialog.show", this.handleShow); - appHelper.off(addonKey + ".dialog.close", this.handleClose); + if (editor && pluginKey) { + editor.off(pluginKey + ".dialog.show", this.handleShow); + editor.off(pluginKey + ".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 _this$props6 = this.props, + marked = _this$props6.marked, + locked = _this$props6.locked, + active = _this$props6.active, + disabled = _this$props6.disabled, + config = _this$props6.config, + editor = _this$props6.editor, + Comp = _this$props6.pluginClass; var _ref3 = config || {}, - addonKey = _ref3.addonKey, + pluginKey = _ref3.pluginKey, props = _ref3.props, type = _ref3.type, - addonProps = _ref3.addonProps; + pluginProps = _ref3.pluginProps; 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({ + if (!pluginKey || !type || !props) return null; + var node = Comp ? React.createElement(Comp, _extends({ + editor: editor, active: active, locked: locked, disabled: disabled, config: config, onClick: function onClick() { - _onClick2 && _onClick2.call(null, appHelper); + _onClick2 && _onClick2.call(null, editor); } - }, localeProps, addonProps || {})) || null; + }, pluginProps)) : null; switch (type) { case 'LinkIcon': return React.createElement("a", props.linkProps || {}, this.renderIcon(function () { - _onClick2 && _onClick2.call(null, appHelper); + _onClick2 && _onClick2.call(null, editor); })); case 'Icon': return this.renderIcon(function () { - _onClick2 && _onClick2.call(null, appHelper); + _onClick2 && _onClick2.call(null, editor); }); case 'DialogIcon': return React.createElement(Fragment, null, this.renderIcon(function () { - _onClick2 && _onClick2.call(null, appHelper); + _onClick2 && _onClick2.call(null, editor); _this2.handleOpen(); - }), React.createElement(Dialog, _extends({ + }), React.createElement(_Dialog, _extends({ onOk: function onOk() { - appHelper.emit(addonKey + ".dialog.onOk"); + editor.emit(pluginKey + ".dialog.onOk"); _this2.handleClose(); }, onCancel: this.handleClose, onClose: this.handleClose, - title: title + title: title, + style: _extends({ + width: 500 + }, props.dialogProps && props.dialogProps.style) }, props.dialogProps || {}, { visible: dialogVisible }), node)); case 'BalloonIcon': - return React.createElement(Balloon, _extends({ + return React.createElement(_Balloon, _extends({ trigger: this.renderIcon(function () { - _onClick2 && _onClick2.call(null, appHelper); + _onClick2 && _onClick2.call(null, editor); }), align: "r", triggerType: ['click', 'hover'] @@ -215,13 +211,11 @@ var LeftAddon = /*#__PURE__*/function (_PureComponent) { case 'PanelIcon': return this.renderIcon(function () { - _onClick2 && _onClick2.call(null, appHelper); - - _this2.handleOpen(); + _onClick2 && _onClick2.call(null, editor); }); case 'Custom': - return dotted ? React.createElement(Badge, { + return marked ? React.createElement(_Badge, { dot: true }, node) : node; @@ -230,30 +224,16 @@ var LeftAddon = /*#__PURE__*/function (_PureComponent) { } }; - return LeftAddon; + return LeftPlugin; }(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 = { +LeftPlugin.displayName = 'LowcodeLeftPlugin'; +LeftPlugin.defaultProps = { active: false, config: {}, disabled: false, - dotted: false, + marked: false, locked: false, onClick: function onClick() {} }; -LeftAddon.contextType = AppContext; -export { LeftAddon as default }; \ No newline at end of file +export { LeftPlugin 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 9c6922129..37b79669b 100644 --- a/packages/editor-skeleton/es/components/LeftPlugin/index.scss +++ b/packages/editor-skeleton/es/components/LeftPlugin/index.scss @@ -1,59 +1,60 @@ -.luna-left-addon { - font-size: 16px; +.lowcode-left-plugin { + font-size: 20px; text-align: center; - line-height: 36px; - height: 36px; + line-height: 44px; + height: 44px; position: relative; cursor: pointer; transition: all 0.3s ease; - color: #777; - &.collapse { - height: 40px; - color: #8c8c8c; - border-bottom: 1px solid #bfbfbf; - } + color: $color-text1-3; &.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; + left: 45px; + top: 8px; line-height: 18px; font-size: 12px; white-space: nowrap; padding: 6px 8px; border-radius: 4px; - background: rgba(0, 0, 0, 0.75); - color: #fff; + background: $balloon-normal-color-bg; + border: 1px solid $balloon-normal-color-border; + opacity: 0.8; + color: $color-text1-3; z-index: 100; } &:after { content: ''; display: block; position: absolute; + width: 10px; + height: 10px; + transform: rotate(45deg); left: 40px; - top: 15px; - border: 5px solid transparent; - border-right-color: rgba(0, 0, 0, 0.75); + top: 18px; + background: $balloon-normal-color-bg; + border-left: 1px solid $balloon-normal-color-border; + border-bottom: 1px solid $balloon-normal-color-border; z-index: 100; } } + &.active { + color: $color-brand1-9; + &.disabled { + color: $color-text1-1; + } + &:hover { + color: $color-brand1-6; + } + } + &.disabled { + cursor: not-allowed; + color: $color-text1-1; + } } diff --git a/packages/editor-skeleton/es/components/Panel/index.d.ts b/packages/editor-skeleton/es/components/Panel/index.d.ts new file mode 100644 index 000000000..28bb28693 --- /dev/null +++ b/packages/editor-skeleton/es/components/Panel/index.d.ts @@ -0,0 +1,27 @@ +import React, { PureComponent } from 'react'; +import './index.scss'; +export interface PanelProps { + align: 'left' | 'right'; + defaultWidth: number; + minWidth: number; + draggable: boolean; + floatable: boolean; + children: Plugin; + visible: boolean; +} +export interface PanelState { + width: number; +} +export default class Panel extends PureComponent { + static displayName: string; + static defaultProps: { + align: string; + defaultWidth: number; + minWidth: number; + draggable: boolean; + floatable: boolean; + visible: boolean; + }; + constructor(props: any); + render(): React.ReactNode; +} diff --git a/packages/editor-skeleton/es/components/Panel/index.js b/packages/editor-skeleton/es/components/Panel/index.js index e69de29bb..54496425f 100644 --- a/packages/editor-skeleton/es/components/Panel/index.js +++ b/packages/editor-skeleton/es/components/Panel/index.js @@ -0,0 +1,55 @@ +import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; +import React, { PureComponent } from 'react'; +import classNames from 'classnames'; +import './index.scss'; + +var Panel = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(Panel, _PureComponent); + + function Panel(props) { + var _this; + + _this = _PureComponent.call(this, props) || this; + _this.state = { + width: props.defaultWidth + }; + return _this; + } + + var _proto = Panel.prototype; + + _proto.render = function render() { + var _this$props = this.props, + align = _this$props.align, + draggable = _this$props.draggable, + floatable = _this$props.floatable, + visible = _this$props.visible; + var width = this.state.width; + return React.createElement("div", { + className: classNames('lowcode-panel', align, { + draggable: draggable, + floatable: floatable, + visible: visible + }), + style: { + width: width, + display: visible ? '' : 'none' + } + }, this.props.children, React.createElement("div", { + className: "drag-area" + })); + }; + + return Panel; +}(PureComponent); + +Panel.displayName = 'LowcodePanel'; +Panel.defaultProps = { + align: 'left', + defaultWidth: 240, + minWidth: 100, + draggable: true, + floatable: false, + visible: true +}; +export { Panel as default }; \ No newline at end of file diff --git a/packages/editor/src/skeleton/components/Panel/index.scss b/packages/editor-skeleton/es/components/Panel/index.scss similarity index 100% rename from packages/editor/src/skeleton/components/Panel/index.scss rename to packages/editor-skeleton/es/components/Panel/index.scss diff --git a/packages/editor-skeleton/es/components/TopIcon/index.d.ts b/packages/editor-skeleton/es/components/TopIcon/index.d.ts index 2bb6854b4..694956cf0 100644 --- a/packages/editor-skeleton/es/components/TopIcon/index.d.ts +++ b/packages/editor-skeleton/es/components/TopIcon/index.d.ts @@ -1,19 +1,19 @@ -import { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; import './index.scss'; -export default class TopIcon extends PureComponent { +export interface TopIconProps { + active?: boolean; + className?: string; + disabled?: boolean; + icon: string; + id?: string; + locked?: boolean; + marked?: boolean; + onClick?: () => void; + style?: React.CSSProperties; + title?: string; +} +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; @@ -22,9 +22,8 @@ export default class TopIcon extends PureComponent { id: string; locked: boolean; onClick: () => void; - showTitle: boolean; style: {}; title: string; }; - render(): JSX.Element; + render(): React.ReactNode; } diff --git a/packages/editor-skeleton/es/components/TopIcon/index.js b/packages/editor-skeleton/es/components/TopIcon/index.js index bfa50a96b..8720bb079 100644 --- a/packages/editor-skeleton/es/components/TopIcon/index.js +++ b/packages/editor-skeleton/es/components/TopIcon/index.js @@ -1,8 +1,6 @@ -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'; @@ -25,42 +23,26 @@ var TopIcon = /*#__PURE__*/function (_PureComponent) { 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, { + return React.createElement("div", { + className: classNames('lowcode-top-icon', className, { active: active, disabled: disabled, locked: locked }), + "data-tooltip": title, id: id, style: style, - onClick: disabled ? null : onClick - }, React.createElement("div", null, React.createElement(_Icon, { - size: "large", + onClick: disabled ? undefined : onClick + }, React.createElement(_Icon, { 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 -}; +TopIcon.displayName = 'LowcodeTopIcon'; TopIcon.defaultProps = { active: false, className: '', @@ -69,7 +51,6 @@ TopIcon.defaultProps = { id: '', locked: false, onClick: function onClick() {}, - showTitle: false, style: {}, title: '' }; diff --git a/packages/editor-skeleton/es/components/TopIcon/index.scss b/packages/editor-skeleton/es/components/TopIcon/index.scss index 1cb3bdfdf..4a826b05f 100644 --- a/packages/editor-skeleton/es/components/TopIcon/index.scss +++ b/packages/editor-skeleton/es/components/TopIcon/index.scss @@ -1,22 +1,68 @@ -.next-btn.next-large.lowcode-top-btn { +.lowcode-top-icon { + display: inline-block; width: 44px; - height: 44px; - padding: 0; - margin: 4px -2px; - text-align: center; - border-radius: 8px; - border: 1px solid transparent; - color: #777; + font-size: 20px; + line-height: 48px; + color: $color-text1-3; + position: relative; &.disabled { cursor: not-allowed; color: $color-text1-1; + &:hover { + color: $color-text1-1; + } + } + &.active { + color: $color-brand1-9; + &:hover { + color: $color-brand1-6; + } } &.locked { color: red !important; } + &:hover { + color: $color-brand1-6; + &:before { + content: attr(data-tooltip); + display: block; + height: auto; + width: auto; + position: absolute; + left: 50%; + transform: translate(-50%, 0); + bottom: -32px; + line-height: 18px; + font-size: 12px; + white-space: nowrap; + padding: 6px 8px; + border-radius: 4px; + background: $balloon-normal-color-bg; + border: 1px solid $balloon-normal-color-border; + opacity: 0.8; + color: $color-text1-3; + z-index: 100; + } + &:after { + content: ''; + display: block; + position: absolute; + width: 10px; + height: 10px; + left: 50%; + transform: translate(-50%, 0) rotate(45deg); + bottom: -5px; + background: $balloon-normal-color-bg; + border-left: 1px solid $balloon-normal-color-border; + border-top: 1px solid $balloon-normal-color-border; + opacity: 1; + visibility: visible; + z-index: 100; + } + } i.next-icon { &:before { - font-size: 17px; + font-size: 16px; } margin-right: 0; line-height: 18px; diff --git a/packages/editor-skeleton/es/components/TopPlugin/index.d.ts b/packages/editor-skeleton/es/components/TopPlugin/index.d.ts index dc09c377e..2ef80c825 100644 --- a/packages/editor-skeleton/es/components/TopPlugin/index.d.ts +++ b/packages/editor-skeleton/es/components/TopPlugin/index.d.ts @@ -1,12 +1,27 @@ -import { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; +import Editor from '@ali/lowcode-editor-framework'; +import { PluginConfig, PluginClass } from '@ali/lowcode-editor-framework/lib/definitions'; import './index.scss'; -export default class TopPlugin extends PureComponent { +export interface TopPluginProps { + active?: boolean; + config: PluginConfig; + disabled?: boolean; + editor: Editor; + locked?: boolean; + marked?: boolean; + onClick?: () => void; + pluginClass: PluginClass | undefined; +} +export interface TopPluginState { + dialogVisible: boolean; +} +export default class TopPlugin extends PureComponent { static displayName: string; static defaultProps: { active: boolean; config: {}; disabled: boolean; - dotted: boolean; + marked: boolean; locked: boolean; onClick: () => void; }; @@ -16,6 +31,6 @@ export default class TopPlugin extends PureComponent { handleShow: () => void; handleClose: () => void; handleOpen: () => void; - renderIcon: (clickCallback: any) => JSX.Element; - render(): JSX.Element; + renderIcon: (clickCallback: any) => any; + render(): React.ReactNode; } diff --git a/packages/editor-skeleton/es/components/TopPlugin/index.js b/packages/editor-skeleton/es/components/TopPlugin/index.js index e881bb6c1..85c9f9ed7 100644 --- a/packages/editor-skeleton/es/components/TopPlugin/index.js +++ b/packages/editor-skeleton/es/components/TopPlugin/index.js @@ -19,25 +19,29 @@ var TopPlugin = /*#__PURE__*/function (_PureComponent) { 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; //考虑到弹窗情况,延时发送消息 + onClick = _this$props.onClick, + editor = _this$props.editor; + var pluginKey = config && config.pluginKey; + if (disabled || !pluginKey) return; + + _this.handleOpen(); // 考虑到弹窗情况,延时发送消息 + setTimeout(function () { - return _this.appHelper.emit(addonKey + ".addon.activate"); + editor.emit(pluginKey + ".plugin.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]; + var _this$props2 = _this.props, + config = _this$props2.config, + editor = _this$props2.editor; + var pluginKey = config && config.pluginKey; + var plugin = editor.plugins && editor.plugins[pluginKey]; - if (currentAddon) { - _this.utils.transformToPromise(currentAddon.close()).then(function () { + if (plugin) { + plugin.close().then(function () { _this.setState({ dialogVisible: false }); @@ -53,13 +57,14 @@ var TopPlugin = /*#__PURE__*/function (_PureComponent) { }; _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 _this$props3 = _this.props, + active = _this$props3.active, + disabled = _this$props3.disabled, + marked = _this$props3.marked, + locked = _this$props3.locked, + config = _this$props3.config, + _onClick = _this$props3.onClick, + editor = _this$props3.editor; var _ref = config || {}, pluginKey = _ref.pluginKey, @@ -70,23 +75,23 @@ var TopPlugin = /*#__PURE__*/function (_PureComponent) { title = _ref2.title; var node = React.createElement(TopIcon, { - className: "lowcode-top-addon " + pluginKey, + className: "lowcode-top-plugin " + pluginKey, active: active, disabled: disabled, locked: locked, icon: icon, title: title, onClick: function onClick() { - if (disabled) return; //考虑到弹窗情况,延时发送消息 + if (disabled) return; // 考虑到弹窗情况,延时发送消息 setTimeout(function () { - return _this.appHelper.emit(pluginKey + ".addon.activate"); + editor.emit(pluginKey + ".plugin.activate"); }, 0); clickCallback && clickCallback(); _onClick && _onClick(); } }); - return dotted ? React.createElement(_Badge, { + return marked ? React.createElement(_Badge, { dot: true }, node) : node; }; @@ -100,34 +105,40 @@ var TopPlugin = /*#__PURE__*/function (_PureComponent) { 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); - // } + var _this$props4 = this.props, + config = _this$props4.config, + editor = _this$props4.editor; + var pluginKey = config && config.pluginKey; + + if (editor && pluginKey) { + editor.on(pluginKey + ".dialog.show", this.handleShow); + editor.on(pluginKey + ".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.componentWillUnmount = function componentWillUnmount() { + var _this$props5 = this.props, + config = _this$props5.config, + editor = _this$props5.editor; + var pluginKey = config && config.pluginKey; + + if (editor && pluginKey) { + editor.off(pluginKey + ".dialog.show", this.handleShow); + editor.off(pluginKey + ".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 _this$props6 = this.props, + active = _this$props6.active, + marked = _this$props6.marked, + locked = _this$props6.locked, + disabled = _this$props6.disabled, + config = _this$props6.config, + editor = _this$props6.editor, + Comp = _this$props6.pluginClass; var _ref3 = config || {}, pluginKey = _ref3.pluginKey, @@ -140,8 +151,9 @@ var TopPlugin = /*#__PURE__*/function (_PureComponent) { title = _ref4.title; var dialogVisible = this.state.dialogVisible; - if (!pluginKey || !type || !Comp) return null; - var node = React.createElement(Comp, _extends({ + if (!pluginKey || !type) return null; + var node = Comp ? React.createElement(Comp, _extends({ + editor: editor, active: active, locked: locked, disabled: disabled, @@ -149,7 +161,7 @@ var TopPlugin = /*#__PURE__*/function (_PureComponent) { onClick: function onClick() { _onClick2 && _onClick2.call(null, editor); } - }, pluginProps)); + }, pluginProps)) : null; switch (type) { case 'LinkIcon': @@ -175,7 +187,10 @@ var TopPlugin = /*#__PURE__*/function (_PureComponent) { }, onCancel: this.handleClose, onClose: this.handleClose, - title: title + title: title, + style: _extends({ + width: 500 + }, props.dialogProps && props.dialogProps.style) }, props.dialogProps, { visible: dialogVisible }), node)); @@ -189,7 +204,7 @@ var TopPlugin = /*#__PURE__*/function (_PureComponent) { }, props.balloonProps), node); case 'Custom': - return dotted ? React.createElement(_Badge, { + return marked ? React.createElement(_Badge, { dot: true }, node) : node; @@ -201,12 +216,12 @@ var TopPlugin = /*#__PURE__*/function (_PureComponent) { return TopPlugin; }(PureComponent); -TopPlugin.displayName = 'lowcodeTopPlugin'; +TopPlugin.displayName = 'LowcodeTopPlugin'; TopPlugin.defaultProps = { active: false, config: {}, disabled: false, - dotted: false, + marked: false, locked: false, onClick: function onClick() {} }; diff --git a/packages/editor-skeleton/es/config/skeleton.d.ts b/packages/editor-skeleton/es/config/skeleton.d.ts index 67ae8a0ae..d87c48512 100644 --- a/packages/editor-skeleton/es/config/skeleton.d.ts +++ b/packages/editor-skeleton/es/config/skeleton.d.ts @@ -1,14 +1,2 @@ -declare const routerConfig: { - path: string; - component: any; - children: ({ - path: string; - component: any; - redirect?: undefined; - } | { - path: string; - redirect: string; - component?: undefined; - })[]; -}[]; -export default routerConfig; +declare const _default: {}; +export default _default; diff --git a/packages/editor-skeleton/es/config/skeleton.js b/packages/editor-skeleton/es/config/skeleton.js index 8fb0727ab..7c645e42f 100644 --- a/packages/editor-skeleton/es/config/skeleton.js +++ b/packages/editor-skeleton/es/config/skeleton.js @@ -1,14 +1 @@ -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 +export default {}; \ 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 index 5e5bda5ab..d87c48512 100644 --- a/packages/editor-skeleton/es/config/utils.d.ts +++ b/packages/editor-skeleton/es/config/utils.d.ts @@ -1,2 +1,2 @@ -declare const asideMenuConfig: any[]; -export { asideMenuConfig }; +declare const _default: {}; +export default _default; diff --git a/packages/editor-skeleton/es/config/utils.js b/packages/editor-skeleton/es/config/utils.js index 39cee308b..7c645e42f 100644 --- a/packages/editor-skeleton/es/config/utils.js +++ b/packages/editor-skeleton/es/config/utils.js @@ -1,3 +1 @@ -// 菜单配置 -var asideMenuConfig = []; -export { asideMenuConfig }; \ No newline at end of file +export default {}; \ No newline at end of file diff --git a/packages/editor-skeleton/es/global.scss b/packages/editor-skeleton/es/global.scss index 0a710b895..72f7b8525 100644 --- a/packages/editor-skeleton/es/global.scss +++ b/packages/editor-skeleton/es/global.scss @@ -1,13 +1,14 @@ 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; padding: 0; margin: 0; * { box-sizing: border-box; } + color: $color-text1-3; } + .next-loading { .next-loading-wrap { height: 100%; @@ -16,18 +17,17 @@ body { .lowcode-editor { .lowcode-main-content { position: absolute; - top: 48px; + top: 50px; left: 0; right: 0; bottom: 0; display: flex; - background-color: #d8d8d8; + background-color: rgba(31, 56, 88, 0.06); } .lowcode-center-area { flex: 1; display: flex; flex-direction: column; - padding: 10px; overflow: auto; } } diff --git a/packages/editor-skeleton/es/index.d.ts b/packages/editor-skeleton/es/index.d.ts index 468afa29c..66a91524c 100644 --- a/packages/editor-skeleton/es/index.d.ts +++ b/packages/editor-skeleton/es/index.d.ts @@ -1,8 +1,42 @@ -import { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; +import Editor from '@ali/lowcode-editor-framework'; +import { EditorConfig, Utils, PluginClassSet } from '@ali/lowcode-editor-framework/lib/definitions'; import './global.scss'; -export default class Skeleton extends PureComponent { +declare global { + interface Window { + __ctx: { + editor: Editor; + appHelper: Editor; + }; + } +} +export interface SkeletonProps { + components: PluginClassSet; + config: EditorConfig; + history: object; + location: object; + match: object; + utils: Utils; +} +export interface SkeletonState { + initReady?: boolean; + skeletonKey?: string; + __hasError?: boolean; +} +export declare class Skeleton extends PureComponent { static displayName: string; + static getDerivedStateFromError(): SkeletonState; + private editor; constructor(props: any); componentWillUnmount(): void; - render(): JSX.Element; + componentDidCatch(err: any): void; + init: (isReset?: boolean) => void; + render(): React.ReactNode; } +export interface SkeletonWithRouterProps { + components: PluginClassSet; + config: EditorConfig; + utils: Utils; +} +declare const SkeletonWithRouter: React.FC; +export default SkeletonWithRouter; diff --git a/packages/editor-skeleton/es/index.js b/packages/editor-skeleton/es/index.js index f875f6604..c853d170a 100644 --- a/packages/editor-skeleton/es/index.js +++ b/packages/editor-skeleton/es/index.js @@ -1,53 +1,125 @@ +import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/objectWithoutPropertiesLoose"; import _ConfigProvider from "@alifd/next/es/config-provider"; import _Loading from "@alifd/next/es/loading"; +import _extends from "@babel/runtime/helpers/extends"; import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; -import React, { PureComponent } from 'react'; // import Editor from '@ali/lowcode-engine-editor'; - +import React, { PureComponent } from 'react'; +import { HashRouter as Router, Route } from 'react-router-dom'; +import Editor, { utils } from '@ali/lowcode-editor-framework'; +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'; - -var Skeleton = /*#__PURE__*/function (_PureComponent) { +var comboEditorConfig = utils.comboEditorConfig, + parseSearch = utils.parseSearch; +var renderIdx = 0; +export var Skeleton = /*#__PURE__*/function (_PureComponent) { _inheritsLoose(Skeleton, _PureComponent); + Skeleton.getDerivedStateFromError = function getDerivedStateFromError() { + return { + __hasError: true + }; + }; + function Skeleton(props) { var _this; - _this = _PureComponent.call(this, props) || this; // this.editor = new Editor(props.config, props.utils); + _this = _PureComponent.call(this, props) || this; + _this.editor = void 0; - _this.editor = { - on: function on() {}, - off: function off() {}, - config: props.config, - pluginComponents: props.pluginComponents + _this.init = function (isReset) { + if (isReset === void 0) { + isReset = false; + } + + if (_this.editor) { + _this.editor.destroy(); + } + + var _this$props = _this.props, + utils = _this$props.utils, + config = _this$props.config, + components = _this$props.components; + var editor = new Editor(comboEditorConfig(defaultConfig, config), components, _extends({}, skeletonUtils, {}, utils)); + _this.editor = editor; // eslint-disable-next-line no-underscore-dangle + + window.__ctx = { + editor: editor, + appHelper: editor + }; + editor.once('editor.reset', function () { + _this.setState({ + initReady: false + }); + + editor.emit('editor.beforeReset'); + + _this.init(true); + }); + + _this.editor.init().then(function () { + _this.setState({ + initReady: true, + // 刷新IDE时生成新的skeletonKey保证插件生命周期重新执行 + skeletonKey: isReset ? "skeleton" + ++renderIdx : _this.state.skeletonKey + }, function () { + editor.emit('editor.ready'); + editor.emit('ide.ready'); + isReset && editor.emit('ide.afterReset'); + }); + }); }; + + _this.state = { + initReady: false, + skeletonKey: "skeleton" + renderIdx + }; + + _this.init(); + return _this; } var _proto = Skeleton.prototype; - _proto.componentWillUnmount = function componentWillUnmount() {// this.editor && this.editor.destroy(); - // this.editor = null; + _proto.componentWillUnmount = function componentWillUnmount() { + this.editor && this.editor.destroy(); + }; + + _proto.componentDidCatch = function componentDidCatch(err) { + console.error(err); }; _proto.render = function render() { - var _this$props = this.props, - location = _this$props.location, - history = _this$props.history, - messages = _this$props.messages; - this.editor.location = location; - this.editor.history = history; - this.editor.messages = messages; + var _this$state = this.state, + initReady = _this$state.initReady, + skeletonKey = _this$state.skeletonKey, + __hasError = _this$state.__hasError; + var _this$props2 = this.props, + location = _this$props2.location, + history = _this$props2.history, + match = _this$props2.match; + + if (__hasError || !this.editor) { + return 'error'; + } + + location.query = parseSearch(location.search); + this.editor.set('location', location); + this.editor.set('history', history); + this.editor.set('match', match); return React.createElement(_ConfigProvider, null, React.createElement(_Loading, { tip: "Loading", size: "large", - visible: false, - shape: "fusion-reactor", + visible: !initReady, fullScreen: true }, React.createElement("div", { - className: "lowcode-editor" + className: "lowcode-editor", + key: skeletonKey }, React.createElement(TopArea, { editor: this.editor }), React.createElement("div", { @@ -64,7 +136,23 @@ var Skeleton = /*#__PURE__*/function (_PureComponent) { }; return Skeleton; -}(PureComponent); +}(PureComponent); // 通过React-Router包裹,支持编辑器内页面根据路由切换 -Skeleton.displayName = 'lowcodeEditorSkeleton'; -export { Skeleton as default }; \ No newline at end of file +Skeleton.displayName = 'LowcodeEditorSkeleton'; +; + +var SkeletonWithRouter = function SkeletonWithRouter(props) { + var config = props.config, + otherProps = _objectWithoutPropertiesLoose(props, ["config"]); + + return React.createElement(Router, null, React.createElement(Route, { + path: "/*", + component: function component(routerProps) { + return React.createElement(Skeleton, _extends({}, routerProps, otherProps, config.skeleton && config.skeleton.props, { + config: config + })); + } + })); +}; + +export default SkeletonWithRouter; \ 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 index 201db137a..17fc0df75 100644 --- a/packages/editor-skeleton/es/layouts/CenterArea/index.d.ts +++ b/packages/editor-skeleton/es/layouts/CenterArea/index.d.ts @@ -1,7 +1,16 @@ -import { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; +import Editor from '@ali/lowcode-editor-framework'; import './index.scss'; -export default class CenterArea extends PureComponent { - static displayName: string; - constructor(props: any); - render(): JSX.Element; +export interface CenterAreaProps { + editor: Editor; +} +export default class CenterArea extends PureComponent { + static displayName: string; + private editor; + private areaManager; + constructor(props: any); + componentDidMount(): void; + componentWillUnmount(): void; + handleSkeletonUpdate: () => void; + render(): React.ReactNode; } diff --git a/packages/editor-skeleton/es/layouts/CenterArea/index.js b/packages/editor-skeleton/es/layouts/CenterArea/index.js index 4b9710f81..bc7c3f2c1 100644 --- a/packages/editor-skeleton/es/layouts/CenterArea/index.js +++ b/packages/editor-skeleton/es/layouts/CenterArea/index.js @@ -1,24 +1,64 @@ +import _extends from "@babel/runtime/helpers/extends"; import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; import React, { PureComponent } from 'react'; +import { AreaManager } from '@ali/lowcode-editor-framework'; import './index.scss'; var CenterArea = /*#__PURE__*/function (_PureComponent) { _inheritsLoose(CenterArea, _PureComponent); function CenterArea(props) { - return _PureComponent.call(this, props) || this; + var _this; + + _this = _PureComponent.call(this, props) || this; + _this.editor = void 0; + _this.areaManager = void 0; + + _this.handleSkeletonUpdate = function () { + // 当前区域插件状态改变是更新区域 + if (_this.areaManager.isPluginStatusUpdate()) { + _this.forceUpdate(); + } + }; + + _this.editor = props.editor; + _this.areaManager = new AreaManager(_this.editor, 'centerArea'); + return _this; } var _proto = CenterArea.prototype; + _proto.componentDidMount = function componentDidMount() { + this.editor.on('skeleton.update', this.handleSkeletonUpdate); + }; + + _proto.componentWillUnmount = function componentWillUnmount() { + this.editor.off('skeleton.update', this.handleSkeletonUpdate); + }; + _proto.render = function render() { + var _this2 = this; + + var visiblePluginList = this.areaManager.getVisiblePluginList(); return React.createElement("div", { className: "lowcode-center-area" - }); + }, visiblePluginList.map(function (item) { + var Comp = _this2.areaManager.getPluginClass(item.pluginKey); + + if (Comp) { + return React.createElement(Comp, _extends({ + key: item.pluginKey, + editor: _this2.editor, + config: item + }, item.pluginProps)); + } + + return null; + })); }; return CenterArea; }(PureComponent); -CenterArea.displayName = 'lowcodeCenterArea'; +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 index b2584ed2b..40a1806a6 100644 --- a/packages/editor-skeleton/es/layouts/CenterArea/index.scss +++ b/packages/editor-skeleton/es/layouts/CenterArea/index.scss @@ -1,3 +1,3 @@ .lowcode-center-area { - padding: 12px; + padding: 0; } diff --git a/packages/editor-skeleton/es/layouts/LeftArea/index.scss b/packages/editor-skeleton/es/layouts/LeftArea/index.scss index dac1b6b0a..8c69f42fc 100644 --- a/packages/editor-skeleton/es/layouts/LeftArea/index.scss +++ b/packages/editor-skeleton/es/layouts/LeftArea/index.scss @@ -1,21 +1,23 @@ .lowcode-left-area-nav { - width: 48px; + width: 50px; height: 100%; - background: #ffffff; - border-right: 1px solid #e8ebee; + background-color: $card-background; + border-right: 2px solid $color-line1-1; position: relative; .top-area { position: absolute; top: 0; width: 100%; - background: #ffffff; + padding: 12px 0; + background-color: $card-background; max-height: 100%; } .bottom-area { position: absolute; - bottom: 20px; + bottom: 0; width: 100%; - background: #ffffff; + padding: 12px 0; + background-color: $card-background; max-height: calc(100% - 20px); } } diff --git a/packages/editor-skeleton/es/layouts/LeftArea/nav.d.ts b/packages/editor-skeleton/es/layouts/LeftArea/nav.d.ts index c44a09527..e12b41c0e 100644 --- a/packages/editor-skeleton/es/layouts/LeftArea/nav.d.ts +++ b/packages/editor-skeleton/es/layouts/LeftArea/nav.d.ts @@ -1,7 +1,24 @@ -import { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; +import Editor from '@ali/lowcode-editor-framework'; +import { PluginConfig } from '@ali/lowcode-editor-framework/lib/definitions'; import './index.scss'; -export default class LeftAreaPanel extends PureComponent { - static displayName: string; - constructor(props: any); - render(): JSX.Element; +export interface LeftAreaNavProps { + editor: Editor; +} +export interface LeftAreaNavState { + activeKey: string; +} +export default class LeftAreaNav extends PureComponent { + static displayName: string; + private editor; + private areaManager; + constructor(props: any); + componentDidMount(): void; + componentWillUnmount(): void; + handleSkeletonUpdate: () => void; + handlePluginChange: (key: string) => void; + handlePluginClick: (item: PluginConfig) => void; + updateActiveKey: (key: string) => void; + renderPluginList: (list?: PluginConfig[]) => any[]; + render(): React.ReactNode; } diff --git a/packages/editor-skeleton/es/layouts/LeftArea/nav.js b/packages/editor-skeleton/es/layouts/LeftArea/nav.js index 962129007..5401ced91 100644 --- a/packages/editor-skeleton/es/layouts/LeftArea/nav.js +++ b/packages/editor-skeleton/es/layouts/LeftArea/nav.js @@ -1,24 +1,150 @@ +import _extends from "@babel/runtime/helpers/extends"; import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; import React, { PureComponent } from 'react'; +import LeftPlugin from '../../components/LeftPlugin'; +import { utils, AreaManager } from '@ali/lowcode-editor-framework'; import './index.scss'; +var isEmpty = utils.isEmpty; -var LeftAreaPanel = /*#__PURE__*/function (_PureComponent) { - _inheritsLoose(LeftAreaPanel, _PureComponent); +var LeftAreaNav = /*#__PURE__*/function (_PureComponent) { + _inheritsLoose(LeftAreaNav, _PureComponent); - function LeftAreaPanel(props) { - return _PureComponent.call(this, props) || this; + function LeftAreaNav(props) { + var _this; + + _this = _PureComponent.call(this, props) || this; + _this.editor = void 0; + _this.areaManager = void 0; + + _this.handleSkeletonUpdate = function () { + // 当前区域插件状态改变是更新区域 + if (_this.areaManager.isPluginStatusUpdate()) { + _this.forceUpdate(); + } + }; + + _this.handlePluginChange = function (key) { + var activeKey = _this.state.activeKey; + var plugins = _this.editor.plugins; + var prePlugin = plugins[activeKey]; + var nextPlugin = plugins[key]; + + if (activeKey === 'none') { + if (nextPlugin) { + nextPlugin.open().then(function () { + _this.updateActiveKey(key); + }); + } + } else if (activeKey === key) { + if (prePlugin) { + prePlugin.close().then(function () { + _this.updateActiveKey('none'); + }); + } + } else if (prePlugin) { + // 先关后开 + prePlugin.close().then(function () { + if (nextPlugin) { + nextPlugin.open().then(function () { + _this.updateActiveKey(key); + }); + } + }); + } + }; + + _this.handlePluginClick = function (item) { + if (item.type === 'PanelIcon') { + _this.handlePluginChange(item.pluginKey); + } + }; + + _this.updateActiveKey = function (key) { + _this.editor.set('leftNav', key); + + _this.setState({ + activeKey: key + }); + + _this.editor.emit('leftPanel.show', key); + }; + + _this.renderPluginList = function (list) { + if (list === void 0) { + list = []; + } + + var activeKey = _this.state.activeKey; + return list.map(function (item) { + var pluginStatus = _this.areaManager.getPluginStatus(item.pluginKey); + + var pluginClass = _this.areaManager.getPluginClass(item.pluginKey); + + return React.createElement(LeftPlugin, _extends({ + key: item.pluginKey, + config: item, + editor: _this.editor, + pluginClass: pluginClass, + onClick: function onClick() { + return _this.handlePluginClick(item); + }, + active: activeKey === item.pluginKey + }, pluginStatus)); + }); + }; + + _this.editor = props.editor; + _this.areaManager = new AreaManager(_this.editor, 'leftArea'); + _this.state = { + activeKey: 'none' + }; + return _this; } - var _proto = LeftAreaPanel.prototype; + var _proto = LeftAreaNav.prototype; - _proto.render = function render() { - return React.createElement("div", { - className: "lowcode-left-area-nav" - }); + _proto.componentDidMount = function componentDidMount() { + this.editor.on('skeleton.update', this.handleSkeletonUpdate); + this.editor.on('leftNav.change', this.handlePluginChange); + var visiblePanelPluginList = this.areaManager.getVisiblePluginList('IconPanel'); + var defaultKey = visiblePanelPluginList[0] && visiblePanelPluginList[0].pluginKey || 'componentAttr'; + this.handlePluginChange(defaultKey); }; - return LeftAreaPanel; + _proto.componentWillUnmount = function componentWillUnmount() { + this.editor.off('skeleton.update', this.handleSkeletonUpdate); + this.editor.off('leftNav.change', this.handlePluginChange); + }; + + _proto.render = function render() { + var topList = []; + var bottomList = []; + var visiblePluginList = this.areaManager.getVisiblePluginList(); + + if (isEmpty(visiblePluginList)) { + return null; + } + + visiblePluginList.forEach(function (item) { + var align = item.props && item.props.align === 'bottom' ? 'bottom' : 'top'; + + if (align === 'bottom') { + bottomList.push(item); + } else { + topList.push(item); + } + }); + return React.createElement("div", { + className: "lowcode-left-area-nav" + }, React.createElement("div", { + className: "bottom-area" + }, this.renderPluginList(bottomList)), React.createElement("div", { + className: "top-area" + }, this.renderPluginList(topList))); + }; + + return LeftAreaNav; }(PureComponent); -LeftAreaPanel.displayName = 'lowcodeLeftAreaNav'; -export { LeftAreaPanel as default }; \ No newline at end of file +LeftAreaNav.displayName = 'LowcodeLeftAreaNav'; +export { LeftAreaNav 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 index c44a09527..c9320350c 100644 --- a/packages/editor-skeleton/es/layouts/LeftArea/panel.d.ts +++ b/packages/editor-skeleton/es/layouts/LeftArea/panel.d.ts @@ -1,7 +1,20 @@ -import { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; +import Editor from '@ali/lowcode-editor-framework'; import './index.scss'; -export default class LeftAreaPanel extends PureComponent { - static displayName: string; - constructor(props: any); - render(): JSX.Element; +export interface LeftAreaPanelProps { + editor: Editor; +} +export interface LeftAreaPanelState { + activeKey: string; +} +export default class LeftAreaPanel extends PureComponent { + static displayName: string; + private editor; + private areaManager; + constructor(props: any); + componentDidMount(): void; + componentWillUnmount(): void; + handleSkeletonUpdate: () => void; + handlePluginChange: (key: string) => void; + render(): React.ReactNode; } diff --git a/packages/editor-skeleton/es/layouts/LeftArea/panel.js b/packages/editor-skeleton/es/layouts/LeftArea/panel.js index 332529ce2..2104ab096 100644 --- a/packages/editor-skeleton/es/layouts/LeftArea/panel.js +++ b/packages/editor-skeleton/es/layouts/LeftArea/panel.js @@ -1,24 +1,77 @@ +import _extends from "@babel/runtime/helpers/extends"; import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; -import React, { PureComponent } from 'react'; +import React, { PureComponent, Fragment } from 'react'; +import { AreaManager } from '@ali/lowcode-editor-framework'; +import Panel from '../../components/Panel'; import './index.scss'; var LeftAreaPanel = /*#__PURE__*/function (_PureComponent) { _inheritsLoose(LeftAreaPanel, _PureComponent); function LeftAreaPanel(props) { - return _PureComponent.call(this, props) || this; + var _this; + + _this = _PureComponent.call(this, props) || this; + _this.editor = void 0; + _this.areaManager = void 0; + + _this.handleSkeletonUpdate = function () { + // 当前区域插件状态改变是更新区域 + if (_this.areaManager.isPluginStatusUpdate('PanelIcon')) { + _this.forceUpdate(); + } + }; + + _this.handlePluginChange = function (key) { + _this.setState({ + activeKey: key + }); + }; + + _this.editor = props.editor; + _this.areaManager = new AreaManager(_this.editor, 'leftArea'); + _this.state = { + activeKey: 'none' + }; + return _this; } var _proto = LeftAreaPanel.prototype; + _proto.componentDidMount = function componentDidMount() { + this.editor.on('skeleton.update', this.handleSkeletonUpdate); + this.editor.on('leftPanel.show', this.handlePluginChange); + }; + + _proto.componentWillUnmount = function componentWillUnmount() { + this.editor.off('skeleton.update', this.handleSkeletonUpdate); + this.editor.off('leftPanel.show', this.handlePluginChange); + }; + _proto.render = function render() { - return React.createElement("div", { - className: "lowcode-left-area-panel" - }); + var _this2 = this; + + var activeKey = this.state.activeKey; + var list = this.areaManager.getVisiblePluginList('PanelIcon'); + return React.createElement(Fragment, null, list.map(function (item) { + var Comp = _this2.areaManager.getPluginClass(item.pluginKey); + + if (Comp) { + return React.createElement(Panel, _extends({ + key: item.pluginKey, + visible: item.pluginKey === activeKey + }, item.props && item.props.panelProps), React.createElement(Comp, _extends({ + editor: _this2.editor, + config: item + }, item.pluginProps))); + } + + return null; + })); }; return LeftAreaPanel; }(PureComponent); -LeftAreaPanel.displayName = 'lowcodeLeftAreaPanel'; +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 index ad87a57de..8ecf5b466 100644 --- a/packages/editor-skeleton/es/layouts/RightArea/index.d.ts +++ b/packages/editor-skeleton/es/layouts/RightArea/index.d.ts @@ -1,7 +1,24 @@ -import { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; +import Editor from '@ali/lowcode-editor-framework'; +import { PluginConfig } from '@ali/lowcode-editor-framework/lib/definitions'; import './index.scss'; -export default class RightArea extends PureComponent { - static displayName: string; - constructor(props: any); - render(): JSX.Element; +export interface RightAreaProps { + editor: Editor; +} +export interface RightAreaState { + activeKey: string; +} +export default class RightArea extends PureComponent { + static displayName: string; + private editor; + private areaManager; + constructor(props: any); + componentDidMount(): void; + componentWillUnmount(): void; + handleSkeletonUpdate: () => void; + handlePluginChange: (key: string, isinit?: boolean) => void; + renderTabTitle: (config: PluginConfig) => any; + renderTabPanels: (list: PluginConfig[], height: string) => any; + renderPanels: (list: PluginConfig[], height: string) => any; + render(): React.ReactNode; } diff --git a/packages/editor-skeleton/es/layouts/RightArea/index.js b/packages/editor-skeleton/es/layouts/RightArea/index.js index d1d38689d..f66672d48 100644 --- a/packages/editor-skeleton/es/layouts/RightArea/index.js +++ b/packages/editor-skeleton/es/layouts/RightArea/index.js @@ -1,24 +1,218 @@ +import _Tab from "@alifd/next/es/tab"; +import _extends from "@babel/runtime/helpers/extends"; +import _Badge from "@alifd/next/es/badge"; +import _Icon from "@alifd/next/es/icon"; import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; import React, { PureComponent } from 'react'; +import classNames from 'classnames'; +import { AreaManager, utils } from '@ali/lowcode-editor-framework'; import './index.scss'; +var isEmpty = utils.isEmpty; var RightArea = /*#__PURE__*/function (_PureComponent) { _inheritsLoose(RightArea, _PureComponent); function RightArea(props) { - return _PureComponent.call(this, props) || this; + var _this; + + _this = _PureComponent.call(this, props) || this; + _this.editor = void 0; + _this.areaManager = void 0; + + _this.handleSkeletonUpdate = function () { + // 当前区域插件状态改变是更新区域 + if (_this.areaManager.isPluginStatusUpdate()) { + var activeKey = _this.state.activeKey; + + var activePluginStatus = _this.areaManager.getPluginStatus(activeKey); + + if (activePluginStatus && activePluginStatus.visible) { + _this.forceUpdate(); + } else { + var currentPlugin = _this.areaManager.getPlugin(activeKey); + + if (currentPlugin) { + currentPlugin.close().then(function () { + _this.setState({ + activeKey: '' + }, function () { + var visiblePluginList = _this.areaManager.getVisiblePluginList('TabPanel'); + + var firstPlugin = visiblePluginList && visiblePluginList[0]; + + if (firstPlugin) { + _this.handlePluginChange(firstPlugin.pluginKey); + } + }); + }); + } + } + } + }; + + _this.handlePluginChange = function (key, isinit) { + var activeKey = _this.state.activeKey; + + var currentPlugin = _this.areaManager.getPlugin(activeKey); + + var nextPlugin = _this.areaManager.getPlugin(key); + + var openPlugin = function openPlugin() { + if (!nextPlugin) { + console.error("plugin " + key + " has not regist in the editor"); + return; + } + + nextPlugin.open().then(function () { + _this.editor.set('rightNav', key); + + _this.setState({ + activeKey: key + }); + }); + }; + + if (key === activeKey && !isinit) return; + + if (currentPlugin) { + currentPlugin.close().then(function () { + openPlugin(); + }); + } else { + openPlugin(); + } + }; + + _this.renderTabTitle = function (config) { + var _ref = config.props || {}, + icon = _ref.icon, + title = _ref.title; + + var pluginStatus = _this.editor.pluginStatus[config.pluginKey]; + var marked = pluginStatus.marked, + disabled = pluginStatus.disabled, + locked = pluginStatus.locked; + var active = _this.state.activeKey === config.pluginKey; + + var renderTitle = function renderTitle() { + return React.createElement("div", { + className: classNames('right-plugin-title', { + active: active, + locked: locked, + disabled: disabled + }) + }, !!icon && React.createElement(_Icon, { + size: "xs", + type: icon + }), title); + }; + + if (marked) { + return React.createElement(_Badge, { + dot: true + }, renderTitle()); + } + + return renderTitle(); + }; + + _this.renderTabPanels = function (list, height) { + if (isEmpty(list)) { + return null; + } + + return React.createElement(_Tab, { + className: "right-tabs", + style: { + height: height + }, + activeKey: _this.state.activeKey, + lazyLoad: false, + onChange: _this.handlePluginChange + }, list.map(function (item) { + var Comp = _this.areaManager.getPluginClass(item.pluginKey); + + if (Comp) { + return React.createElement(_Tab.Item, { + key: item.pluginKey, + title: _this.renderTabTitle(item), + disabled: _this.editor.pluginStatus[item.pluginKey].disabled, + style: { + width: 100 / list.length + "%" + } + }, React.createElement(Comp, _extends({ + editor: _this.editor, + config: item + }, item.pluginProps))); + } + + return null; + })); + }; + + _this.renderPanels = function (list, height) { + return list.map(function (item) { + var Comp = _this.areaManager.getPluginClass(item.pluginKey); + + if (Comp) { + return React.createElement("div", { + className: "right-panel", + style: { + height: height + }, + key: item.pluginKey + }, React.createElement(Comp, _extends({ + editor: _this.editor, + config: item + }, item.pluginProps))); + } + + return null; + }); + }; + + _this.editor = props.editor; + _this.areaManager = new AreaManager(_this.editor, 'rightArea'); + _this.state = { + activeKey: '' + }; + return _this; } var _proto = RightArea.prototype; + _proto.componentDidMount = function componentDidMount() { + this.editor.on('skeleton.update', this.handleSkeletonUpdate); + this.editor.on('rightNav.change', this.handlePluginChange); + var visiblePluginList = this.areaManager.getVisiblePluginList('TabPanel'); + var defaultKey = visiblePluginList[0] && visiblePluginList[0].pluginKey || 'componentAttr'; + this.handlePluginChange(defaultKey, true); + }; + + _proto.componentWillUnmount = function componentWillUnmount() { + this.editor.off('skeleton.update', this.handleSkeletonUpdate); + this.editor.off('rightNav.change', this.handlePluginChange); + }; + _proto.render = function render() { + var tabList = this.areaManager.getVisiblePluginList('TabPanel'); + var panelList = this.areaManager.getVisiblePluginList('Panel'); + + if (isEmpty(panelList) && isEmpty(tabList)) { + return null; + } else if (tabList.length === 1) { + panelList.unshift(tabList[0]); + tabList.splice(0, 1); + } + + var height = Math.floor(100 / (panelList.length + (tabList.length > 0 ? 1 : 0))) + "%"; return React.createElement("div", { className: "lowcode-right-area" - }); + }, this.renderTabPanels(tabList, height), this.renderPanels(panelList, height)); }; return RightArea; }(PureComponent); -RightArea.displayName = 'lowcodeRightArea'; +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 index 120ef4f11..7373933b0 100644 --- a/packages/editor-skeleton/es/layouts/RightArea/index.scss +++ b/packages/editor-skeleton/es/layouts/RightArea/index.scss @@ -1,155 +1,37 @@ .lowcode-right-area { - width: 300px; + width: 262px; 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; - } + background-color: $card-background; + border-left: 2px solid $color-line1-1; + + .right-panel { + overflow: auto; + // border-top: 2px solid $color-line1-1; } //tab定义 - .next-tabs-wrapped.right-tabs { - display: flex; - flex-direction: column; - margin-top: -1px; - .next-tabs-bar { - z-index: 1; - } + .right-tabs.next-tabs { .next-tabs-nav { - display: block; - .next-tabs-tab { - &:first-child { - border-left: none; - } - font-size: 14px; + width: 100%; + .next-tabs-tab-inner { + padding-left: 0; + padding-right: 0; + } + .right-plugin-title { text-align: center; - border-right: none !important; - margin-right: 0 !important; - width: 25%; + &.locked { + color: red !important; + } &.active { - background: none; - border-bottom-color: #f7f7f7 !important; + color: $color-brand1-9 !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; + &.disabled { + cursor: not-allowed; + color: $color-text1-1; } - } - .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; + .next-icon { + line-height: 15px; + margin-right: 2px; } } } diff --git a/packages/editor-skeleton/es/layouts/TopArea/index.d.ts b/packages/editor-skeleton/es/layouts/TopArea/index.d.ts index e800a1e74..658aea93d 100644 --- a/packages/editor-skeleton/es/layouts/TopArea/index.d.ts +++ b/packages/editor-skeleton/es/layouts/TopArea/index.d.ts @@ -1,11 +1,18 @@ -import { PureComponent } from 'react'; +import React, { PureComponent } from 'react'; +import Editor from '@ali/lowcode-editor-framework'; +import { PluginConfig } from '@ali/lowcode-editor-framework/lib/definitions'; import './index.scss'; -export default class TopArea extends PureComponent { +export interface TopAreaProps { + editor: Editor; +} +export default class TopArea extends PureComponent { static displayName: string; + private areaManager; + private editor; constructor(props: any); componentDidMount(): void; componentWillUnmount(): void; - handlePluginStatusChange: () => void; - renderPluginList: (list?: any[]) => JSX.Element[]; - render(): JSX.Element; + handleSkeletonUpdate: () => void; + renderPluginList: (list?: PluginConfig[]) => any[]; + render(): React.ReactNode; } diff --git a/packages/editor-skeleton/es/layouts/TopArea/index.js b/packages/editor-skeleton/es/layouts/TopArea/index.js index c82eefc61..edc1e793a 100644 --- a/packages/editor-skeleton/es/layouts/TopArea/index.js +++ b/packages/editor-skeleton/es/layouts/TopArea/index.js @@ -1,6 +1,7 @@ import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose"; import _Grid from "@alifd/next/es/grid"; import React, { PureComponent } from 'react'; +import { AreaManager } from '@ali/lowcode-editor-framework'; import TopPlugin from '../../components/TopPlugin'; import './index.scss'; var Row = _Grid.Row, @@ -13,8 +14,15 @@ var TopArea = /*#__PURE__*/function (_PureComponent) { var _this; _this = _PureComponent.call(this, props) || this; + _this.areaManager = void 0; + _this.editor = void 0; - _this.handlePluginStatusChange = function () {}; + _this.handleSkeletonUpdate = function () { + // 当前区域插件状态改变是更新区域 + if (_this.areaManager.isPluginStatusUpdate()) { + _this.forceUpdate(); + } + }; _this.renderPluginList = function (list) { if (list === void 0) { @@ -23,41 +31,48 @@ var TopArea = /*#__PURE__*/function (_PureComponent) { return list.map(function (item, idx) { var isDivider = item.type === 'Divider'; + + var PluginClass = _this.areaManager.getPluginClass(item.pluginKey); + return React.createElement(Col, { className: isDivider ? 'divider' : '', key: isDivider ? idx : item.pluginKey, style: { - width: item.props && item.props.width || 40, + width: item.props && item.props.width || 36, flex: 'none' } }, !isDivider && React.createElement(TopPlugin, { config: item, - pluginClass: _this.editor.pluginComponents[item.pluginKey], - status: _this.editor.pluginStatus[item.pluginKey] + pluginClass: PluginClass, + editor: _this.editor })); }); }; _this.editor = props.editor; - _this.config = _this.editor.config.plugins && _this.editor.config.plugins.topArea; + _this.areaManager = new AreaManager(props.editor, 'topArea'); return _this; } var _proto = TopArea.prototype; - _proto.componentDidMount = function componentDidMount() {}; + _proto.componentDidMount = function componentDidMount() { + this.editor.on('skeleton.update', this.handleSkeletonUpdate); + }; - _proto.componentWillUnmount = function componentWillUnmount() {}; + _proto.componentWillUnmount = function componentWillUnmount() { + this.editor.off('skeleton.update', this.handleSkeletonUpdate); + }; _proto.render = function render() { - if (!this.config) return null; var leftList = []; var rightList = []; - this.config.forEach(function (item) { + var visiblePluginList = this.areaManager.getVisiblePluginList(); + visiblePluginList.forEach(function (item) { var align = item.props && item.props.align === 'right' ? 'right' : 'left'; // 分隔符不允许相邻 if (item.type === 'Divider') { - var currentList = align === 'right' ? rightList : leftList; + var currList = align === 'right' ? rightList : leftList; if (currList.length === 0 || currList[currList.length - 1].type === 'Divider') return; } @@ -71,13 +86,15 @@ var TopArea = /*#__PURE__*/function (_PureComponent) { className: "lowcode-top-area" }, React.createElement("div", { className: "left-area" - }, this.renderPluginList(leftList)), React.createElement("div", { - classname: "right-area" - }, this.renderPluginList(rightList))); + }, React.createElement(Row, null, this.renderPluginList(leftList))), React.createElement("div", { + className: "right-area" + }, React.createElement(Row, { + justify: "end" + }, this.renderPluginList(rightList)))); }; return TopArea; }(PureComponent); -TopArea.displayName = 'lowcodeTopArea'; +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 index ca8bbd825..c0a132f84 100644 --- a/packages/editor-skeleton/es/layouts/TopArea/index.scss +++ b/packages/editor-skeleton/es/layouts/TopArea/index.scss @@ -1,5 +1,30 @@ .lowcode-top-area { - height: 48px; - background-color: #ffffff; - border-bottom: 1px solid #e8ebee; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 50px; + background-color: $card-background; + border-bottom: 2px solid $color-line1-1; + user-select: none; + .divider { + max-width: 0; + margin: 12px 16px; + height: 24px; + border-right: 1px solid $color-line1-2; + } + .next-col { + text-align: center; + } + .left-area { + padding: 0 16px; + } + .right-area { + position: absolute; + right: 0; + top: 0; + padding: 0 16px; + height: 100%; + background: $card-background; + } } diff --git a/packages/editor-skeleton/es/locale/en-US.js b/packages/editor-skeleton/es/locale/en-US.js index f190625da..7c645e42f 100644 --- a/packages/editor-skeleton/es/locale/en-US.js +++ b/packages/editor-skeleton/es/locale/en-US.js @@ -1,10 +1 @@ -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 +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 index 51791f741..7c645e42f 100644 --- a/packages/editor-skeleton/es/locale/zh-CN.js +++ b/packages/editor-skeleton/es/locale/zh-CN.js @@ -1,10 +1 @@ -export default { - loading: '加载中...', - rejectRedirect: '开发中,已阻止发生跳转', - expand: '展开', - fold: '收起', - pageNotExist: '当前访问地址不存在', - enterFromAppCenter: '请从应用中心入口重新进入', - noPermission: '抱歉,您暂无开发权限', - getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限' -}; \ No newline at end of file +export default {}; \ No newline at end of file diff --git a/packages/editor-skeleton/es/style.js b/packages/editor-skeleton/es/style.js index 7f81d0018..36f598306 100644 --- a/packages/editor-skeleton/es/style.js +++ b/packages/editor-skeleton/es/style.js @@ -1,8 +1,9 @@ import '@alifd/next/es/config-provider/style'; import '@alifd/next/es/loading/style'; +import '@ali/lowcode-editor-framework/es/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 +import '@alifd/next/es/icon/style'; +import '@alifd/next/es/tab/style'; \ No newline at end of file diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json index 1320d2903..4fa9dfb69 100644 --- a/packages/editor-skeleton/package.json +++ b/packages/editor-skeleton/package.json @@ -1,5 +1,5 @@ { - "name": "@ali/lowcode-engine-skeleton", + "name": "@ali/lowcode-editor-skeleton", "version": "0.0.1", "description": "alibaba lowcode editor skeleton", "files": [ @@ -24,15 +24,13 @@ ], "author": "xiayang.xy", "dependencies": { + "@ali/lowcode-editor-framework": "0.0.1", "@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" + "react": "^16.8.1", + "react-dom": "^16.8.1", + "react-router-dom": "^5.1.2", + "store": "^2.0.12" }, "devDependencies": { "@alib/build-scripts": "^0.1.3", diff --git a/packages/editor-skeleton/src/components/LeftIcon/index.scss b/packages/editor-skeleton/src/components/LeftIcon/index.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/editor-skeleton/src/components/LeftIcon/index.tsx b/packages/editor-skeleton/src/components/LeftIcon/index.tsx deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/editor-skeleton/src/components/LeftPlugin/index.scss b/packages/editor-skeleton/src/components/LeftPlugin/index.scss index 9c6922129..37b79669b 100644 --- a/packages/editor-skeleton/src/components/LeftPlugin/index.scss +++ b/packages/editor-skeleton/src/components/LeftPlugin/index.scss @@ -1,59 +1,60 @@ -.luna-left-addon { - font-size: 16px; +.lowcode-left-plugin { + font-size: 20px; text-align: center; - line-height: 36px; - height: 36px; + line-height: 44px; + height: 44px; position: relative; cursor: pointer; transition: all 0.3s ease; - color: #777; - &.collapse { - height: 40px; - color: #8c8c8c; - border-bottom: 1px solid #bfbfbf; - } + color: $color-text1-3; &.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; + left: 45px; + top: 8px; line-height: 18px; font-size: 12px; white-space: nowrap; padding: 6px 8px; border-radius: 4px; - background: rgba(0, 0, 0, 0.75); - color: #fff; + background: $balloon-normal-color-bg; + border: 1px solid $balloon-normal-color-border; + opacity: 0.8; + color: $color-text1-3; z-index: 100; } &:after { content: ''; display: block; position: absolute; + width: 10px; + height: 10px; + transform: rotate(45deg); left: 40px; - top: 15px; - border: 5px solid transparent; - border-right-color: rgba(0, 0, 0, 0.75); + top: 18px; + background: $balloon-normal-color-bg; + border-left: 1px solid $balloon-normal-color-border; + border-bottom: 1px solid $balloon-normal-color-border; z-index: 100; } } + &.active { + color: $color-brand1-9; + &.disabled { + color: $color-text1-1; + } + &:hover { + color: $color-brand1-6; + } + } + &.disabled { + cursor: not-allowed; + color: $color-text1-1; + } } diff --git a/packages/editor-skeleton/src/components/LeftPlugin/index.tsx b/packages/editor-skeleton/src/components/LeftPlugin/index.tsx index d9a637a21..66b718226 100644 --- a/packages/editor-skeleton/src/components/LeftPlugin/index.tsx +++ b/packages/editor-skeleton/src/components/LeftPlugin/index.tsx @@ -1,79 +1,74 @@ 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 { Balloon, Dialog, Icon, Badge } from '@alifd/next'; +import Editor from '@ali/lowcode-editor-framework'; +import { + PluginConfig, + PluginClass, +} from '@ali/lowcode-editor-framework/lib/definitions'; 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, - }; + +export interface LeftPluginProps { + active?: boolean; + config: PluginConfig; + disabled?: boolean; + editor: Editor; + locked?: boolean; + marked?: boolean; + onClick?: () => void; + pluginClass: PluginClass | undefined; +} + +export interface LeftPluginState { + dialogVisible: boolean; +} + +export default class LeftPlugin extends PureComponent< + LeftPluginProps, + LeftPluginState +> { + static displayName = 'LowcodeLeftPlugin'; + static defaultProps = { active: false, config: {}, disabled: false, - dotted: false, + marked: false, locked: false, - onClick: () => {}, + onClick: (): void => {}, }; - 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); + componentDidMount(): void { + const { config, editor } = this.props; + const pluginKey = config && config.pluginKey; + if (editor && pluginKey) { + editor.on(`${pluginKey}.dialog.show`, this.handleShow); + editor.on(`${pluginKey}.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); + componentWillUnmount(): void { + const { config, editor } = this.props; + const pluginKey = config && config.pluginKey; + if (editor && pluginKey) { + editor.off(`${pluginKey}.dialog.show`, this.handleShow); + editor.off(`${pluginKey}.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(() => { + handleClose = (): void => { + const { config, editor } = this.props; + const pluginKey = config && config.pluginKey; + const plugin = editor.plugins && editor.plugins[pluginKey]; + if (plugin) { + plugin.close().then((): void => { this.setState({ dialogVisible: false, }); @@ -81,43 +76,48 @@ export default class LeftAddon extends PureComponent { } }; - handleOpen = () => { + handleOpen = (): void => { // 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); + handleShow = (): void => { + const { disabled, config, onClick, editor } = this.props; + const pluginKey = config && config.pluginKey; + if (disabled || !pluginKey) return; this.handleOpen(); - onClick && onClick(); + // 考虑到弹窗情况,延时发送消息 + setTimeout((): void => { + editor.emit(`${pluginKey}.plugin.activate`); + }, 0); + if (onClick) { + onClick(); + } }; - renderIcon = clickCallback => { - const { active, disabled, dotted, locked, onClick, config } = this.props; - const { addonKey, props } = config || {}; + renderIcon = (clickCallback): React.ReactNode => { + const { active, disabled, marked, locked, onClick, config } = this.props; + const { pluginKey, props } = config || {}; const { icon, title } = props || {}; return (
{ + onClick={(): void => { if (disabled) return; - //考虑到弹窗情况,延时发送消息 + // 考虑到弹窗情况,延时发送消息 clickCallback && clickCallback(); + onClick && onClick(); }} > - {dotted ? ( + {marked ? ( @@ -128,67 +128,66 @@ export default class LeftAddon extends PureComponent { ); }; - render() { - const { dotted, locked, active, disabled, config } = this.props; - const { addonKey, props, type, addonProps } = config || {}; + render(): React.ReactNode { + 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; - 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; + 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, appHelper); + {this.renderIcon((): void => { + onClick && onClick.call(null, editor); })} ); case 'Icon': - return this.renderIcon(() => { - onClick && onClick.call(null, appHelper); + return this.renderIcon((): void => { + onClick && onClick.call(null, editor); }); case 'DialogIcon': return ( - {this.renderIcon(() => { - onClick && onClick.call(null, appHelper); + {this.renderIcon((): void => { + onClick && onClick.call(null, editor); this.handleOpen(); })} { - appHelper.emit(`${addonKey}.dialog.onOk`); + onOk={(): void => { + editor.emit(`${pluginKey}.dialog.onOk`); this.handleClose(); }} onCancel={this.handleClose} onClose={this.handleClose} title={title} + style={{ + width: 500, + ...(props.dialogProps && props.dialogProps.style), + }} {...(props.dialogProps || {})} visible={dialogVisible} > @@ -199,8 +198,8 @@ export default class LeftAddon extends PureComponent { case 'BalloonIcon': return ( { - onClick && onClick.call(null, appHelper); + trigger={this.renderIcon((): void => { + onClick && onClick.call(null, editor); })} align="r" triggerType={['click', 'hover']} @@ -210,12 +209,11 @@ export default class LeftAddon extends PureComponent { ); case 'PanelIcon': - return this.renderIcon(() => { - onClick && onClick.call(null, appHelper); - this.handleOpen(); + return this.renderIcon((): void => { + onClick && onClick.call(null, editor); }); case 'Custom': - return dotted ? {node} : node; + return marked ? {node} : node; default: return null; } diff --git a/packages/editor-skeleton/src/components/Panel/index.scss b/packages/editor-skeleton/src/components/Panel/index.scss new file mode 100644 index 000000000..8a9660638 --- /dev/null +++ b/packages/editor-skeleton/src/components/Panel/index.scss @@ -0,0 +1,52 @@ +.lowcode-panel { + user-select: none; + overflow: hidden; + position: relative; + background: $card-background; + transition: width 0.3s ease; + transform: translate3d(0, 0, 0); + height: 100%; + &.visible { + border-right: 1px solid $color-line1-1; + } + .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: 50px; + } + } + &.right { + &.floatable { + right: 48px; + } + } +} diff --git a/packages/editor-skeleton/src/components/Panel/index.tsx b/packages/editor-skeleton/src/components/Panel/index.tsx index e69de29bb..6cdb1bd40 100644 --- a/packages/editor-skeleton/src/components/Panel/index.tsx +++ b/packages/editor-skeleton/src/components/Panel/index.tsx @@ -0,0 +1,60 @@ +import React, { PureComponent } from 'react'; +import classNames from 'classnames'; + +import './index.scss'; + +export interface PanelProps { + align: 'left' | 'right'; + defaultWidth: number; + minWidth: number; + draggable: boolean; + floatable: boolean; + children: Plugin; + visible: boolean; +} + +export interface PanelState { + width: number; +} + +export default class Panel extends PureComponent { + static displayName = 'LowcodePanel'; + + static defaultProps = { + align: 'left', + defaultWidth: 240, + minWidth: 100, + draggable: true, + floatable: false, + visible: true, + }; + + constructor(props) { + super(props); + + this.state = { + width: props.defaultWidth, + }; + } + + render(): React.ReactNode { + const { align, draggable, floatable, visible } = this.props; + const { width } = this.state; + return ( +
+ {this.props.children} +
+
+ ); + } +} diff --git a/packages/editor-skeleton/src/components/TopIcon/index.scss b/packages/editor-skeleton/src/components/TopIcon/index.scss index 1cb3bdfdf..4a826b05f 100644 --- a/packages/editor-skeleton/src/components/TopIcon/index.scss +++ b/packages/editor-skeleton/src/components/TopIcon/index.scss @@ -1,22 +1,68 @@ -.next-btn.next-large.lowcode-top-btn { +.lowcode-top-icon { + display: inline-block; width: 44px; - height: 44px; - padding: 0; - margin: 4px -2px; - text-align: center; - border-radius: 8px; - border: 1px solid transparent; - color: #777; + font-size: 20px; + line-height: 48px; + color: $color-text1-3; + position: relative; &.disabled { cursor: not-allowed; color: $color-text1-1; + &:hover { + color: $color-text1-1; + } + } + &.active { + color: $color-brand1-9; + &:hover { + color: $color-brand1-6; + } } &.locked { color: red !important; } + &:hover { + color: $color-brand1-6; + &:before { + content: attr(data-tooltip); + display: block; + height: auto; + width: auto; + position: absolute; + left: 50%; + transform: translate(-50%, 0); + bottom: -32px; + line-height: 18px; + font-size: 12px; + white-space: nowrap; + padding: 6px 8px; + border-radius: 4px; + background: $balloon-normal-color-bg; + border: 1px solid $balloon-normal-color-border; + opacity: 0.8; + color: $color-text1-3; + z-index: 100; + } + &:after { + content: ''; + display: block; + position: absolute; + width: 10px; + height: 10px; + left: 50%; + transform: translate(-50%, 0) rotate(45deg); + bottom: -5px; + background: $balloon-normal-color-bg; + border-left: 1px solid $balloon-normal-color-border; + border-top: 1px solid $balloon-normal-color-border; + opacity: 1; + visibility: visible; + z-index: 100; + } + } i.next-icon { &:before { - font-size: 17px; + font-size: 16px; } margin-right: 0; line-height: 18px; diff --git a/packages/editor-skeleton/src/components/TopIcon/index.tsx b/packages/editor-skeleton/src/components/TopIcon/index.tsx index 8e81c9c3a..939f77ffc 100644 --- a/packages/editor-skeleton/src/components/TopIcon/index.tsx +++ b/packages/editor-skeleton/src/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 { Icon } 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; + style?: React.CSSProperties; + title?: string; +} + +export default class TopIcon extends PureComponent { + static displayName = 'LowcodeTopIcon'; + static defaultProps = { active: false, className: '', @@ -25,13 +27,12 @@ export default class TopIcon extends PureComponent { icon: '', id: '', locked: false, - onClick: () => {}, - showTitle: false, + onClick: (): void => {}, style: {}, title: '', }; - render() { + render(): React.ReactNode { const { active, disabled, @@ -41,28 +42,22 @@ export default class TopIcon extends PureComponent { className, id, style, - showTitle, onClick, } = this.props; return ( - + +
); } } diff --git a/packages/editor-skeleton/src/components/TopPlugin/index.tsx b/packages/editor-skeleton/src/components/TopPlugin/index.tsx index 04f5d0e59..2f00402d5 100644 --- a/packages/editor-skeleton/src/components/TopPlugin/index.tsx +++ b/packages/editor-skeleton/src/components/TopPlugin/index.tsx @@ -1,20 +1,43 @@ import React, { PureComponent, Fragment } from 'react'; -import PropTypes from 'prop-types'; -import TopIcon from '../TopIcon'; import { Balloon, Badge, Dialog } from '@alifd/next'; +import Editor from '@ali/lowcode-editor-framework'; +import { + PluginConfig, + PluginClass, +} from '@ali/lowcode-editor-framework/lib/definitions'; +import TopIcon from '../TopIcon'; import './index.scss'; -export default class TopPlugin extends PureComponent { - static displayName = 'lowcodeTopPlugin'; - + +export interface TopPluginProps { + active?: boolean; + config: PluginConfig; + disabled?: boolean; + editor: Editor; + locked?: boolean; + marked?: boolean; + onClick?: () => void; + pluginClass: PluginClass | undefined; +} + +export interface TopPluginState { + dialogVisible: boolean; +} + +export default class TopPlugin extends PureComponent< + TopPluginProps, + TopPluginState +> { + static displayName = 'LowcodeTopPlugin'; + static defaultProps = { active: false, config: {}, disabled: false, - dotted: false, + marked: false, locked: false, - onClick: () => {}, + onClick: (): void => {}, }; constructor(props, context) { @@ -24,42 +47,42 @@ export default class TopPlugin extends PureComponent { }; } - componentDidMount() { - const { config } = this.props; + componentDidMount(): void { + const { config, editor } = 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); - // } + if (editor && pluginKey) { + editor.on(`${pluginKey}.dialog.show`, this.handleShow); + editor.on(`${pluginKey}.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); - // } + componentWillUnmount(): void { + const { config, editor } = this.props; + const pluginKey = config && config.pluginKey; + if (editor && pluginKey) { + editor.off(`${pluginKey}.dialog.show`, this.handleShow); + editor.off(`${pluginKey}.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); + handleShow = (): void => { + const { disabled, config, onClick, editor } = this.props; + const pluginKey = config && config.pluginKey; + if (disabled || !pluginKey) return; this.handleOpen(); + // 考虑到弹窗情况,延时发送消息 + setTimeout((): void => { + editor.emit(`${pluginKey}.plugin.activate`); + }, 0); 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(() => { + handleClose = (): void => { + const { config, editor } = this.props; + const pluginKey = config && config.pluginKey; + const plugin = editor.plugins && editor.plugins[pluginKey]; + if (plugin) { + plugin.close().then((): void => { this.setState({ dialogVisible: false, }); @@ -67,85 +90,107 @@ export default class TopPlugin extends PureComponent { } }; - handleOpen = () => { + handleOpen = (): void => { // todo dialog类型的插件初始时拿不动插件实例 this.setState({ dialogVisible: true, }); }; - renderIcon = clickCallback => { - const { active, disabled, dotted, locked, config, onClick } = this.props; + renderIcon = (clickCallback): React.ReactNode => { + const { + active, + disabled, + marked, + locked, + config, + onClick, + editor, + } = this.props; const { pluginKey, props } = config || {}; const { icon, title } = props || {}; const node = ( { + onClick={(): void => { if (disabled) return; - //考虑到弹窗情况,延时发送消息 - setTimeout( - () => this.appHelper.emit(`${pluginKey}.addon.activate`), - 0, - ); + // 考虑到弹窗情况,延时发送消息 + setTimeout((): void => { + editor.emit(`${pluginKey}.plugin.activate`); + }, 0); clickCallback && clickCallback(); onClick && onClick(); }} /> ); - return dotted ? {node} : node; + return marked ? {node} : node; }; - render() { - const { active, dotted, locked, disabled, config, editor, pluginClass: Comp } = this.props; + render(): React.ReactNode { + const { + active, + marked, + 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} - />; + if (!pluginKey || !type) return null; + const node = Comp ? ( + { + onClick && onClick.call(null, editor); + }} + {...pluginProps} + /> + ) : null; switch (type) { case 'LinkIcon': return ( - {this.renderIcon(() => { + {this.renderIcon((): void => { onClick && onClick.call(null, editor); })} ); case 'Icon': - return this.renderIcon(() => { + return this.renderIcon((): void => { onClick && onClick.call(null, editor); }); case 'DialogIcon': return ( - {this.renderIcon(() => { + {this.renderIcon((): void => { onClick && onClick.call(null, editor); this.handleOpen(); })} { + onOk={(): void => { editor.emit(`${pluginKey}.dialog.onOk`); this.handleClose(); }} onCancel={this.handleClose} onClose={this.handleClose} title={title} + style={{ + width: 500, + ...(props.dialogProps && props.dialogProps.style), + }} {...props.dialogProps} visible={dialogVisible} > @@ -156,7 +201,7 @@ export default class TopPlugin extends PureComponent { case 'BalloonIcon': return ( { + trigger={this.renderIcon((): void => { onClick && onClick.call(null, editor); })} triggerType={['click', 'hover']} @@ -166,7 +211,7 @@ export default class TopPlugin extends PureComponent { ); case 'Custom': - return dotted ? {node} : node; + return marked ? {node} : node; default: return null; } diff --git a/packages/editor-skeleton/src/config/skeleton.ts b/packages/editor-skeleton/src/config/skeleton.ts index 9e3f6898f..ff8b4c563 100644 --- a/packages/editor-skeleton/src/config/skeleton.ts +++ b/packages/editor-skeleton/src/config/skeleton.ts @@ -1,21 +1 @@ -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; +export default {}; diff --git a/packages/editor-skeleton/src/config/utils.ts b/packages/editor-skeleton/src/config/utils.ts index e3c4c9e37..ff8b4c563 100644 --- a/packages/editor-skeleton/src/config/utils.ts +++ b/packages/editor-skeleton/src/config/utils.ts @@ -1,5 +1 @@ -// 菜单配置 - -const asideMenuConfig = []; - -export { asideMenuConfig }; +export default {}; diff --git a/packages/editor-skeleton/src/global.scss b/packages/editor-skeleton/src/global.scss index 0a710b895..a6b6aea91 100644 --- a/packages/editor-skeleton/src/global.scss +++ b/packages/editor-skeleton/src/global.scss @@ -7,7 +7,9 @@ body { * { box-sizing: border-box; } + color: $color-text1-3; } + .next-loading { .next-loading-wrap { height: 100%; @@ -16,18 +18,17 @@ body { .lowcode-editor { .lowcode-main-content { position: absolute; - top: 48px; + top: 50px; left: 0; right: 0; bottom: 0; display: flex; - background-color: #d8d8d8; + background-color: rgba(31, 56, 88, 0.06); } .lowcode-center-area { flex: 1; display: flex; flex-direction: column; - padding: 10px; overflow: auto; } } diff --git a/packages/editor-skeleton/src/index.tsx b/packages/editor-skeleton/src/index.tsx index 3f1c89522..b4a32480e 100644 --- a/packages/editor-skeleton/src/index.tsx +++ b/packages/editor-skeleton/src/index.tsx @@ -1,56 +1,147 @@ import React, { PureComponent } from 'react'; -// import Editor from '@ali/lowcode-engine-editor'; import { Loading, ConfigProvider } from '@alifd/next'; +import { HashRouter as Router, Route } from 'react-router-dom'; +import Editor, { utils } from '@ali/lowcode-editor-framework'; +import { + EditorConfig, + Utils, + PluginClassSet, +} from '@ali/lowcode-editor-framework/lib/definitions'; 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'; -export default class Skeleton extends PureComponent { - static displayName = 'lowcodeEditorSkeleton'; +const { comboEditorConfig, parseSearch } = utils; - constructor(props) { - super(props); - // this.editor = new Editor(props.config, props.utils); - this.editor = { - on: () => {}, - off: () => {}, - config: props.config, - pluginComponents: props.pluginComponents +let renderIdx = 0; + +declare global { + interface Window { + __ctx: { + editor: Editor; + appHelper: Editor; + }; + } +} + +export interface SkeletonProps { + components: PluginClassSet; + config: EditorConfig; + history: object; + location: object; + match: object; + utils: Utils; +} + +export interface SkeletonState { + initReady?: boolean; + skeletonKey?: string; + __hasError?: boolean; +} + +export class Skeleton extends PureComponent { + static displayName = 'LowcodeEditorSkeleton'; + + static getDerivedStateFromError(): SkeletonState { + return { + __hasError: true, }; } - componentWillUnmount() { - // this.editor && this.editor.destroy(); - // this.editor = null; + private editor: Editor; + + constructor(props) { + super(props); + + this.state = { + initReady: false, + skeletonKey: `skeleton${renderIdx}`, + }; + + this.init(); } - render() { - const { location, history, messages } = this.props; - this.editor.location = location; - this.editor.history = history; - this.editor.messages = messages; + componentWillUnmount(): void { + this.editor && this.editor.destroy(); + } + + componentDidCatch(err): void { + console.error(err); + } + + init = (isReset: boolean = false): void => { + if (this.editor) { + this.editor.destroy(); + } + const { utils, config, components } = this.props; + const editor = new Editor( + comboEditorConfig(defaultConfig, config), + components, + { + ...skeletonUtils, + ...utils, + }, + ); + this.editor = editor; + // eslint-disable-next-line no-underscore-dangle + window.__ctx = { + editor, + appHelper: editor, + }; + editor.once('editor.reset', (): void => { + this.setState({ + initReady: false, + }); + editor.emit('editor.beforeReset'); + this.init(true); + }); + + this.editor.init().then((): void => { + this.setState( + { + initReady: true, + // 刷新IDE时生成新的skeletonKey保证插件生命周期重新执行 + skeletonKey: isReset + ? `skeleton${++renderIdx}` + : this.state.skeletonKey, + }, + (): void => { + editor.emit('editor.ready'); + editor.emit('ide.ready'); + isReset && editor.emit('ide.afterReset'); + }, + ); + }); + }; + + render(): React.ReactNode { + const { initReady, skeletonKey, __hasError } = this.state; + const { location, history, match } = this.props; + if (__hasError || !this.editor) { + return 'error'; + } + + location.query = parseSearch(location.search); + this.editor.set('location', location); + this.editor.set('history', history); + this.editor.set('match', match); + return ( - -
- + +
+
- - - - + + + +
@@ -58,3 +149,33 @@ export default class Skeleton extends PureComponent { ); } } + +// 通过React-Router包裹,支持编辑器内页面根据路由切换 +export interface SkeletonWithRouterProps { + components: PluginClassSet; + config: EditorConfig; + utils: Utils; +} + +const SkeletonWithRouter: React.FC = ( + props, +): React.ReactElement => { + const { config, ...otherProps } = props; + return ( + + ( + + )} + /> + + ); +}; + +export default SkeletonWithRouter; diff --git a/packages/editor-skeleton/src/layouts/CenterArea/index.scss b/packages/editor-skeleton/src/layouts/CenterArea/index.scss index b2584ed2b..40a1806a6 100644 --- a/packages/editor-skeleton/src/layouts/CenterArea/index.scss +++ b/packages/editor-skeleton/src/layouts/CenterArea/index.scss @@ -1,3 +1,3 @@ .lowcode-center-area { - padding: 12px; + padding: 0; } diff --git a/packages/editor-skeleton/src/layouts/CenterArea/index.tsx b/packages/editor-skeleton/src/layouts/CenterArea/index.tsx index dc2b38d25..2736b9005 100644 --- a/packages/editor-skeleton/src/layouts/CenterArea/index.tsx +++ b/packages/editor-skeleton/src/layouts/CenterArea/index.tsx @@ -1,15 +1,60 @@ import React, { PureComponent } from 'react'; - +import Editor, { AreaManager } from '@ali/lowcode-editor-framework'; import './index.scss'; -export default class CenterArea extends PureComponent { - static displayName = 'lowcodeCenterArea'; +export interface CenterAreaProps { + editor: Editor; +} + +export default class CenterArea extends PureComponent { + static displayName = 'LowcodeCenterArea'; + + private editor: Editor; + + private areaManager: AreaManager; constructor(props) { super(props); + this.editor = props.editor; + this.areaManager = new AreaManager(this.editor, 'centerArea'); } - render() { - return
; + componentDidMount(): void { + this.editor.on('skeleton.update', this.handleSkeletonUpdate); + } + + componentWillUnmount(): void { + this.editor.off('skeleton.update', this.handleSkeletonUpdate); + } + + handleSkeletonUpdate = (): void => { + // 当前区域插件状态改变是更新区域 + if (this.areaManager.isPluginStatusUpdate()) { + this.forceUpdate(); + } + }; + + render(): React.ReactNode { + const visiblePluginList = this.areaManager.getVisiblePluginList(); + return ( +
+ {visiblePluginList.map( + (item): React.ReactNode => { + const Comp = this.areaManager.getPluginClass(item.pluginKey); + if (Comp) { + return ( + + ); + } + return null; + }, + )} +
+ ); } } diff --git a/packages/editor-skeleton/src/layouts/LeftArea/index.scss b/packages/editor-skeleton/src/layouts/LeftArea/index.scss index dac1b6b0a..8c69f42fc 100644 --- a/packages/editor-skeleton/src/layouts/LeftArea/index.scss +++ b/packages/editor-skeleton/src/layouts/LeftArea/index.scss @@ -1,21 +1,23 @@ .lowcode-left-area-nav { - width: 48px; + width: 50px; height: 100%; - background: #ffffff; - border-right: 1px solid #e8ebee; + background-color: $card-background; + border-right: 2px solid $color-line1-1; position: relative; .top-area { position: absolute; top: 0; width: 100%; - background: #ffffff; + padding: 12px 0; + background-color: $card-background; max-height: 100%; } .bottom-area { position: absolute; - bottom: 20px; + bottom: 0; width: 100%; - background: #ffffff; + padding: 12px 0; + background-color: $card-background; max-height: calc(100% - 20px); } } diff --git a/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx b/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx index 6c58c12ef..3777fc57a 100644 --- a/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx +++ b/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx @@ -1,15 +1,148 @@ import React, { PureComponent } from 'react'; +import LeftPlugin from '../../components/LeftPlugin'; +import Editor, { utils, AreaManager } from '@ali/lowcode-editor-framework'; +import { PluginConfig } from '@ali/lowcode-editor-framework/lib/definitions'; import './index.scss'; -export default class LeftAreaPanel extends PureComponent { - static displayName = 'lowcodeLeftAreaNav'; +const { isEmpty } = utils; + +export interface LeftAreaNavProps { + editor: Editor; +} + +export interface LeftAreaNavState { + activeKey: string; +} + +export default class LeftAreaNav extends PureComponent< + LeftAreaNavProps, + LeftAreaNavState +> { + static displayName = 'LowcodeLeftAreaNav'; + + private editor: Editor; + + private areaManager: AreaManager; constructor(props) { super(props); + this.editor = props.editor; + this.areaManager = new AreaManager(this.editor, 'leftArea'); + + this.state = { + activeKey: 'none', + }; } - render() { - return
; + componentDidMount(): void { + this.editor.on('skeleton.update', this.handleSkeletonUpdate); + this.editor.on('leftNav.change', this.handlePluginChange); + const visiblePanelPluginList = this.areaManager.getVisiblePluginList( + 'IconPanel', + ); + const defaultKey = + (visiblePanelPluginList[0] && visiblePanelPluginList[0].pluginKey) || + 'componentAttr'; + this.handlePluginChange(defaultKey); + } + + componentWillUnmount(): void { + this.editor.off('skeleton.update', this.handleSkeletonUpdate); + this.editor.off('leftNav.change', this.handlePluginChange); + } + + handleSkeletonUpdate = (): void => { + // 当前区域插件状态改变是更新区域 + if (this.areaManager.isPluginStatusUpdate()) { + this.forceUpdate(); + } + }; + + handlePluginChange = (key: string): void => { + const { activeKey } = this.state; + const plugins = this.editor.plugins; + const prePlugin = plugins[activeKey]; + const nextPlugin = plugins[key]; + if (activeKey === 'none') { + if (nextPlugin) { + nextPlugin.open().then((): void => { + this.updateActiveKey(key); + }); + } + } else if (activeKey === key) { + if (prePlugin) { + prePlugin.close().then((): void => { + this.updateActiveKey('none'); + }); + } + } else if (prePlugin) { + // 先关后开 + prePlugin.close().then((): void => { + if (nextPlugin) { + nextPlugin.open().then((): void => { + this.updateActiveKey(key); + }); + } + }); + } + }; + + handlePluginClick = (item: PluginConfig): void => { + if (item.type === 'PanelIcon') { + this.handlePluginChange(item.pluginKey); + } + }; + + updateActiveKey = (key: string): void => { + this.editor.set('leftNav', key); + this.setState({ activeKey: key }); + this.editor.emit('leftPanel.show', key); + }; + + renderPluginList = (list: PluginConfig[] = []): React.ReactElement[] => { + const { activeKey } = this.state; + return list.map( + (item): React.ReactElement => { + const pluginStatus = this.areaManager.getPluginStatus(item.pluginKey); + const pluginClass = this.areaManager.getPluginClass(item.pluginKey); + return ( + this.handlePluginClick(item)} + active={activeKey === item.pluginKey} + {...pluginStatus} + /> + ); + }, + ); + }; + + render(): React.ReactNode { + const topList: PluginConfig[] = []; + const bottomList: PluginConfig[] = []; + const visiblePluginList = this.areaManager.getVisiblePluginList(); + if (isEmpty(visiblePluginList)) { + return null; + } + visiblePluginList.forEach((item): void => { + 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-skeleton/src/layouts/LeftArea/panel.tsx b/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx index ab41860fb..0c62f07b2 100644 --- a/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx +++ b/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx @@ -1,15 +1,88 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent, Fragment } from 'react'; +import Editor, { AreaManager } from '@ali/lowcode-editor-framework'; +import Panel from '../../components/Panel'; import './index.scss'; -export default class LeftAreaPanel extends PureComponent { - static displayName = 'lowcodeLeftAreaPanel'; +export interface LeftAreaPanelProps { + editor: Editor; +} + +export interface LeftAreaPanelState { + activeKey: string; +} + +export default class LeftAreaPanel extends PureComponent< + LeftAreaPanelProps, + LeftAreaPanelState +> { + static displayName = 'LowcodeLeftAreaPanel'; + + private editor: Editor; + + private areaManager: AreaManager; constructor(props) { super(props); + this.editor = props.editor; + this.areaManager = new AreaManager(this.editor, 'leftArea'); + + this.state = { + activeKey: 'none', + }; } - render() { - return
; + componentDidMount(): void { + this.editor.on('skeleton.update', this.handleSkeletonUpdate); + this.editor.on('leftPanel.show', this.handlePluginChange); + } + + componentWillUnmount(): void { + this.editor.off('skeleton.update', this.handleSkeletonUpdate); + this.editor.off('leftPanel.show', this.handlePluginChange); + } + + handleSkeletonUpdate = (): void => { + // 当前区域插件状态改变是更新区域 + if (this.areaManager.isPluginStatusUpdate('PanelIcon')) { + this.forceUpdate(); + } + }; + + handlePluginChange = (key: string): void => { + this.setState({ + activeKey: key, + }); + }; + + render(): React.ReactNode { + const { activeKey } = this.state; + const list = this.areaManager.getVisiblePluginList('PanelIcon'); + + return ( + + {list.map( + (item): React.ReactNode => { + const Comp = this.areaManager.getPluginClass(item.pluginKey); + if (Comp) { + return ( + + + + ); + } + return null; + }, + )} + + ); } } diff --git a/packages/editor-skeleton/src/layouts/RightArea/index.scss b/packages/editor-skeleton/src/layouts/RightArea/index.scss index 120ef4f11..7373933b0 100644 --- a/packages/editor-skeleton/src/layouts/RightArea/index.scss +++ b/packages/editor-skeleton/src/layouts/RightArea/index.scss @@ -1,155 +1,37 @@ .lowcode-right-area { - width: 300px; + width: 262px; 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; - } + background-color: $card-background; + border-left: 2px solid $color-line1-1; + + .right-panel { + overflow: auto; + // border-top: 2px solid $color-line1-1; } //tab定义 - .next-tabs-wrapped.right-tabs { - display: flex; - flex-direction: column; - margin-top: -1px; - .next-tabs-bar { - z-index: 1; - } + .right-tabs.next-tabs { .next-tabs-nav { - display: block; - .next-tabs-tab { - &:first-child { - border-left: none; - } - font-size: 14px; + width: 100%; + .next-tabs-tab-inner { + padding-left: 0; + padding-right: 0; + } + .right-plugin-title { text-align: center; - border-right: none !important; - margin-right: 0 !important; - width: 25%; + &.locked { + color: red !important; + } &.active { - background: none; - border-bottom-color: #f7f7f7 !important; + color: $color-brand1-9 !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; + &.disabled { + cursor: not-allowed; + color: $color-text1-1; } - } - .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; + .next-icon { + line-height: 15px; + margin-right: 2px; } } } diff --git a/packages/editor-skeleton/src/layouts/RightArea/index.tsx b/packages/editor-skeleton/src/layouts/RightArea/index.tsx index 31273a4e2..40a6d9ad0 100644 --- a/packages/editor-skeleton/src/layouts/RightArea/index.tsx +++ b/packages/editor-skeleton/src/layouts/RightArea/index.tsx @@ -1,15 +1,214 @@ import React, { PureComponent } from 'react'; - +import { Tab, Badge, Icon } from '@alifd/next'; +import classNames from 'classnames'; +import Editor, { AreaManager, utils } from '@ali/lowcode-editor-framework'; +import { PluginConfig } from '@ali/lowcode-editor-framework/lib/definitions'; import './index.scss'; -export default class RightArea extends PureComponent { - static displayName = 'lowcodeRightArea'; +const { isEmpty } = utils; + +export interface RightAreaProps { + editor: Editor; +} + +export interface RightAreaState { + activeKey: string; +} + +export default class RightArea extends PureComponent< + RightAreaProps, + RightAreaState +> { + static displayName = 'LowcodeRightArea'; + + private editor: Editor; + + private areaManager: AreaManager; constructor(props) { super(props); + this.editor = props.editor; + this.areaManager = new AreaManager(this.editor, 'rightArea'); + this.state = { + activeKey: '', + }; } - render() { - return
; + componentDidMount(): void { + this.editor.on('skeleton.update', this.handleSkeletonUpdate); + this.editor.on('rightNav.change', this.handlePluginChange); + const visiblePluginList = this.areaManager.getVisiblePluginList('TabPanel'); + const defaultKey = + (visiblePluginList[0] && visiblePluginList[0].pluginKey) || + 'componentAttr'; + this.handlePluginChange(defaultKey, true); + } + + componentWillUnmount(): void { + this.editor.off('skeleton.update', this.handleSkeletonUpdate); + this.editor.off('rightNav.change', this.handlePluginChange); + } + + handleSkeletonUpdate = (): void => { + // 当前区域插件状态改变是更新区域 + if (this.areaManager.isPluginStatusUpdate()) { + const activeKey = this.state.activeKey; + const activePluginStatus = this.areaManager.getPluginStatus(activeKey); + if (activePluginStatus && activePluginStatus.visible) { + this.forceUpdate(); + } else { + const currentPlugin = this.areaManager.getPlugin(activeKey); + if (currentPlugin) { + currentPlugin.close().then((): void => { + this.setState( + { + activeKey: '', + }, + (): void => { + const visiblePluginList = this.areaManager.getVisiblePluginList( + 'TabPanel', + ); + const firstPlugin = visiblePluginList && visiblePluginList[0]; + if (firstPlugin) { + this.handlePluginChange(firstPlugin.pluginKey); + } + }, + ); + }); + } + } + } + }; + + handlePluginChange = (key: string, isinit?: boolean): void => { + const activeKey = this.state.activeKey; + const currentPlugin = this.areaManager.getPlugin(activeKey); + const nextPlugin = this.areaManager.getPlugin(key); + const openPlugin = (): void => { + if (!nextPlugin) { + console.error(`plugin ${key} has not regist in the editor`); + return; + } + nextPlugin.open().then((): void => { + this.editor.set('rightNav', key); + this.setState({ + activeKey: key, + }); + }); + }; + if (key === activeKey && !isinit) return; + if (currentPlugin) { + currentPlugin.close().then((): void => { + openPlugin(); + }); + } else { + openPlugin(); + } + }; + + renderTabTitle = (config: PluginConfig): React.ReactElement => { + const { icon, title } = config.props || {}; + const pluginStatus = this.editor.pluginStatus[config.pluginKey]; + const { marked, disabled, locked } = pluginStatus; + const active = this.state.activeKey === config.pluginKey; + + const renderTitle = (): React.ReactElement => ( +
+ {!!icon && } + {title} +
+ ); + if (marked) { + return {renderTitle()}; + } + return renderTitle(); + }; + + renderTabPanels = (list: PluginConfig[], height: string): React.ReactNode => { + if (isEmpty(list)) { + return null; + } + return ( + + {list.map( + (item): React.ReactNode => { + const Comp = this.areaManager.getPluginClass(item.pluginKey); + if (Comp) { + return ( + + + + ); + } + return null; + }, + )} + + ); + }; + + renderPanels = (list: PluginConfig[], height: string): React.ReactNode => { + return list.map( + (item): React.ReactNode => { + const Comp = this.areaManager.getPluginClass(item.pluginKey); + if (Comp) { + return ( +
+ +
+ ); + } + return null; + }, + ); + }; + + render(): React.ReactNode { + const tabList = this.areaManager.getVisiblePluginList('TabPanel'); + const panelList = this.areaManager.getVisiblePluginList('Panel'); + if (isEmpty(panelList) && isEmpty(tabList)) { + return null; + } else if (tabList.length === 1) { + panelList.unshift(tabList[0]); + tabList.splice(0, 1); + } + const height = `${Math.floor( + 100 / (panelList.length + (tabList.length > 0 ? 1 : 0)), + )}%`; + return ( +
+ {this.renderTabPanels(tabList, height)} + {this.renderPanels(panelList, height)} +
+ ); } } diff --git a/packages/editor-skeleton/src/layouts/TopArea/index.scss b/packages/editor-skeleton/src/layouts/TopArea/index.scss index ca8bbd825..c0a132f84 100644 --- a/packages/editor-skeleton/src/layouts/TopArea/index.scss +++ b/packages/editor-skeleton/src/layouts/TopArea/index.scss @@ -1,5 +1,30 @@ .lowcode-top-area { - height: 48px; - background-color: #ffffff; - border-bottom: 1px solid #e8ebee; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 50px; + background-color: $card-background; + border-bottom: 2px solid $color-line1-1; + user-select: none; + .divider { + max-width: 0; + margin: 12px 16px; + height: 24px; + border-right: 1px solid $color-line1-2; + } + .next-col { + text-align: center; + } + .left-area { + padding: 0 16px; + } + .right-area { + position: absolute; + right: 0; + top: 0; + padding: 0 16px; + height: 100%; + background: $card-background; + } } diff --git a/packages/editor-skeleton/src/layouts/TopArea/index.tsx b/packages/editor-skeleton/src/layouts/TopArea/index.tsx index c2f60a637..434e12d60 100644 --- a/packages/editor-skeleton/src/layouts/TopArea/index.tsx +++ b/packages/editor-skeleton/src/layouts/TopArea/index.tsx @@ -1,61 +1,81 @@ import React, { PureComponent } from 'react'; import { Grid } from '@alifd/next'; +import Editor, { AreaManager } from '@ali/lowcode-editor-framework'; +import { PluginConfig } from '@ali/lowcode-editor-framework/lib/definitions'; import TopPlugin from '../../components/TopPlugin'; import './index.scss'; const { Row, Col } = Grid; -export default class TopArea extends PureComponent { - static displayName = 'lowcodeTopArea'; +export interface TopAreaProps { + editor: Editor; +} + +export default class TopArea extends PureComponent { + static displayName = 'LowcodeTopArea'; + + private areaManager: AreaManager; + + private editor: Editor; constructor(props) { super(props); this.editor = props.editor; - this.config = this.editor.config.plugins && this.editor.config.plugins.topArea; + this.areaManager = new AreaManager(props.editor, 'topArea'); } - componentDidMount() { - } - componentWillUnmount() { + componentDidMount(): void { + this.editor.on('skeleton.update', this.handleSkeletonUpdate); } - handlePluginStatusChange = () => {}; + componentWillUnmount(): void { + this.editor.off('skeleton.update', this.handleSkeletonUpdate); + } - renderPluginList = (list = []) => { - return list.map((item, idx) => { - const isDivider = item.type === 'Divider'; - - return ( - - {!isDivider && ( - - )} - - ); - }); + handleSkeletonUpdate = (): void => { + // 当前区域插件状态改变是更新区域 + if (this.areaManager.isPluginStatusUpdate()) { + this.forceUpdate(); + } }; - render() { - if (!this.config) return null; - const leftList = []; - const rightList = []; - this.config.forEach(item => { + renderPluginList = (list: PluginConfig[] = []): React.ReactElement[] => { + return list.map( + (item, idx): React.ReactElement => { + const isDivider = item.type === 'Divider'; + const PluginClass = this.areaManager.getPluginClass(item.pluginKey); + return ( + + {!isDivider && ( + + )} + + ); + }, + ); + }; + + render(): React.ReactNode { + const leftList: PluginConfig[] = []; + const rightList: PluginConfig[] = []; + const visiblePluginList = this.areaManager.getVisiblePluginList(); + visiblePluginList.forEach((item): void => { const align = item.props && item.props.align === 'right' ? 'right' : 'left'; // 分隔符不允许相邻 if (item.type === 'Divider') { - const currentList = align === 'right' ? rightList : leftList; + const currList = align === 'right' ? rightList : leftList; if ( currList.length === 0 || currList[currList.length - 1].type === 'Divider' @@ -68,11 +88,14 @@ export default class TopArea extends PureComponent { leftList.push(item); } }); - return (
-
{this.renderPluginList(leftList)}
-
{this.renderPluginList(rightList)}
+
+ {this.renderPluginList(leftList)} +
+
+ {this.renderPluginList(rightList)} +
); } diff --git a/packages/editor-skeleton/src/locale/en-US.js b/packages/editor-skeleton/src/locale/en-US.js index 36e3b219c..ff8b4c563 100644 --- a/packages/editor-skeleton/src/locale/en-US.js +++ b/packages/editor-skeleton/src/locale/en-US.js @@ -1,10 +1 @@ -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', -}; +export default {}; diff --git a/packages/editor-skeleton/src/locale/zh-CN.js b/packages/editor-skeleton/src/locale/zh-CN.js index 2d5229d2c..ff8b4c563 100644 --- a/packages/editor-skeleton/src/locale/zh-CN.js +++ b/packages/editor-skeleton/src/locale/zh-CN.js @@ -1,10 +1 @@ -export default { - loading: '加载中...', - rejectRedirect: '开发中,已阻止发生跳转', - expand: '展开', - fold: '收起', - pageNotExist: '当前访问地址不存在', - enterFromAppCenter: '请从应用中心入口重新进入', - noPermission: '抱歉,您暂无开发权限', - getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限', -}; +export default {}; diff --git a/packages/editor/package.json b/packages/editor/package.json index c9864f35f..c5441d220 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -3,6 +3,8 @@ "version": "0.0.1", "description": "低代码编辑器", "dependencies": { + "@ali/lowcode-editor-framework": "0.0.1", + "@ali/lowcode-editor-skeleton": "0.0.1", "@ali/iceluna-addon-2": "^1.0.3", "@ali/iceluna-addon-component-list": "^1.0.11", "@ali/iceluna-sdk": "^1.0.5-beta.26", diff --git a/packages/editor/src/config/skeleton.js b/packages/editor/src/config/skeleton.js index 6fc68aa2a..2e9012768 100644 --- a/packages/editor/src/config/skeleton.js +++ b/packages/editor/src/config/skeleton.js @@ -3,7 +3,7 @@ import assets from './assets'; export default { version: '^1.0.2', theme: { - dpl: { + fusion: { package: '@alife/dpl-iceluna', version: '^2.3.0' }, diff --git a/packages/editor/src/framework/context.ts b/packages/editor/src/framework/context.ts deleted file mode 100644 index 859b79dc4..000000000 --- a/packages/editor/src/framework/context.ts +++ /dev/null @@ -1,4 +0,0 @@ -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 deleted file mode 100644 index 6bc6d2653..000000000 --- a/packages/editor/src/framework/definitions.ts +++ /dev/null @@ -1,154 +0,0 @@ -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; - 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 { - [propName: string]: PluginConfig[]; -} - -export interface PluginConfig { - pluginKey: string; - type: string; - 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 = HookConfig[]; - -export interface HookConfig { - message: string; - type: 'on' | 'once'; - handler: (editor: Editor, ...args) => void; -} - -export type ShortCutsConfig = ShortCutConfig[]; - -export interface ShortCutConfig { - keyboard: string; - handler: (editor: Editor, ev: Event, keymaster: any) => void; -} - -export type UtilsConfig = UtilConfig[]; - -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 { - [key: string]: string; -} - -export interface I18nConfig { - 'zh-CN'?: I18nMessages; - 'zh-TW'?: I18nMessages; - 'en-US'?: I18nMessages; - 'ja-JP'?: I18nMessages; -} - -export type I18nFunction = (key: string, params: any) => string; - -export interface Utils { - [key: string]: (...args) => any; -} - -export interface PluginProps { - editor: Editor; - config: PluginConfig; - i18n?: I18nFunction; - ref?: React.RefObject; - [key: string]: any; -} - -export type Plugin = React.ReactNode & { - open?: () => boolean | void | Promise; - close?: () => boolean | void | Promise; -}; - -export type HOCPlugin = React.ReactNode & { - open: () => Promise; - close: () => Promise; -}; - -export interface PluginSet { - [key: string]: HOCPlugin; -} - -export type PluginClass = React.ComponentType & { - init?: (editor: Editor) => void; -}; - -export interface PluginClassSet { - [key: string]: PluginClass; -} - -export interface PluginStatus { - disabled?: boolean; - visible?: boolean; - marked?: boolean; - locked?: boolean; -} - -export interface PluginStatusSet { - [key: string]: PluginStatus; -} diff --git a/packages/editor/src/framework/editor.ts b/packages/editor/src/framework/editor.ts deleted file mode 100644 index cf1147e2c..000000000 --- a/packages/editor/src/framework/editor.ts +++ /dev/null @@ -1,230 +0,0 @@ -import Debug from 'debug'; -import EventEmitter from 'events'; -import store from 'store'; -import { - EditorConfig, - HooksConfig, - LocaleType, - PluginStatusSet, - Utils, - PluginClassSet, - PluginSet -} from './definitions'; - -import * as editorUtils from './utils'; - -const { registShortCuts, transformToPromise, unRegistShortCuts } = editorUtils; - -declare global { - interface Window { - __isDebug?: boolean; - __newFunc?: (funcStr: string) => (...args: any[]) => any; - } -} - -// 根据url参数设置debug选项 -const debugRegRes = /_?debug=(.*?)(&|$)/.exec(location.search); -if (debugRegRes && debugRegRes[1]) { - // eslint-disable-next-line no-underscore-dangle - window.__isDebug = true; - store.storage.write('debug', debugRegRes[1] === 'true' ? '*' : debugRegRes[1]); -} else { - // eslint-disable-next-line no-underscore-dangle - window.__isDebug = false; - store.remove('debug'); -} - -// 重要,用于矫正画布执行new Function的window对象上下文 -// eslint-disable-next-line no-underscore-dangle -window.__newFunc = (funContext: string): ((...args: any[]) => any) => { - // eslint-disable-next-line no-new-func - return new Function(funContext) as (...args: any[]) => any; -}; - -// 关闭浏览器前提醒,只有产生过交互才会生效 -window.onbeforeunload = function(e: Event): string | void { - const ev = e || window.event; - // 本地调试不生效 - if (location.href.indexOf('localhost') > 0) { - return; - } - const msg = '您确定要离开此页面吗?'; - ev.cancelBubble = true; - ev.returnValue = true; - if (e.stopPropagation) { - e.stopPropagation(); - e.preventDefault(); - } - return msg; -}; - -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 { - public static getInstance = (config: EditorConfig, components: PluginClassSet, utils?: Utils): Editor => { - if (!instance) { - instance = new Editor(config, components, utils); - } - return instance; - }; - - public config: EditorConfig; - - public components: PluginClassSet; - - public utils: Utils; - - public pluginStatus: PluginStatusSet; - - public plugins: PluginSet; - - 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; - - private hooksFuncs: HooksFuncs; - - constructor(config: EditorConfig, components: PluginClassSet, utils?: Utils) { - super(); - this.config = config; - this.components = components; - this.utils = { ...editorUtils, ...utils }; - instance = this; - this.init(); - } - - public 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.emit('editor.beforeInit'); - const init = (lifeCycles && lifeCycles.init) || ((): void => {}); - // 用户可以通过设置extensions.init自定义初始化流程; - return transformToPromise(init(this)) - .then((): boolean => { - // 注册快捷键 - registShortCuts(shortCuts, this); - this.emit('editor.afterInit'); - return true; - }) - .catch((err): void => { - console.error(err); - }); - } - - public destroy(): void { - debug('destroy'); - try { - const { hooks = [], shortCuts = [], lifeCycles = {} } = this.config; - unRegistShortCuts(shortCuts); - this.destroyHooks(hooks); - if (lifeCycles.destroy) { - lifeCycles.destroy(this); - } - } catch (err) { - console.warn(err); - } - } - - public get(key: string): any { - return this[key]; - } - - public set(key: string | object, val: any): void { - if (typeof key === 'string') { - 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; - } else if (typeof key === 'object') { - Object.keys(key).forEach((item): void => { - this[item] = key[item]; - }); - } - } - - public batchOn(events: string[], lisenter: (...args) => void): void { - if (!Array.isArray(events)) { - return; - } - events.forEach((event): void => this.on(event, lisenter)); - } - - public batchOnce(events: string[], lisenter: (...args) => void): void { - if (!Array.isArray(events)) { - return; - } - events.forEach((event): void => this.once(event, lisenter)); - } - - public batchOff(events: string[], lisenter: (...args) => void): void { - if (!Array.isArray(events)) { - return; - } - events.forEach((event): void => this.off(event, lisenter)); - } - - // 销毁hooks中的消息监听 - private destroyHooks(hooks: HooksConfig = []): void { - hooks.forEach((item, idx): void => { - if (typeof this.hooksFuncs[idx] === 'function') { - this.off(item.message, this.hooksFuncs[idx]); - } - }); - delete this.hooksFuncs; - } - - // 初始化hooks中的消息监听 - private initHooks(hooks: HooksConfig = []): void { - this.hooksFuncs = hooks.map((item): ((...arg) => void) => { - const func = (...args): void => { - item.handler(this, ...args); - }; - this[item.type](item.message, func); - return func; - }); - } - - private initPluginStatus(): PluginStatusSet { - const { plugins = {} } = this.config; - const pluginAreas = Object.keys(plugins); - const res: PluginStatusSet = {}; - pluginAreas.forEach((area): void => { - (plugins[area] || []).forEach((plugin): void => { - if (plugin.type === 'Divider') { - return; - } - const { visible, disabled, marked } = plugin.props || {}; - res[plugin.pluginKey] = { - visible: typeof visible === 'boolean' ? visible : true, - disabled: typeof disabled === 'boolean' ? disabled : false, - marked: typeof marked === 'boolean' ? marked : 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 deleted file mode 100644 index 72af6e859..000000000 --- a/packages/editor/src/framework/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Editor from './editor'; - -import * as editorUtils from './utils'; -import * as editorDefinitions from './definitions'; - -export { default as PluginFactory } from './pluginFactory'; -export { default as EditorContext } from './context'; - -export default Editor; - -export const utils = editorUtils; -export const definitions = editorDefinitions; diff --git a/packages/editor/src/framework/utils.ts b/packages/editor/src/framework/utils.ts deleted file mode 100644 index aced7d318..000000000 --- a/packages/editor/src/framework/utils.ts +++ /dev/null @@ -1,272 +0,0 @@ -import IntlMessageFormat from 'intl-messageformat'; -import keymaster from 'keymaster'; - -import _clone from 'lodash/cloneDeep'; -import _debounce from 'lodash/debounce'; -import _isEmpty from 'lodash/isEmpty'; -import _deepEqual from 'lodash/isEqualWith'; -import _pick from 'lodash/pick'; -import _throttle from 'lodash/throttle'; - -import _serialize from 'serialize-javascript'; -import Editor from './editor'; -import { EditorConfig, I18nFunction, I18nMessages, LocaleType, ShortCutsConfig } from './definitions'; - -export const pick = _pick; -export const deepEqual = _deepEqual; -export const clone = _clone; -export const isEmpty = _isEmpty; -export const throttle = _throttle; -export const debounce = _debounce; - -export const serialize = _serialize; - -const ENV = { - TBE: 'TBE', - WEBIDE: 'WEB-IDE', - VSCODE: 'VSCODE', - WEB: 'WEB' -}; - -declare global { - interface Window { - sendIDEMessage?: (params: IDEMessageParams) => void; - goldlog?: { - record: (logKey: string, gmKey: string, goKey: string, method: 'POST' | 'GET') => (...args: any[]) => any; - }; - is_theia?: boolean; - vscode?: boolean; - } -} - -export interface IDEMessageParams { - action: string; - data: { - logKey: string; - gmKey: string; - goKey: string; - }; -} - -/* - * 用于构造国际化字符串处理函数 - */ -export function generateI18n(locale: LocaleType = 'zh-CN', messages: I18nMessages = {}): I18nFunction { - return (key: string, values): string => { - if (!messages || !messages[key]) { - return ''; - } - const formater = new IntlMessageFormat(messages[key], locale); - return formater.format(values); - }; -} - -/** - * 序列化参数 - */ -export function serializeParams(obj: object): string { - if (typeof obj !== 'object') { - return ''; - } - const res: string[] = []; - Object.entries(obj).forEach(([key, val]): void => { - 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: string, params: object = {}, logKey: string = 'other'): void { - const sendIDEMessage = window.sendIDEMessage || window.parent.sendIDEMessage; - const goKey = serializeParams({ - env: getEnv(), - ...params - }); - if (sendIDEMessage) { - sendIDEMessage({ - action: 'goldlog', - data: { - logKey: `/iceluna.core.${logKey}`, - gmKey, - goKey - } - }); - } - if (window.goldlog) { - window.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST'); - } -} - -/** - * 获取当前编辑器环境 - */ -export function getEnv(): string { - 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: ShortCutsConfig, editor: Editor): void { - (config || []).forEach((item): void => { - keymaster(item.keyboard, (ev: Event): void => { - ev.preventDefault(); - item.handler(editor, ev, keymaster); - }); - }); -} - -// 取消注册快捷 -export function unRegistShortCuts(config: ShortCutsConfig): void { - (config || []).forEach((item): void => { - keymaster.unbind(item.keyboard); - }); - if (window.parent.vscode) { - keymaster.unbind('command+c'); - keymaster.unbind('command+v'); - } -} - -/** - * 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve - */ -export function transformToPromise(input: any): Promise<{}> { - if (input instanceof Promise) { - return input; - } - return new Promise((resolve, reject): void => { - if (input || input === undefined) { - resolve(); - } else { - reject(); - } - }); -} - -/** - * 将数组类型转换为Map类型 - */ -interface MapOf { - [propName: string]: T; -} -export function transformArrayToMap(arr: T[], key: string, overwrite: boolean = true): MapOf { - if (isEmpty(arr) || !Array.isArray(arr)) { - return {}; - } - const res = {}; - arr.forEach((item): void => { - const curKey = item[key]; - if (item[key] === undefined) { - return; - } - if (res[curKey] && !overwrite) { - return; - } - res[curKey] = item; - }); - return res; -} - -/** - * 解析url的查询参数 - */ -interface Query { - [propName: string]: string; -} -export function parseSearch(search: string): Query { - if (!search || typeof search !== 'string') { - return {}; - } - const str = search.replace(/^\?/, ''); - const paramStr = str.split('&'); - const res = {}; - paramStr.forEach((item): void => { - const regRes = item.split('='); - if (regRes[0] && regRes[1]) { - res[regRes[0]] = decodeURIComponent(regRes[1]); - } - }); - return res; -} - -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 - }); - } - - 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): void => { - 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 - }; -} - -/** - * 判断当前组件是否能够设置ref - * @param {*} Comp 需要判断的组件 - */ -export function acceptsRef(Comp: React.ReactNode): boolean { - const hasSymbol = typeof Symbol === 'function' && Symbol.for; - const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0; - if (!Comp || typeof Comp !== 'object' || isEmpty(Comp)) { - return false; - } - return ( - (Comp.$$typeof && Comp.$$typeof === REACT_FORWARD_REF_TYPE) || (Comp.prototype && Comp.prototype.isReactComponent) - ); -} diff --git a/packages/editor/src/index.d.ts b/packages/editor/src/index.d.ts deleted file mode 100644 index 1fe34f40d..000000000 --- a/packages/editor/src/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -interface Window { - sendIDEMessage?: (IDEMessageParams) => void; - goldlog?: { - record: (logKey: string, gmKey: string, goKey: string, method: 'GET' | 'POST') => void; - }; - is_theia?: boolean; - vscode?: boolean; -} diff --git a/packages/editor/src/index.tsx b/packages/editor/src/index.tsx index 22e76df35..23dbb8f92 100644 --- a/packages/editor/src/index.tsx +++ b/packages/editor/src/index.tsx @@ -1,12 +1,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; -// import Skeleton from '@ali/lowcode-engine-skeleton'; -import { HashRouter as Router, Route } from 'react-router-dom'; -import Skeleton from './skeleton'; +import Skeleton from '@ali/lowcode-editor-skeleton'; import config from './config/skeleton'; import components from './config/components'; import utils from './config/utils'; -import constants from './config/constants'; import './config/locale'; import './config/setters'; @@ -19,21 +16,6 @@ if (!ICE_CONTAINER) { throw new Error('当前页面不存在
节点.'); } -ReactDOM.render( - - ( - - )} - /> - , +ReactDOM.render(, ICE_CONTAINER ); diff --git a/packages/editor/src/skeleton/components/LeftPlugin/index.scss b/packages/editor/src/skeleton/components/LeftPlugin/index.scss deleted file mode 100644 index f1e7876d3..000000000 --- a/packages/editor/src/skeleton/components/LeftPlugin/index.scss +++ /dev/null @@ -1,54 +0,0 @@ -.lowcode-left-plugin { - font-size: 20px; - text-align: center; - line-height: 44px; - height: 44px; - position: relative; - cursor: pointer; - transition: all 0.3s ease; - color: $color-text1-3; - &.locked { - color: red !important; - } - &:hover { - 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: $balloon-tooltip-color-bg; - color: $color-text1-3; - z-index: 100; - } - &:after { - content: ''; - display: block; - position: absolute; - left: 40px; - top: 15px; - border: 5px solid transparent; - border-right: 5px solid $balloon-tooltip-color-bg; - z-index: 100; - } - } - &.active { - color: $color-brand1-9; - &.disabled { - color: $color-text1-1; - } - &:hover { - color: $color-brand1-6; - } - } - &.disabled { - cursor: not-allowed; - color: $color-text1-1; - } -} diff --git a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx b/packages/editor/src/skeleton/components/LeftPlugin/index.tsx deleted file mode 100644 index eafe894ee..000000000 --- a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import React, { PureComponent, Fragment } from 'react'; -import classNames from 'classnames'; -import { Balloon, Dialog, Icon, Badge } from '@alifd/next'; - -import './index.scss'; -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, - marked: false, - locked: false, - onClick: (): void => {} - }; - - constructor(props, context) { - super(props, context); - this.state = { - dialogVisible: false - }; - } - - componentDidMount(): void { - const { config, editor } = this.props; - const pluginKey = config && config.pluginKey; - if (editor && pluginKey) { - editor.on(`${pluginKey}.dialog.show`, this.handleShow); - editor.on(`${pluginKey}.dialog.close`, this.handleClose); - } - } - - componentWillUnmount(): void { - const { config, editor } = this.props; - const pluginKey = config && config.pluginKey; - if (editor && pluginKey) { - editor.off(`${pluginKey}.dialog.show`, this.handleShow); - editor.off(`${pluginKey}.dialog.close`, this.handleClose); - } - } - - handleClose = (): void => { - const { config, editor } = this.props; - const pluginKey = config && config.pluginKey; - const plugin = editor.plugins && editor.plugins[pluginKey]; - if (plugin && plugin.close) { - plugin.close().then((): void => { - this.setState({ - dialogVisible: false - }); - }); - } - }; - - handleOpen = (): void => { - // todo 对话框类型的插件初始时拿不到插件实例 - this.setState({ - dialogVisible: true - }); - }; - - handleShow = (): void => { - const { disabled, config, onClick, editor } = this.props; - const pluginKey = config && config.pluginKey; - if (disabled || !pluginKey) return; - // 考虑到弹窗情况,延时发送消息 - setTimeout((): void => editor.emit(`${pluginKey}.plugin.activate`), 0); - this.handleOpen(); - if (onClick) { - onClick(); - } - }; - - renderIcon = (clickCallback): React.ReactNode => { - const { active, disabled, marked, locked, onClick, config } = this.props; - const { pluginKey, props } = config || {}; - const { icon, title } = props || {}; - return ( -
{ - if (disabled) return; - // 考虑到弹窗情况,延时发送消息 - clickCallback && clickCallback(); - - onClick && onClick(); - }} - > - {marked ? ( - - - - ) : ( - - )} -
- ); - }; - - render(): React.ReactNode { - 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; - if (!pluginKey || !type || !props) return null; - - const node = - (Comp && ( - { - onClick && onClick.call(null, editor); - }} - {...pluginProps} - /> - )) || - null; - - switch (type) { - case 'LinkIcon': - return ( - - {this.renderIcon((): void => { - onClick && onClick.call(null, editor); - })} - - ); - case 'Icon': - return this.renderIcon((): void => { - onClick && onClick.call(null, editor); - }); - case 'DialogIcon': - return ( - - {this.renderIcon((): void => { - onClick && onClick.call(null, editor); - this.handleOpen(); - })} - { - editor.emit(`${pluginKey}.dialog.onOk`); - this.handleClose(); - }} - onCancel={this.handleClose} - onClose={this.handleClose} - title={title} - style={{ - width: 500, - ...(props.dialogProps && props.dialogProps.style) - }} - {...(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((): void => { - onClick && onClick.call(null, editor); - this.handleOpen(); - }); - case 'Custom': - 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 deleted file mode 100644 index 611877d46..000000000 --- a/packages/editor/src/skeleton/components/Panel/index.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React, { PureComponent } from 'react'; -import classNames from 'classnames'; - -import './index.scss'; - -export interface PanelProps { - align: 'left' | 'right'; - defaultWidth: number; - minWidth: number; - draggable: boolean; - floatable: boolean; - children: Plugin; - visible: boolean; -} - -export interface PanelState { - width: number; -} - -export default class Panel extends PureComponent { - static displayName = 'LowcodePanel'; - - static defaultProps = { - align: 'left', - defaultWidth: 240, - minWidth: 100, - draggable: true, - floatable: false, - visible: true - }; - - constructor(props) { - super(props); - - this.state = { - width: props.defaultWidth - }; - } - - render(): React.ReactNode { - const { align, draggable, floatable, visible } = this.props; - const { width } = this.state; - return ( -
- {this.props.children} -
-
- ); - } -} diff --git a/packages/editor/src/skeleton/components/TopIcon/index.scss b/packages/editor/src/skeleton/components/TopIcon/index.scss deleted file mode 100644 index 2dd2e3bb7..000000000 --- a/packages/editor/src/skeleton/components/TopIcon/index.scss +++ /dev/null @@ -1,73 +0,0 @@ -.lowcode-top-icon { - display: inline-block; - width: 44px; - font-size: 20px; - line-height: 48px; - color: $color-text1-3; - position: relative; - &.disabled { - cursor: not-allowed; - color: $color-text1-1; - &:hover { - color: $color-text1-1; - } - } - &.active { - color: $color-brand1-9; - &:hover { - color: $color-brand1-6; - } - } - &.locked { - color: red !important; - } - &:hover { - color: $color-brand1-6; - &:before { - content: attr(data-tooltip); - display: block; - height: auto; - width: auto; - position: absolute; - left: 50%; - transform: translate(-50%, 0); - bottom: -35px; - line-height: 18px; - font-size: 12px; - white-space: nowrap; - padding: 6px 8px; - border-radius: 4px; - background: $balloon-tooltip-color-bg; - color: $color-text1-3; - z-index: 100; - } - &:after { - content: ''; - display: block; - position: absolute; - left: 50%; - transform: translate(-50%, 0); - bottom: -5px; - border: 5px solid transparent; - border-bottom-color: $balloon-tooltip-color-bg; - opacity: 1; - visibility: visible; - z-index: 100; - } - } - i.next-icon { - &:before { - font-size: 16px; - } - 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 deleted file mode 100644 index 2c14d8d33..000000000 --- a/packages/editor/src/skeleton/components/TopIcon/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { PureComponent } from 'react'; -import classNames from 'classnames'; -import { Icon } from '@alifd/next'; - -import './index.scss'; - -export interface TopIconProps { - active?: boolean; - className?: string; - disabled?: boolean; - icon: string; - id?: string; - locked?: boolean; - marked?: boolean; - onClick?: () => void; - style?: React.CSSProperties; - title?: string; -} - -export default class TopIcon extends PureComponent { - static displayName = 'LowcodeTopIcon'; - - static defaultProps = { - active: false, - className: '', - disabled: false, - icon: '', - id: '', - locked: false, - onClick: (): void => {}, - style: {}, - title: '' - }; - - render(): React.ReactNode { - const { active, disabled, icon, locked, title, className, id, style, onClick } = this.props; - return ( -
- -
- ); - } -} diff --git a/packages/editor/src/skeleton/components/TopPlugin/index.scss b/packages/editor/src/skeleton/components/TopPlugin/index.scss deleted file mode 100644 index 4bdd7b8d2..000000000 --- a/packages/editor/src/skeleton/components/TopPlugin/index.scss +++ /dev/null @@ -1,2 +0,0 @@ -.lowcode-top-addon { -} diff --git a/packages/editor/src/skeleton/components/TopPlugin/index.tsx b/packages/editor/src/skeleton/components/TopPlugin/index.tsx deleted file mode 100644 index 70c653f4f..000000000 --- a/packages/editor/src/skeleton/components/TopPlugin/index.tsx +++ /dev/null @@ -1,195 +0,0 @@ -import React, { PureComponent, Fragment, CSSProperties } from 'react'; - -import { Balloon, Badge, Dialog } from '@alifd/next'; -import TopIcon from '../TopIcon'; - -import './index.scss'; -import { PluginConfig, PluginClass } from '../../../framework/definitions'; -import Editor from '../../../framework/editor'; - -export interface TopPluginProps { - active?: boolean; - config: PluginConfig; - disabled?: boolean; - editor: Editor; - locked?: boolean; - marked?: boolean; - onClick?: () => void; - pluginClass: PluginClass; -} - -export interface TopPluginState { - dialogVisible: boolean; -} - -export default class TopPlugin extends PureComponent { - static displayName = 'LowcodeTopPlugin'; - - static defaultProps = { - active: false, - config: {}, - disabled: false, - marked: false, - locked: false, - onClick: (): void => {} - }; - - constructor(props, context) { - super(props, context); - this.state = { - dialogVisible: false - }; - } - - componentDidMount(): void { - const { config, editor } = this.props; - const pluginKey = config && config.pluginKey; - if (editor && pluginKey) { - editor.on(`${pluginKey}.dialog.show`, this.handleShow); - editor.on(`${pluginKey}.dialog.close`, this.handleClose); - } - } - - componentWillUnmount(): void { - const { config, editor } = this.props; - const pluginKey = config && config.pluginKey; - if (editor && pluginKey) { - editor.off(`${pluginKey}.dialog.show`, this.handleShow); - editor.off(`${pluginKey}.dialog.close`, this.handleClose); - } - } - - handleShow = (): void => { - const { disabled, config, onClick, editor } = this.props; - const pluginKey = config && config.pluginKey; - if (disabled || !pluginKey) return; - // 考虑到弹窗情况,延时发送消息 - setTimeout((): void => editor.emit(`${pluginKey}.plugin.activate`), 0); - this.handleOpen(); - onClick && onClick(); - }; - - handleClose = (): void => { - const { config, editor } = this.props; - const pluginKey = config && config.pluginKey; - const plugin = editor.plugins && editor.plugins[pluginKey]; - if (plugin && plugin.close) { - plugin.close().then((): void => { - this.setState({ - dialogVisible: false - }); - }); - } - }; - - handleOpen = (): void => { - // todo dialog类型的插件初始时拿不动插件实例 - this.setState({ - dialogVisible: true - }); - }; - - renderIcon = (clickCallback): React.ReactNode => { - const { active, disabled, marked, locked, config, onClick, editor } = this.props; - const { pluginKey, props } = config || {}; - const { icon, title } = props || {}; - const node = ( - { - if (disabled) return; - // 考虑到弹窗情况,延时发送消息 - setTimeout((): void => editor.emit(`${pluginKey}.plugin.activate`), 0); - clickCallback && clickCallback(); - onClick && onClick(); - }} - /> - ); - return marked ? {node} : node; - }; - - render(): React.ReactNode { - const { active, marked, locked, disabled, config, editor, pluginClass: Comp, style } = 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((): void => { - onClick && onClick.call(null, editor); - })} - - ); - case 'Icon': - return this.renderIcon((): void => { - onClick && onClick.call(null, editor); - }); - case 'DialogIcon': - return ( - - {this.renderIcon((): void => { - onClick && onClick.call(null, editor); - this.handleOpen(); - })} - { - editor.emit(`${pluginKey}.dialog.onOk`); - this.handleClose(); - }} - onCancel={this.handleClose} - onClose={this.handleClose} - title={title} - style={{ - width: 500, - ...(props.dialogProps && props.dialogProps.style) - }} - {...props.dialogProps} - visible={dialogVisible} - > - {node} - - - ); - case 'BalloonIcon': - return ( - { - onClick && onClick.call(null, editor); - })} - triggerType={['click', 'hover']} - {...props.balloonProps} - > - {node} - - ); - case 'Custom': - return marked ? {node} : node; - default: - return null; - } - } -} diff --git a/packages/editor/src/skeleton/config/skeleton.ts b/packages/editor/src/skeleton/config/skeleton.ts deleted file mode 100644 index ff8b4c563..000000000 --- a/packages/editor/src/skeleton/config/skeleton.ts +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/packages/editor/src/skeleton/config/utils.ts b/packages/editor/src/skeleton/config/utils.ts deleted file mode 100644 index ff8b4c563..000000000 --- a/packages/editor/src/skeleton/config/utils.ts +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/packages/editor/src/skeleton/global.scss b/packages/editor/src/skeleton/global.scss deleted file mode 100644 index 72f7b8525..000000000 --- a/packages/editor/src/skeleton/global.scss +++ /dev/null @@ -1,33 +0,0 @@ -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; - } - color: $color-text1-3; -} - -.next-loading { - .next-loading-wrap { - height: 100%; - } -} -.lowcode-editor { - .lowcode-main-content { - position: absolute; - top: 50px; - left: 0; - right: 0; - bottom: 0; - display: flex; - background-color: rgba(31, 56, 88, 0.06); - } - .lowcode-center-area { - flex: 1; - display: flex; - flex-direction: column; - overflow: auto; - } -} diff --git a/packages/editor/src/skeleton/index.tsx b/packages/editor/src/skeleton/index.tsx deleted file mode 100644 index c30a0fc88..000000000 --- a/packages/editor/src/skeleton/index.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import React, { PureComponent } from 'react'; - -import { Loading, ConfigProvider } from '@alifd/next'; - -import Editor from '../framework/editor'; -import { EditorConfig, Utils, PluginClassSet } from '../framework/definitions'; -import { comboEditorConfig, parseSearch } from '../framework/utils'; - -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; - -declare global { - interface Window { - __ctx: { - editor: Editor; - appHelper: Editor; - }; - } -} - -export interface SkeletonProps { - components: PluginClassSet; - config: EditorConfig; - history: object; - location: object; - match: object; - utils: Utils; -} - -export interface SkeletonState { - initReady?: boolean; - skeletonKey?: string; - __hasError?: boolean; -} - -export default class Skeleton extends PureComponent { - static displayName = 'LowcodeEditorSkeleton'; - - static getDerivedStateFromError(): SkeletonState { - return { - __hasError: true - }; - } - - private editor: Editor; - - constructor(props) { - super(props); - - this.state = { - initReady: false, - skeletonKey: `skeleton${renderIdx}` - }; - - this.init(); - } - - componentWillUnmount(): void { - this.editor && this.editor.destroy(); - } - - componentDidCatch(err): void { - console.error(err); - } - - init = (isReset: boolean = false): void => { - if (this.editor) { - this.editor.destroy(); - } - const { utils, config, components } = this.props; - const editor = new Editor(comboEditorConfig(defaultConfig, config), components, { - ...skeletonUtils, - ...utils - }); - this.editor = editor; - // eslint-disable-next-line no-underscore-dangle - window.__ctx = { - editor, - appHelper: editor - }; - editor.once('editor.reset', (): void => { - this.setState({ - initReady: false - }); - editor.emit('editor.beforeReset'); - this.init(true); - }); - - this.editor.init().then((): void => { - this.setState( - { - initReady: true, - // 刷新IDE时生成新的skeletonKey保证插件生命周期重新执行 - skeletonKey: isReset ? `skeleton${++renderIdx}` : this.state.skeletonKey - }, - (): void => { - editor.emit('editor.ready'); - editor.emit('ide.ready'); - isReset && editor.emit('ide.afterReset'); - } - ); - }); - }; - - render(): React.ReactNode { - const { initReady, skeletonKey, __hasError } = this.state; - const { location, history, match } = this.props; - if (__hasError || !this.editor) { - return 'error'; - } - - 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 deleted file mode 100644 index 40a1806a6..000000000 --- a/packages/editor/src/skeleton/layouts/CenterArea/index.scss +++ /dev/null @@ -1,3 +0,0 @@ -.lowcode-center-area { - padding: 0; -} diff --git a/packages/editor/src/skeleton/layouts/CenterArea/index.tsx b/packages/editor/src/skeleton/layouts/CenterArea/index.tsx deleted file mode 100644 index cf27d66db..000000000 --- a/packages/editor/src/skeleton/layouts/CenterArea/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { PureComponent } from 'react'; - -import Editor from '../../../framework/editor'; -import './index.scss'; -import AreaManager from '../../../framework/areaManager'; - -export interface CenterAreaProps { - editor: Editor; -} - -export default class CenterArea extends PureComponent { - static displayName = 'LowcodeCenterArea'; - - private editor: Editor; - - private areaManager: AreaManager; - - constructor(props) { - super(props); - this.editor = props.editor; - this.areaManager = new AreaManager(this.editor, 'centerArea'); - } - - componentDidMount(): void { - this.editor.on('skeleton.update', this.handleSkeletonUpdate); - } - - componentWillUnmount(): void { - this.editor.off('skeleton.update', this.handleSkeletonUpdate); - } - - handleSkeletonUpdate = (): void => { - // 当前区域插件状态改变是更新区域 - if (this.areaManager.isPluginStatusUpdate()) { - this.forceUpdate(); - } - }; - - render(): React.ReactNode { - const visiblePluginList = this.areaManager.getVisiblePluginList(); - return ( -
- {visiblePluginList.map( - (item): React.ReactNode => { - 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 deleted file mode 100644 index 8c69f42fc..000000000 --- a/packages/editor/src/skeleton/layouts/LeftArea/index.scss +++ /dev/null @@ -1,23 +0,0 @@ -.lowcode-left-area-nav { - width: 50px; - height: 100%; - background-color: $card-background; - border-right: 2px solid $color-line1-1; - position: relative; - .top-area { - position: absolute; - top: 0; - width: 100%; - padding: 12px 0; - background-color: $card-background; - max-height: 100%; - } - .bottom-area { - position: absolute; - bottom: 0; - width: 100%; - padding: 12px 0; - background-color: $card-background; - 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 deleted file mode 100644 index 7f4684546..000000000 --- a/packages/editor/src/skeleton/layouts/LeftArea/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index cd749073a..000000000 --- a/packages/editor/src/skeleton/layouts/LeftArea/nav.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import React, { PureComponent } from 'react'; -import LeftPlugin from '../../components/LeftPlugin'; -import './index.scss'; -import Editor from '../../../framework/editor'; -import { PluginConfig } from '../../../framework/definitions'; -import AreaManager from '../../../framework/areaManager'; -import { isEmpty } from '../../../framework/utils'; - -export interface LeftAreaNavProps { - editor: Editor; -} - -export interface LeftAreaNavState { - activeKey: string; -} - -export default class LeftAreaNav extends PureComponent { - static displayName = 'LowcodeLeftAreaNav'; - - private editor: Editor; - - private areaManager: AreaManager; - - // private cacheActiveKey: string; - - constructor(props) { - super(props); - this.editor = props.editor; - this.areaManager = new AreaManager(this.editor, 'leftArea'); - - this.state = { - activeKey: 'none' - }; - // this.cacheActiveKey = 'none'; - } - - componentDidMount(): void { - this.editor.on('skeleton.update', this.handleSkeletonUpdate); - this.editor.on('leftNav.change', this.handlePluginChange); - const visiblePanelPluginList = this.areaManager.getVisiblePluginList().filter(item => item.type === 'IconPanel'); - const defaultKey = (visiblePanelPluginList[0] && visiblePanelPluginList[0].pluginKey) || 'componentAttr'; - this.handlePluginChange(defaultKey); - } - - componentWillUnmount(): void { - this.editor.off('skeleton.update', this.handleSkeletonUpdate); - this.editor.off('leftNav.change', this.handlePluginChange); - } - - handleSkeletonUpdate = (): void => { - // 当前区域插件状态改变是更新区域 - if (this.areaManager.isPluginStatusUpdate()) { - this.forceUpdate(); - } - }; - - handlePluginChange = (key: string): void => { - const { activeKey } = this.state; - const plugins = this.editor.plugins; - const prePlugin = plugins[activeKey]; - const nextPlugin = plugins[key]; - if (activeKey === 'none') { - if (nextPlugin) { - nextPlugin.open().then((): void => { - this.updateActiveKey(key); - }); - } - } else if (activeKey === key) { - if (prePlugin) { - prePlugin.close().then((): void => { - this.updateActiveKey('none'); - }); - } - } else if (prePlugin) { - // 先关后开 - prePlugin.close().then((): void => { - if (nextPlugin) { - nextPlugin.open().then((): void => { - this.updateActiveKey(key); - }); - } - }); - } - }; - - // handleCollapseClick = (): void => { - // const { activeKey } = this.state; - // if (activeKey === 'none') { - // const plugin = this.editor.plugins[this.cacheActiveKey]; - // if (plugin) { - // plugin.open().then(() => { - // this.updateActiveKey(this.cacheActiveKey); - // }); - // } - // } else { - // const plugin = this.editor.plugins[activeKey]; - // if (plugin) { - // plugin.close().then(() => { - // this.updateActiveKey('none'); - // }); - // } - // } - // }; - - handlePluginClick = (item: PluginConfig): void => { - if (item.type === 'PanelIcon') { - this.handlePluginChange(item.pluginKey); - } - }; - - updateActiveKey = (key: string): void => { - // if (key === 'none') { - // this.cacheActiveKey = this.state.activeKey; - // } - this.editor.set('leftNav', key); - this.setState({ activeKey: key }); - this.editor.emit('leftPanel.show', key); - }; - - renderPluginList = (list: PluginConfig[] = []): React.ReactElement[] => { - const { activeKey } = this.state; - return list.map( - (item): React.ReactElement => { - const pluginStatus = this.editor.pluginStatus[item.pluginKey]; - return ( - this.handlePluginClick(item)} - active={activeKey === item.pluginKey} - {...pluginStatus} - /> - ); - } - ); - }; - - render(): React.ReactNode { - const topList: PluginConfig[] = []; - const bottomList: PluginConfig[] = []; - const visiblePluginList = this.areaManager.getVisiblePluginList(); - if (isEmpty(visiblePluginList)) { - return null; - } - visiblePluginList.forEach((item): void => { - 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 deleted file mode 100644 index 31f2c5343..000000000 --- a/packages/editor/src/skeleton/layouts/LeftArea/panel.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { PureComponent, Fragment } from 'react'; -import Panel from '../../components/Panel'; -import './index.scss'; -import Editor from '../../../framework/editor'; -import AreaManager from '../../../framework/areaManager'; - -export interface LeftAreaPanelProps { - editor: Editor; -} - -export interface LeftAreaPanelState { - activeKey: string; -} - -export default class LeftAreaPanel extends PureComponent { - static displayName = 'LowcodeLeftAreaPanel'; - - private editor: Editor; - - private areaManager: AreaManager; - - constructor(props) { - super(props); - this.editor = props.editor; - this.areaManager = new AreaManager(this.editor, 'leftArea'); - - this.state = { - activeKey: 'none' - }; - } - - componentDidMount(): void { - this.editor.on('skeleton.update', this.handleSkeletonUpdate); - this.editor.on('leftPanel.show', this.handlePluginChange); - } - - componentWillUnmount(): void { - this.editor.off('skeleton.update', this.handleSkeletonUpdate); - this.editor.off('leftPanel.show', this.handlePluginChange); - } - - handleSkeletonUpdate = (): void => { - // 当前区域插件状态改变是更新区域 - if (this.areaManager.isPluginStatusUpdate('PanelIcon')) { - this.forceUpdate(); - } - }; - - handlePluginChange = (key: string): void => { - this.setState({ - activeKey: key - }); - }; - - render(): React.ReactNode { - const { activeKey } = this.state; - const list = this.areaManager.getVisiblePluginList('PanelIcon'); - - return ( - - {list.map( - (item): React.ReactElement => { - 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 deleted file mode 100644 index 7373933b0..000000000 --- a/packages/editor/src/skeleton/layouts/RightArea/index.scss +++ /dev/null @@ -1,39 +0,0 @@ -.lowcode-right-area { - width: 262px; - height: 100%; - background-color: $card-background; - border-left: 2px solid $color-line1-1; - - .right-panel { - overflow: auto; - // border-top: 2px solid $color-line1-1; - } - - //tab定义 - .right-tabs.next-tabs { - .next-tabs-nav { - width: 100%; - .next-tabs-tab-inner { - padding-left: 0; - padding-right: 0; - } - .right-plugin-title { - text-align: center; - &.locked { - color: red !important; - } - &.active { - color: $color-brand1-9 !important; - } - &.disabled { - cursor: not-allowed; - color: $color-text1-1; - } - .next-icon { - line-height: 15px; - margin-right: 2px; - } - } - } - } -} diff --git a/packages/editor/src/skeleton/layouts/RightArea/index.tsx b/packages/editor/src/skeleton/layouts/RightArea/index.tsx deleted file mode 100644 index 64c44af50..000000000 --- a/packages/editor/src/skeleton/layouts/RightArea/index.tsx +++ /dev/null @@ -1,187 +0,0 @@ -import React, { PureComponent } from 'react'; -import { Tab, Badge, Icon } from '@alifd/next'; -import './index.scss'; -import Editor from '../../../framework/editor'; -import AreaManager from '../../../framework/areaManager'; -import { PluginConfig } from '../../../framework/definitions'; -import { isEmpty } from '../../../framework/utils'; - -export interface RightAreaProps { - editor: Editor; -} - -export interface RightAreaState { - activeKey: string; -} - -export default class RightArea extends PureComponent { - static displayName = 'LowcodeRightArea'; - - private editor: Editor; - - private areaManager: AreaManager; - - constructor(props) { - super(props); - this.editor = props.editor; - this.areaManager = new AreaManager(this.editor, 'rightArea'); - this.state = { - activeKey: '' - }; - } - - componentDidMount(): void { - this.editor.on('skeleton.update', this.handleSkeletonUpdate); - this.editor.on('rightNav.change', this.handlePluginChange); - const visiblePluginList = this.areaManager.getVisiblePluginList('TabPanel'); - const defaultKey = (visiblePluginList[0] && visiblePluginList[0].pluginKey) || 'componentAttr'; - this.handlePluginChange(defaultKey, true); - } - - componentWillUnmount(): void { - this.editor.off('skeleton.update', this.handleSkeletonUpdate); - this.editor.off('rightNav.change', this.handlePluginChange); - } - - handleSkeletonUpdate = (): void => { - // 当前区域插件状态改变是更新区域 - if (this.areaManager.isPluginStatusUpdate()) { - const pluginStatus = this.editor.pluginStatus; - const activeKey = this.state.activeKey; - if (pluginStatus[activeKey] && pluginStatus[activeKey].visible) { - this.forceUpdate(); - } else { - const currentPlugin = this.editor.plugins[activeKey]; - if (currentPlugin) { - currentPlugin.close().then((): void => { - this.setState( - { - activeKey: '' - }, - (): void => { - const visiblePluginList = this.areaManager.getVisiblePluginList(); - const firstPlugin = visiblePluginList && visiblePluginList[0]; - if (firstPlugin) { - this.handlePluginChange(firstPlugin.pluginKey); - } - } - ); - }); - } - } - } - }; - - handlePluginChange = (key: string, isinit?: boolean): void => { - const activeKey = this.state.activeKey; - const plugins = this.editor.plugins || {}; - const openPlugin = (): void => { - if (!plugins[key]) { - console.error(`plugin ${key} has not regist in the editor`); - return; - } - plugins[key].open().then((): void => { - this.editor.set('rightNav', key); - this.setState({ - activeKey: key - }); - }); - }; - if (key === activeKey && !isinit) return; - if (activeKey && plugins[activeKey]) { - plugins[activeKey].close().then((): void => { - openPlugin(); - }); - } else { - openPlugin(); - } - }; - - renderTabTitle = (config: PluginConfig): React.ReactElement => { - const { icon, title } = config.props || {}; - const pluginStatus = this.editor.pluginStatus[config.pluginKey]; - const { marked, disabled, locked } = pluginStatus; - const active = this.state.activeKey === config.pluginKey; - - const renderTitle = (): React.ReactElement => ( -
- {!!icon && } - {title} -
- ); - if (marked) { - return {renderTitle()}; - } - return renderTitle(); - }; - - renderTabPanels = (list: PluginConfig[], height: string): React.ReactNode => { - if (isEmpty(list)) { - return null; - } - return ( - - {list.map( - (item): React.ReactElement => { - const Comp = this.editor.components[item.pluginKey]; - return ( - - - - ); - } - )} - - ); - }; - - renderPanels = (list: PluginConfig[], height: string): React.ReactNode => { - return list.map( - (item): React.ReactElement => { - const Comp = this.editor.components[item.pluginKey]; - return ( -
- -
- ); - } - ); - }; - - render(): React.ReactNode { - const tabList = this.areaManager.getVisiblePluginList('TabPanel'); - const panelList = this.areaManager.getVisiblePluginList('Panel'); - if (isEmpty(panelList) && isEmpty(tabList)) { - return null; - } else if (tabList.length === 1) { - panelList.unshift(tabList[0]); - tabList.splice(0, 1); - } - const height = `${Math.floor(100 / (panelList.length + (tabList.length > 0 ? 1 : 0)))}%`; - return ( -
- {this.renderTabPanels(tabList, height)} - {this.renderPanels(panelList, height)} -
- ); - } -} diff --git a/packages/editor/src/skeleton/layouts/TopArea/index.scss b/packages/editor/src/skeleton/layouts/TopArea/index.scss deleted file mode 100644 index c0a132f84..000000000 --- a/packages/editor/src/skeleton/layouts/TopArea/index.scss +++ /dev/null @@ -1,30 +0,0 @@ -.lowcode-top-area { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 50px; - background-color: $card-background; - border-bottom: 2px solid $color-line1-1; - user-select: none; - .divider { - max-width: 0; - margin: 12px 16px; - height: 24px; - border-right: 1px solid $color-line1-2; - } - .next-col { - text-align: center; - } - .left-area { - padding: 0 16px; - } - .right-area { - position: absolute; - right: 0; - top: 0; - padding: 0 16px; - height: 100%; - background: $card-background; - } -} diff --git a/packages/editor/src/skeleton/layouts/TopArea/index.tsx b/packages/editor/src/skeleton/layouts/TopArea/index.tsx deleted file mode 100644 index d6b33453c..000000000 --- a/packages/editor/src/skeleton/layouts/TopArea/index.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { PureComponent } from 'react'; -import { Grid } from '@alifd/next'; -import TopPlugin from '../../components/TopPlugin'; -import './index.scss'; -import Editor from '../../../framework/index'; -import { PluginConfig } from '../../../framework/definitions'; -import AreaManager from '../../../framework/areaManager'; - -const { Row, Col } = Grid; - -export interface TopAreaProps { - editor: Editor; -} - -export default class TopArea extends PureComponent { - static displayName = 'LowcodeTopArea'; - - private areaManager: AreaManager; - - private editor: Editor; - - constructor(props) { - super(props); - this.editor = props.editor; - this.areaManager = new AreaManager(props.editor, 'topArea'); - } - - componentDidMount(): void { - this.editor.on('skeleton.update', this.handleSkeletonUpdate); - } - - componentWillUnmount(): void { - this.editor.off('skeleton.update', this.handleSkeletonUpdate); - } - - handleSkeletonUpdate = (): void => { - // 当前区域插件状态改变是更新区域 - if (this.areaManager.isPluginStatusUpdate()) { - this.forceUpdate(); - } - }; - - renderPluginList = (list: PluginConfig[] = []): React.ReactElement[] => { - return list.map( - (item, idx): React.ReactElement => { - const isDivider = item.type === 'Divider'; - return ( - - {!isDivider && ( - - )} - - ); - } - ); - }; - - render(): React.ReactNode { - const leftList: PluginConfig[] = []; - const rightList: PluginConfig[] = []; - const visiblePluginList = this.areaManager.getVisiblePluginList(); - visiblePluginList.forEach((item): void => { - 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 deleted file mode 100644 index 936701e33..000000000 --- a/packages/editor/src/skeleton/locale/en-US.js +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index ff8b4c563..000000000 --- a/packages/editor/src/skeleton/locale/ja-JP.js +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/packages/editor/src/skeleton/locale/zh-CN.js b/packages/editor/src/skeleton/locale/zh-CN.js deleted file mode 100644 index efe4ea898..000000000 --- a/packages/editor/src/skeleton/locale/zh-CN.js +++ /dev/null @@ -1,10 +0,0 @@ -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 deleted file mode 100644 index ff8b4c563..000000000 --- a/packages/editor/src/skeleton/locale/zh-TW.js +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/packages/editor/src/skeleton/skeleton.tsx b/packages/editor/src/skeleton/skeleton.tsx deleted file mode 100644 index eababc5f7..000000000 --- a/packages/editor/src/skeleton/skeleton.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import React, { PureComponent } from 'react'; - -import { HashRouter as Router, Route } from 'react-router-dom'; -import { Loading, ConfigProvider } from '@alifd/next'; - -import Editor from '../framework/editor'; -import { EditorConfig, Utils, PluginComponents } from '../framework/definitions'; -import { comboEditorConfig, parseSearch } from '../framework/utils'; - -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 interface SkeletonProps { - components: PluginComponents; - config: EditorConfig; - utils: Utils; -} - -export interface SkeletonState { - initReady: boolean; - skeletonKey: string; - __hasError?: boolean; -} - -export default class Skeleton extends PureComponent { - static displayName = 'LowcodeEditorSkeleton'; - - static getDerivedStateFromError() { - return { - __hasError: true - }; - } - - private editor: Editor; - - constructor(props) { - super(props); - - this.state = { - initReady: false, - skeletonKey: `skeleton${renderIdx}` - }; - - this.init(); - } - - componentWillUnmount() { - this.editor && this.editor.destroy(); - } - - componentDidCatch(err) { - console.error(err); - } - - init = (isReset: boolean = false): void => { - if (this.editor) { - this.editor.destroy(); - } - const { utils, config, components } = this.props; - const editor = (this.editor = new Editor(comboEditorConfig(defaultConfig, config), components, { - ...skeletonUtils, - ...utils - })); - 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 || !this.editor) { - 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); - console.log('&&&&&&&&&&'); - return ( - - -
- -
- - - - -
-
-
-
- ); - }} - /> -
- ); - } -} diff --git a/packages/material-parser/package.json b/packages/material-parser/package.json index 068a7fed4..0966e5681 100644 --- a/packages/material-parser/package.json +++ b/packages/material-parser/package.json @@ -42,6 +42,7 @@ }, "license": "MIT", "dependencies": { + "@ali/lowcode-editor-framework": "^0.0.1", "@babel/generator": "^7.8.4", "@babel/parser": "^7.8.4", "@babel/traverse": "^7.8.4", diff --git a/packages/plugin-setters/package.json b/packages/plugin-setters/package.json index 05ce25bac..c297ebf59 100644 --- a/packages/plugin-setters/package.json +++ b/packages/plugin-setters/package.json @@ -7,6 +7,7 @@ "@ali/iceluna-comp-object-button": "^1.0.23", "@ali/iceluna-comp-react-node": "^1.0.5", "@ali/iceluna-sdk": "^1.0.5-beta.24", + "@ali/lowcode-editor-framework": "^0.0.1", "@alifd/next": "^1.19.16", "@alife/next": "^1.19.16", "acorn": "^6.4.1", diff --git a/packages/plugin-settings/package.json b/packages/plugin-settings/package.json index cd4bbe4c6..952795cb2 100644 --- a/packages/plugin-settings/package.json +++ b/packages/plugin-settings/package.json @@ -12,6 +12,7 @@ "test:snapshot": "ava --update-snapshots" }, "dependencies": { + "@ali/lowcode-editor-framework": "^0.0.1", "@alifd/next": "^1.19.16", "classnames": "^2.2.6", "react": "^16", @@ -25,9 +26,9 @@ "@types/react-dom": "^16", "eslint": "^6.5.1", "prettier": "^1.18.2", + "ts-node": "^8.0.1", "tslib": "^1.9.3", - "typescript": "^3.1.3", - "ts-node": "^8.0.1" + "typescript": "^3.1.3" }, "ava": { "compileEnhancements": false, diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json index fd8e46e29..ba214fd16 100644 --- a/packages/react-renderer/package.json +++ b/packages/react-renderer/package.json @@ -26,6 +26,7 @@ "@ali/iceluna-comp-div": "^0.0.5", "@ali/iceluna-rax": "0.0.5", "@ali/lib-mtop": "^2.5.1", + "@ali/lowcode-editor-framework": "^0.0.1", "@alifd/next": "^1.18.17", "debug": "^4.1.1", "driver-universal": "^3.1.2", @@ -45,9 +46,9 @@ "whatwg-fetch": "^3.0.0" }, "peerDependencies": { + "prop-types": "^15.7.2", "react": "^16.8.6", - "react-dom": "^16.8.6", - "prop-types": "^15.7.2" + "react-dom": "^16.8.6" }, "devDependencies": { "@babel/cli": "^7.4.4", diff --git a/packages/runtime-framework/package.json b/packages/runtime-framework/package.json index 225792897..fe8370bf4 100644 --- a/packages/runtime-framework/package.json +++ b/packages/runtime-framework/package.json @@ -26,6 +26,7 @@ }, "license": "MIT", "dependencies": { + "@ali/lowcode-editor-framework": "^0.0.1", "@ali/recore": "^1.6.9" } } diff --git a/packages/utils/package.json b/packages/utils/package.json index b90bb4493..e00dce7a2 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,5 +1,6 @@ { "dependencies": { + "@ali/lowcode-editor-framework": "^0.0.1", "@babel/runtime": "^7.8.7" } }