diff --git a/packages/editor/.eslintrc.js b/packages/editor/.eslintrc.js
index 1c034b884..4d6be3c66 100644
--- a/packages/editor/.eslintrc.js
+++ b/packages/editor/.eslintrc.js
@@ -1,8 +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,
- "interface-name" : [true, "never-prefix"]
+ "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/ice.config.js b/packages/editor/ice.config.js
index 71d81b8b1..6843f35bb 100644
--- a/packages/editor/ice.config.js
+++ b/packages/editor/ice.config.js
@@ -12,7 +12,7 @@ module.exports = {
},
plugins: [
['ice-plugin-fusion', {
- themePackage: '@alife/dpl-iceluna',
+ themePackage: '@alife/theme-lowcode-light',
}],
['ice-plugin-moment-locales', {
locales: ['zh-cn'],
diff --git a/packages/editor/package.json b/packages/editor/package.json
index 62c301598..19c333e76 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -4,9 +4,12 @@
"description": "低代码编辑器",
"dependencies": {
"@ali/iceluna-addon-2": "^1.0.3",
+ "@ali/iceluna-addon-component-list": "^1.0.10",
"@ali/iceluna-sdk": "^1.0.5-beta.26",
"@alifd/next": "^1.x",
"@alife/dpl-iceluna": "^2.3.2",
+ "@alife/theme-lowcode-dark": "^0.1.0",
+ "@alife/theme-lowcode-light": "^0.1.0",
"@icedesign/theme": "^1.x",
"events": "^3.1.0",
"intl-messageformat": "^8.2.1",
@@ -21,13 +24,14 @@
},
"devDependencies": {
"@ice/spec": "^0.1.1",
- "@types/react": "^16.8.3",
- "@types/react-dom": "^16.8.2",
"@types/debug": "^4.1.5",
"@types/events": "^3.0.0",
+ "@types/react": "^16.8.3",
+ "@types/react-dom": "^16.8.2",
"@types/store": "^2.0.2",
"css-modules-typescript-loader": "^2.0.4",
"eslint": "^6.0.1",
+ "husky": "^4.2.3",
"ice-plugin-fusion": "^0.1.4",
"ice-plugin-moment-locales": "^0.1.0",
"ice-scripts": "^2.0.0",
diff --git a/packages/editor/public/index.html b/packages/editor/public/index.html
index 9c2cc0c1a..04c747a37 100644
--- a/packages/editor/public/index.html
+++ b/packages/editor/public/index.html
@@ -15,7 +15,7 @@
-
+
diff --git a/packages/editor/src/config/assets.js b/packages/editor/src/config/assets.js
new file mode 100644
index 000000000..5724f4742
--- /dev/null
+++ b/packages/editor/src/config/assets.js
@@ -0,0 +1,152 @@
+export default {
+ version: '1.0.0',
+ packages: {
+ '@alifd/next': {
+ title: 'fusion组件库',
+ package: '@alifd/next',
+ version: '1.19.18',
+ urls: [
+ 'https://unpkg.antfin-inc.com/@alife/next@1.19.18/dist/next.js',
+ 'https://unpkg.antfin-inc.com/@alife/next@1.19.18/dist/next.css'
+ ],
+ library: 'Next'
+ }
+ },
+ components: {
+ Button: {
+ componentName: 'Button',
+ title: '按钮',
+ devMode: 'proCode',
+ npm: {
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Button'
+ },
+ props: [
+ {
+ name: 'type',
+ propType: 'string'
+ },
+ {
+ name: 'children',
+ propType: 'string'
+ }
+ ],
+ configure: {
+ props: [
+ {
+ name: 'type',
+ setter: {
+ componentName: 'Input'
+ }
+ },
+ {
+ name: 'children',
+ setter: {
+ componentName: 'Input'
+ }
+ }
+ ]
+ }
+ },
+ Input: {
+ componentName: 'Input',
+ title: '输入框',
+ devMode: 'proCode',
+ npm: {
+ package: '@alifd/next',
+ version: '1.19.18',
+ destructuring: true,
+ exportName: 'Input'
+ },
+ props: [
+ {
+ name: 'placeholder',
+ propType: 'string'
+ }
+ ],
+ configure: {
+ props: [
+ {
+ name: 'placeholder',
+ setter: {
+ componentName: 'Input'
+ }
+ }
+ ]
+ }
+ }
+ },
+ componentList: [
+ {
+ title: '基础',
+ icon: '',
+ children: [
+ {
+ componentName: 'Button',
+ title: '按钮',
+ icon: '',
+ package: '@alife/next',
+ snippets: [
+ {
+ title: 'private',
+ screenshort: '',
+ schema: {
+ componentName: 'Button',
+ props: {
+ type: 'primary'
+ },
+ children: 'Primary'
+ }
+ },
+ {
+ title: 'secondary',
+ screenshort: '',
+ schema: {
+ componentName: 'Button',
+ props: {
+ type: 'secondary'
+ },
+ children: 'secondary'
+ }
+ },
+ {
+ title: 'normal',
+ screenshort: '',
+ schema: {
+ componentName: 'Button',
+ props: {
+ type: 'normal'
+ },
+ children: 'normal'
+ }
+ }
+ ]
+ }
+ ]
+ },
+ {
+ title: '表单',
+ icon: '',
+ children: [
+ {
+ componentName: 'Input',
+ title: '输入框',
+ icon: '',
+ package: '@alife/next',
+ snippets: [
+ {
+ title: '普通',
+ screenshort: '',
+ schema: {
+ componentName: 'Input',
+ props: {}
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ]
+};
diff --git a/packages/editor/src/config/components.js b/packages/editor/src/config/components.js
index 15edcae76..823238113 100644
--- a/packages/editor/src/config/components.js
+++ b/packages/editor/src/config/components.js
@@ -1,7 +1,3 @@
-import logo from '../plugins/logo';
-import Designer from '../plugins/designer';
-import undoRedo from '../plugins/undoRedo';
-import Settings from '../../../plugin-settings';
import topBalloonIcon from '@ali/iceluna-addon-2';
import topDialogIcon from '@ali/iceluna-addon-2';
import leftPanelIcon from '@ali/iceluna-addon-2';
@@ -12,6 +8,11 @@ import rightPanel1 from '@ali/iceluna-addon-2';
import rightPanel2 from '@ali/iceluna-addon-2';
import rightPanel3 from '@ali/iceluna-addon-2';
import rightPanel4 from '@ali/iceluna-addon-2';
+import componentList from '@ali/iceluna-addon-component-list';
+import Settings from '../../../plugin-settings';
+import undoRedo from '../plugins/undoRedo';
+import Designer from '../plugins/designer';
+import logo from '../plugins/logo';
import PluginFactory from '../framework/pluginFactory';
@@ -29,5 +30,6 @@ export default {
rightPanel1: PluginFactory(rightPanel1),
rightPanel2: PluginFactory(rightPanel2),
rightPanel3: PluginFactory(rightPanel3),
- rightPanel4: PluginFactory(rightPanel4)
+ rightPanel4: PluginFactory(rightPanel4),
+ componentList: PluginFactory(componentList)
};
diff --git a/packages/editor/src/config/setters.ts b/packages/editor/src/config/setters.ts
index 122792ca6..dc3ae6cb0 100644
--- a/packages/editor/src/config/setters.ts
+++ b/packages/editor/src/config/setters.ts
@@ -4,13 +4,17 @@ import { registerSetter } from '../../../plugin-settings/src';
import { createElement } from 'react';
registerSetter('ClassNameSetter', () => {
- return createElement('div', {
- className: 'lc-block-setter'
- }, '这里是类名绑定');
+ return createElement(
+ 'div',
+ {
+ className: 'lc-block-setter'
+ },
+ '这里是类名绑定'
+ );
});
registerSetter('EventsSetter', Input);
-registerSetter('StringSetter', { component: Input, props: { placeholder: "请输入" } });
+registerSetter('StringSetter', { component: Input, props: { placeholder: '请输入' } });
registerSetter('NumberSetter', NumberSetter as any);
diff --git a/packages/editor/src/config/skeleton.js b/packages/editor/src/config/skeleton.js
index 64826af1c..e633f3a47 100644
--- a/packages/editor/src/config/skeleton.js
+++ b/packages/editor/src/config/skeleton.js
@@ -1,3 +1,5 @@
+import assets from './assets';
+
export default {
version: '^1.0.2',
theme: {
@@ -25,7 +27,8 @@ export default {
version: '1.0.0'
},
pluginProps: {
- logo: 'https://img.alicdn.com/tfs/TB1mHYDxQP2gK0jSZPxXXacQpXa-112-64.png'
+ logo: 'https://img.alicdn.com/tfs/TB1hoI9x1H2gK0jSZFEXXcqMpXa-146-40.png',
+ href: '/'
}
},
{
@@ -71,7 +74,7 @@ export default {
type: 'Custom',
props: {
align: 'right',
- width: 90
+ width: 88
},
config: {
package: '@ali/lowcode-plugin-undo-redo',
@@ -107,8 +110,8 @@ export default {
align: 'right',
title: 'icon',
icon: 'dengpao',
- onClick: function(editor) {
- alert('icon addon invoke, current activeKey: ' + editor.activeKey);
+ onClick(editor) {
+ alert(`icon addon invoke, current activeKey: ${editor.activeKey}`);
}
},
config: {},
@@ -116,6 +119,22 @@ export default {
}
],
leftArea: [
+ {
+ pluginKey: 'componentList',
+ type: 'PanelIcon',
+ props: {
+ align: 'top',
+ icon: 'zujianku',
+ title: '组件库'
+ },
+ config: {
+ package: '@ali/iceluna-addon-component-list',
+ version: '^1.0.4'
+ },
+ pluginProps: {
+ disableAppComponent: true
+ }
+ },
{
pluginKey: 'leftPanelIcon',
type: 'PanelIcon',
@@ -150,7 +169,11 @@ export default {
props: {
align: 'top',
title: 'panel2',
- icon: 'dengpao'
+ icon: 'dengpao',
+ panelProps: {
+ defaultWidth: 400,
+ floatable: true
+ }
},
config: {
package: '@ali/iceluna-addon-2',
@@ -194,8 +217,8 @@ export default {
align: 'bottom',
title: 'icon',
icon: 'dengpao',
- onClick: function(editor) {
- alert('icon addon invoke, current activeKey: ' + editor.activeKey);
+ onClick(editor) {
+ alert(`icon addon invoke, current activeKey: ${editor.activeKey}`);
}
},
config: {},
@@ -211,7 +234,52 @@ export default {
version: '^1.0.0'
},
pluginProps: {}
- },
+ }
+ // {
+ // pluginKey: 'rightPanel1',
+ // type: 'TabPanel',
+ // props: {
+ // title: '样式'
+ // },
+ // config: {
+ // version: '^1.0.0'
+ // },
+ // pluginProps: {}
+ // },
+ // {
+ // pluginKey: 'rightPanel2',
+ // type: 'TabPanel',
+ // props: {
+ // title: '属性',
+ // icon: 'dengpao'
+ // },
+ // config: {
+ // version: '^1.0.0'
+ // },
+ // pluginProps: {}
+ // },
+ // {
+ // pluginKey: 'rightPanel3',
+ // type: 'TabPanel',
+ // props: {
+ // title: '事件'
+ // },
+ // config: {
+ // version: '^1.0.0'
+ // },
+ // pluginProps: {}
+ // },
+ // {
+ // pluginKey: 'rightPanel4',
+ // type: 'TabPanel',
+ // props: {
+ // title: '数据'
+ // },
+ // config: {
+ // version: '^1.0.0'
+ // },
+ // pluginProps: {}
+ // }
],
centerArea: [
{
@@ -224,5 +292,51 @@ export default {
]
},
hooks: [],
- shortCuts: []
+ shortCuts: [],
+ lifeCycles: {
+ init: function init(editor) {
+ const transformMaterial = componentList => {
+ return componentList.map(category => {
+ return {
+ name: category.title,
+ items: category.children.map(comp => {
+ return {
+ ...comp,
+ name: comp.componentName,
+ libraryId: 1,
+ snippets: comp.snippets.map(snippet => {
+ return {
+ name: snippet.title,
+ screenshort: snippet.screenshort,
+ code: JSON.stringify(snippet.schema)
+ };
+ })
+ };
+ })
+ };
+ });
+ };
+
+ const list = transformMaterial(assets.componentList);
+ editor.set({
+ componentsMap: assets.components,
+ componentMaterial: {
+ library: [
+ {
+ name: 'Fusion组件库',
+ id: 1
+ }
+ ],
+ list
+ }
+ });
+
+ editor.set('dndHelper', {
+ handleResourceDragStart: function(ev, tagName, schema) {
+ // 物料面板中组件snippet的dragStart回调
+ // ev: 原始的domEvent;tagName: 组件的描述文案;schema: snippet的schema
+ }
+ });
+ }
+ }
};
diff --git a/packages/editor/src/framework/areaManager.ts b/packages/editor/src/framework/areaManager.ts
index ac7f1ed3c..28f24eb0e 100644
--- a/packages/editor/src/framework/areaManager.ts
+++ b/packages/editor/src/framework/areaManager.ts
@@ -4,26 +4,36 @@ import { clone, deepEqual } from './utils';
export default class AreaManager {
private pluginStatus: PluginStatus;
+
private config: PluginConfig[];
- constructor(private editor: Editor, private area: string) {
+
+ private editor: Editor;
+
+ private area: string;
+
+ constructor(editor: Editor, area: string) {
+ this.editor = editor;
+ this.area = area;
this.config = (editor && editor.config && editor.config.plugins && editor.config.plugins[this.area]) || [];
this.pluginStatus = clone(editor.pluginStatus);
}
public isPluginStatusUpdate(pluginType?: string): boolean {
const { pluginStatus } = this.editor;
- const list = pluginType ? this.config.filter(item => item.type === pluginType) : this.config;
+ const list = pluginType ? this.config.filter((item): boolean => item.type === pluginType) : this.config;
- const isUpdate = list.some(item => !deepEqual(pluginStatus[item.pluginKey], this.pluginStatus[item.pluginKey]));
+ const isUpdate = list.some(
+ (item): boolean => !deepEqual(pluginStatus[item.pluginKey], this.pluginStatus[item.pluginKey])
+ );
this.pluginStatus = clone(pluginStatus);
return isUpdate;
}
public getVisiblePluginList(pluginType?: string): PluginConfig[] {
- const res = this.config.filter(item => {
- return !this.pluginStatus[item.pluginKey] || this.pluginStatus[item.pluginKey].visible;
+ const res = this.config.filter((item): boolean => {
+ return !!(!this.pluginStatus[item.pluginKey] || this.pluginStatus[item.pluginKey].visible);
});
- return pluginType ? res.filter(item => item.type === pluginType) : res;
+ return pluginType ? res.filter((item): boolean => item.type === pluginType) : res;
}
public getPluginConfig(): PluginConfig[] {
diff --git a/packages/editor/src/framework/context.ts b/packages/editor/src/framework/context.ts
index 78d3ce177..859b79dc4 100644
--- a/packages/editor/src/framework/context.ts
+++ b/packages/editor/src/framework/context.ts
@@ -1,3 +1,4 @@
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
index 968a1935e..6bc6d2653 100644
--- a/packages/editor/src/framework/definitions.ts
+++ b/packages/editor/src/framework/definitions.ts
@@ -9,7 +9,7 @@ export interface EditorConfig {
shortCuts?: ShortCutsConfig;
utils?: UtilsConfig;
constants?: ConstantsConfig;
- lifeCycles?: lifeCyclesConfig;
+ lifeCycles?: LifeCyclesConfig;
i18n?: I18nConfig;
}
@@ -38,7 +38,7 @@ export interface ThemeConfig {
}
export interface PluginsConfig {
- [propName: string]: Array;
+ [propName: string]: PluginConfig[];
}
export interface PluginConfig {
@@ -63,7 +63,7 @@ export interface PluginConfig {
pluginProps?: object;
}
-export type HooksConfig = Array;
+export type HooksConfig = HookConfig[];
export interface HookConfig {
message: string;
@@ -71,14 +71,14 @@ export interface HookConfig {
handler: (editor: Editor, ...args) => void;
}
-export type ShortCutsConfig = Array;
+export type ShortCutsConfig = ShortCutConfig[];
export interface ShortCutConfig {
keyboard: string;
- handler: (editor: Editor, ev: React.KeyboardEventHandler, keymaster: any) => void;
+ handler: (editor: Editor, ev: Event, keymaster: any) => void;
}
-export type UtilsConfig = Array;
+export type UtilsConfig = UtilConfig[];
export interface UtilConfig {
name: string;
@@ -88,7 +88,7 @@ export interface UtilConfig {
export type ConstantsConfig = object;
-export interface lifeCyclesConfig {
+export interface LifeCyclesConfig {
init?: (editor: Editor) => any;
destroy?: (editor: Editor) => any;
}
@@ -96,7 +96,7 @@ export interface lifeCyclesConfig {
export type LocaleType = 'zh-CN' | 'zh-TW' | 'en-US' | 'ja-JP';
export interface I18nMessages {
- [propName: string]: string;
+ [key: string]: string;
}
export interface I18nConfig {
@@ -109,27 +109,46 @@ export interface I18nConfig {
export type I18nFunction = (key: string, params: any) => string;
export interface Utils {
- [propName: string]: (...args) => any;
+ [key: string]: (...args) => any;
}
-export interface PluginClass extends React.ComponentClass<{
+export interface PluginProps {
editor: Editor;
- [key: string]: any
-}> {
- init?: (editor: Editor) => void;
- open?: () => any;
- close?: () => any;
+ config: PluginConfig;
+ i18n?: I18nFunction;
+ ref?: React.RefObject;
+ [key: string]: any;
}
-export interface PluginComponents {
- [propName: string]: PluginClass;
+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 {
- [propName: string]: {
- disabled?: boolean;
- visible?: boolean;
- marked?: boolean;
- locked?: boolean;
- };
+ 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
index 9fe46806a..cf1147e2c 100644
--- a/packages/editor/src/framework/editor.ts
+++ b/packages/editor/src/framework/editor.ts
@@ -1,35 +1,56 @@
import Debug from 'debug';
import EventEmitter from 'events';
import store from 'store';
-import { EditorConfig, HooksConfig, LocaleType, PluginComponents, PluginStatus, Utils } from './definitions';
+import {
+ EditorConfig,
+ HooksConfig,
+ LocaleType,
+ PluginStatusSet,
+ Utils,
+ PluginClassSet,
+ PluginSet
+} from './definitions';
-import { registShortCuts, transformToPromise, unRegistShortCuts } from './utils';
+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);
+// 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;
}
const msg = '您确定要离开此页面吗?';
- e.cancelBubble = true;
- e.returnValue = msg;
+ ev.cancelBubble = true;
+ ev.returnValue = true;
if (e.stopPropagation) {
e.stopPropagation();
e.preventDefault();
@@ -47,26 +68,40 @@ export interface HooksFuncs {
}
export default class Editor extends EventEmitter {
- public static getInstance = (config: EditorConfig, components: PluginComponents, utils?: Utils): Editor => {
+ public static getInstance = (config: EditorConfig, components: PluginClassSet, utils?: Utils): Editor => {
if (!instance) {
instance = new Editor(config, components, utils);
}
return instance;
};
- public pluginStatus: PluginStatus;
- public plugins: PluginComponents;
+ 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(public config: EditorConfig, public components: PluginComponents, public utils?: Utils) {
+ constructor(config: EditorConfig, components: PluginClassSet, utils?: Utils) {
super();
+ this.config = config;
+ this.components = components;
+ this.utils = { ...editorUtils, ...utils };
instance = this;
this.init();
}
@@ -80,21 +115,21 @@ export default class Editor extends EventEmitter {
this.initHooks(hooks || []);
this.emit('editor.beforeInit');
- const init = (lifeCycles && lifeCycles.init) || (() => {});
+ const init = (lifeCycles && lifeCycles.init) || ((): void => {});
// 用户可以通过设置extensions.init自定义初始化流程;
return transformToPromise(init(this))
- .then(() => {
+ .then((): boolean => {
// 注册快捷键
registShortCuts(shortCuts, this);
this.emit('editor.afterInit');
return true;
})
- .catch(err => {
+ .catch((err): void => {
console.error(err);
});
}
- public destroy() {
+ public destroy(): void {
debug('destroy');
try {
const { hooks = [], shortCuts = [], lifeCycles = {} } = this.config;
@@ -105,7 +140,6 @@ export default class Editor extends EventEmitter {
}
} catch (err) {
console.warn(err);
- return;
}
}
@@ -121,7 +155,7 @@ export default class Editor extends EventEmitter {
}
this[key] = val;
} else if (typeof key === 'object') {
- Object.keys(key).forEach(item => {
+ Object.keys(key).forEach((item): void => {
this[item] = key[item];
});
}
@@ -131,26 +165,26 @@ export default class Editor extends EventEmitter {
if (!Array.isArray(events)) {
return;
}
- events.forEach(event => this.on(event, lisenter));
+ events.forEach((event): void => this.on(event, lisenter));
}
public batchOnce(events: string[], lisenter: (...args) => void): void {
if (!Array.isArray(events)) {
return;
}
- events.forEach(event => this.once(event, lisenter));
+ events.forEach((event): void => this.once(event, lisenter));
}
public batchOff(events: string[], lisenter: (...args) => void): void {
if (!Array.isArray(events)) {
return;
}
- events.forEach(event => this.off(event, lisenter));
+ events.forEach((event): void => this.off(event, lisenter));
}
// 销毁hooks中的消息监听
- private destroyHooks(hooks: HooksConfig = []) {
- hooks.forEach((item, idx) => {
+ private destroyHooks(hooks: HooksConfig = []): void {
+ hooks.forEach((item, idx): void => {
if (typeof this.hooksFuncs[idx] === 'function') {
this.off(item.message, this.hooksFuncs[idx]);
}
@@ -160,8 +194,8 @@ export default class Editor extends EventEmitter {
// 初始化hooks中的消息监听
private initHooks(hooks: HooksConfig = []): void {
- this.hooksFuncs = hooks.map(item => {
- const func = (...args) => {
+ this.hooksFuncs = hooks.map((item): ((...arg) => void) => {
+ const func = (...args): void => {
item.handler(this, ...args);
};
this[item.type](item.message, func);
@@ -169,12 +203,12 @@ export default class Editor extends EventEmitter {
});
}
- private initPluginStatus() {
+ private initPluginStatus(): PluginStatusSet {
const { plugins = {} } = this.config;
const pluginAreas = Object.keys(plugins);
- const res: PluginStatus = {};
- pluginAreas.forEach(area => {
- (plugins[area] || []).forEach(plugin => {
+ const res: PluginStatusSet = {};
+ pluginAreas.forEach((area): void => {
+ (plugins[area] || []).forEach((plugin): void => {
if (plugin.type === 'Divider') {
return;
}
diff --git a/packages/editor/src/framework/index.ts b/packages/editor/src/framework/index.ts
index be8b55d0a..72af6e859 100644
--- a/packages/editor/src/framework/index.ts
+++ b/packages/editor/src/framework/index.ts
@@ -1,9 +1,11 @@
import Editor from './editor';
-export { default as PluginFactory } from './pluginFactory';
-export { default as EditorContext } from './context';
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;
diff --git a/packages/editor/src/framework/pluginFactory.tsx b/packages/editor/src/framework/pluginFactory.tsx
index dea2cd602..f32edd9e9 100644
--- a/packages/editor/src/framework/pluginFactory.tsx
+++ b/packages/editor/src/framework/pluginFactory.tsx
@@ -1,32 +1,24 @@
import React, { createRef, PureComponent } from 'react';
import EditorContext from './context';
-import { I18nFunction, PluginConfig } from './definitions';
+import { I18nFunction, PluginProps, PluginClass, Plugin } from './definitions';
import Editor from './editor';
import { acceptsRef, generateI18n, isEmpty, transformToPromise } from './utils';
-export interface PluginProps {
- editor: Editor;
- config: PluginConfig;
-}
-
-export interface InjectedPluginProps {
- i18n?: I18nFunction;
-}
-
-export default function pluginFactory(
- Comp: React.ComponentType
-): React.ComponentType {
+export default function pluginFactory(Comp: PluginClass): React.ComponentType {
class LowcodePlugin extends PureComponent {
public static displayName = 'LowcodeEditorPlugin';
- public static defaultProps = {
- config: {}
- };
+
public static contextType = EditorContext;
+
public static init = Comp.init;
- public ref = createRef();
+
+ public ref: React.RefObject & Plugin;
+
private editor: Editor;
+
private pluginKey: string;
+
private i18n: I18nFunction;
constructor(props, context) {
@@ -36,6 +28,7 @@ export default function pluginFactory(
return;
}
const { locale, messages, editor } = props;
+ this.ref = createRef();
// 注册插件
this.editor = editor;
this.i18n = generateI18n(locale, messages);
@@ -46,7 +39,7 @@ export default function pluginFactory(
});
}
- public componentWillUnmount() {
+ public componentWillUnmount(): void {
// 销毁插件
if (this.editor && this.editor.plugins) {
delete this.editor.plugins[this.pluginKey];
@@ -60,14 +53,14 @@ export default function pluginFactory(
return Promise.resolve();
};
- public close = () => {
+ public close = (): Promise => {
if (this.ref && this.ref.close && typeof this.ref.close === 'function') {
return transformToPromise(this.ref.close());
}
return Promise.resolve();
};
- public render() {
+ public render(): React.ReactNode {
const { config } = this.props;
const props = {
i18n: this.i18n,
diff --git a/packages/editor/src/framework/utils.ts b/packages/editor/src/framework/utils.ts
index 794b0c9d3..aced7d318 100644
--- a/packages/editor/src/framework/utils.ts
+++ b/packages/editor/src/framework/utils.ts
@@ -1,7 +1,5 @@
import IntlMessageFormat from 'intl-messageformat';
import keymaster from 'keymaster';
-import { EditorConfig, I18nFunction, I18nMessages, LocaleType, ShortCutsConfig } from './definitions';
-import Editor from './editor';
import _clone from 'lodash/cloneDeep';
import _debounce from 'lodash/debounce';
@@ -10,6 +8,10 @@ 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;
@@ -17,7 +19,6 @@ export const isEmpty = _isEmpty;
export const throttle = _throttle;
export const debounce = _debounce;
-import _serialize from 'serialize-javascript';
export const serialize = _serialize;
const ENV = {
@@ -27,6 +28,17 @@ const ENV = {
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: {
@@ -57,7 +69,7 @@ export function serializeParams(obj: object): string {
return '';
}
const res: string[] = [];
- Object.entries(obj).forEach(([key, val]) => {
+ Object.entries(obj).forEach(([key, val]): void => {
if (val === null || val === undefined || val === '') {
return;
}
@@ -115,8 +127,8 @@ export function getEnv(): string {
// 注册快捷键
export function registShortCuts(config: ShortCutsConfig, editor: Editor): void {
- (config || []).forEach(item => {
- keymaster(item.keyboard, ev => {
+ (config || []).forEach((item): void => {
+ keymaster(item.keyboard, (ev: Event): void => {
ev.preventDefault();
item.handler(editor, ev, keymaster);
});
@@ -125,7 +137,7 @@ export function registShortCuts(config: ShortCutsConfig, editor: Editor): void {
// 取消注册快捷
export function unRegistShortCuts(config: ShortCutsConfig): void {
- (config || []).forEach(item => {
+ (config || []).forEach((item): void => {
keymaster.unbind(item.keyboard);
});
if (window.parent.vscode) {
@@ -141,7 +153,7 @@ export function transformToPromise(input: any): Promise<{}> {
if (input instanceof Promise) {
return input;
}
- return new Promise((resolve, reject) => {
+ return new Promise((resolve, reject): void => {
if (input || input === undefined) {
resolve();
} else {
@@ -161,7 +173,7 @@ export function transformArrayToMap(arr: T[], key: string, overwrite: boolean
return {};
}
const res = {};
- arr.forEach(item => {
+ arr.forEach((item): void => {
const curKey = item[key];
if (item[key] === undefined) {
return;
@@ -187,7 +199,7 @@ export function parseSearch(search: string): Query {
const str = search.replace(/^\?/, '');
const paramStr = str.split('&');
const res = {};
- paramStr.forEach(item => {
+ paramStr.forEach((item): void => {
const regRes = item.split('=');
if (regRes[0] && regRes[1]) {
res[regRes[0]] = decodeURIComponent(regRes[1]);
@@ -210,7 +222,7 @@ export function comboEditorConfig(defaultConfig: EditorConfig = {}, customConfig
const customShortCuts = transformArrayToMap(shortCuts || [], 'keyboard');
const localeList = ['zh-CN', 'zh-TW', 'en-US', 'ja-JP'];
const i18nConfig = {};
- localeList.forEach(key => {
+ localeList.forEach((key): void => {
i18nConfig[key] = {
...(defaultConfig.i18n && defaultConfig.i18n[key]),
...(i18n && i18n[key])
@@ -248,9 +260,12 @@ export function comboEditorConfig(defaultConfig: EditorConfig = {}, customConfig
* 判断当前组件是否能够设置ref
* @param {*} Comp 需要判断的组件
*/
-export function acceptsRef(Comp: React.ComponentType) {
+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.tsx b/packages/editor/src/index.tsx
index f93e9b0e5..22e76df35 100644
--- a/packages/editor/src/index.tsx
+++ b/packages/editor/src/index.tsx
@@ -10,12 +10,9 @@ import constants from './config/constants';
import './config/locale';
import './config/setters';
-import pkg from '../package.json';
import './global.scss';
import './config/theme.scss';
-(window as any).__pkg = pkg;
-
const ICE_CONTAINER = document.getElementById('ice-container');
if (!ICE_CONTAINER) {
@@ -26,7 +23,7 @@ ReactDOM.render(
(
+ component={(props): React.ReactNode => (
{
- static displayName: 'LowcodePluginDesigner';
+ displayName: 'LowcodePluginDesigner';
- constructor(props) {
- super(props);
- }
+ handleDesignerMount = (designer): void => {
+ const { editor } = this.props;
+ editor.set('designer', designer);
+ editor.emit('designer.ready', designer);
+ };
- render() {
+ render(): React.ReactNode {
const { editor } = this.props;
return (
= (props): React.ReactElement => {
return (
);
-}
+};
+
+export default Logo;
diff --git a/packages/editor/src/plugins/undoRedo/index.tsx b/packages/editor/src/plugins/undoRedo/index.tsx
index d5af0badf..0def89cfe 100644
--- a/packages/editor/src/plugins/undoRedo/index.tsx
+++ b/packages/editor/src/plugins/undoRedo/index.tsx
@@ -1,22 +1,75 @@
-import React, { useState } from 'react';
+import React, { PureComponent } from 'react';
import './index.scss';
-import Editor from '../../framework/index';
-import { PluginConfig } from '../../framework/definitions';
+import { PluginProps } from '../../framework/definitions';
import TopIcon from '../../skeleton/components/TopIcon/index';
-export interface PluginProps {
- editor: Editor;
- config: PluginConfig;
+export interface IProps {
logo?: string;
}
-export default function(props: PluginProps) {
- const [backEnable, setBackEnable] = useState(true);
- const [forwardEnable, setForwardEnable] = useState(true);
- return (
-
-
-
-
- );
+export interface IState {
+ undoEnable: boolean;
+ redoEnable: boolean;
+}
+
+export default class UndoRedo extends PureComponent {
+ public static display = 'LowcodeUndoRedo';
+
+ private history: any;
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ undoEnable: false,
+ redoEnable: false
+ };
+ if (props.editor.designer) {
+ this.init();
+ } else {
+ props.editor.on('designer.ready', (): void => {
+ this.init();
+ });
+ }
+ }
+
+ init = (): void => {
+ const { editor } = this.props;
+ this.history = editor.designer.currentHistory;
+ this.updateState(this.history.getState());
+ editor.on('designer.history-change', (history): void => {
+ this.history = history;
+ this.history.onStateChange(this.updateState);
+ });
+ this.history.onStateChange(this.updateState);
+ };
+
+ updateState = (state: number): void => {
+ console.log('++++', !!(state & 1), !!(state & 2));
+ this.setState({
+ undoEnable: !!(state & 1),
+ redoEnable: !!(state & 2)
+ });
+ };
+
+ handleUndoClick = (): void => {
+ if (this.history) {
+ this.history.back();
+ }
+ };
+
+ handleRedoClick = (): void => {
+ if (this.history) {
+ this.history.forward();
+ }
+ };
+
+ render(): React.ReactNode {
+ const { undoEnable, redoEnable } = this.state;
+ return (
+
+
+
+
+ );
+ }
}
diff --git a/packages/editor/src/skeleton/components/LeftPlugin/index.scss b/packages/editor/src/skeleton/components/LeftPlugin/index.scss
index 06b6ef63a..f1e7876d3 100644
--- a/packages/editor/src/skeleton/components/LeftPlugin/index.scss
+++ b/packages/editor/src/skeleton/components/LeftPlugin/index.scss
@@ -1,34 +1,16 @@
.lowcode-left-plugin {
- font-size: 16px;
+ 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);
@@ -41,8 +23,8 @@
white-space: nowrap;
padding: 6px 8px;
border-radius: 4px;
- background: rgba(0, 0, 0, 0.75);
- color: #fff;
+ background: $balloon-tooltip-color-bg;
+ color: $color-text1-3;
z-index: 100;
}
&:after {
@@ -52,8 +34,21 @@
left: 40px;
top: 15px;
border: 5px solid transparent;
- border-right-color: rgba(0, 0, 0, 0.75);
+ 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
index 6d807548c..eafe894ee 100644
--- a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx
+++ b/packages/editor/src/skeleton/components/LeftPlugin/index.tsx
@@ -30,7 +30,7 @@ export default class LeftPlugin extends PureComponent {}
+ onClick: (): void => {}
};
constructor(props, context) {
@@ -40,7 +40,7 @@ export default class LeftPlugin extends PureComponent {
+ handleClose = (): void => {
const { config, editor } = this.props;
const pluginKey = config && config.pluginKey;
const plugin = editor.plugins && editor.plugins[pluginKey];
- if (plugin) {
- plugin.close().then(() => {
+ if (plugin && plugin.close) {
+ plugin.close().then((): void => {
this.setState({
dialogVisible: false
});
@@ -71,24 +71,26 @@ export default class LeftPlugin extends PureComponent {
+ handleOpen = (): void => {
// todo 对话框类型的插件初始时拿不到插件实例
this.setState({
dialogVisible: true
});
};
- handleShow = () => {
+ handleShow = (): void => {
const { disabled, config, onClick, editor } = this.props;
const pluginKey = config && config.pluginKey;
if (disabled || !pluginKey) return;
- //考虑到弹窗情况,延时发送消息
- setTimeout(() => editor.emit(`${pluginKey}.addon.activate`), 0);
+ // 考虑到弹窗情况,延时发送消息
+ setTimeout((): void => editor.emit(`${pluginKey}.plugin.activate`), 0);
this.handleOpen();
- onClick && onClick();
+ if (onClick) {
+ onClick();
+ }
};
- renderIcon = clickCallback => {
+ renderIcon = (clickCallback): React.ReactNode => {
const { active, disabled, marked, locked, onClick, config } = this.props;
const { pluginKey, props } = config || {};
const { icon, title } = props || {};
@@ -100,10 +102,11 @@ export default class LeftPlugin extends PureComponent {
+ onClick={(): void => {
if (disabled) return;
- //考虑到弹窗情况,延时发送消息
+ // 考虑到弹窗情况,延时发送消息
clickCallback && clickCallback();
+
onClick && onClick();
}}
>
@@ -118,7 +121,7 @@ export default class LeftPlugin extends PureComponent {
+ onClick={(): void => {
onClick && onClick.call(null, editor);
}}
{...pluginProps}
@@ -145,30 +148,34 @@ export default class LeftPlugin extends PureComponent
- {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();
})}