daily tag

This commit is contained in:
下羊 2020-03-09 15:58:46 +08:00
parent c9d139bf30
commit baa6adb199
27 changed files with 519 additions and 484 deletions

View File

@ -0,0 +1,4 @@
{
"printWidth": 120,
"singleQuote": true
}

View File

@ -8,15 +8,21 @@
"@icedesign/theme": "^1.x", "@icedesign/theme": "^1.x",
"@types/react": "^16.8.3", "@types/react": "^16.8.3",
"@types/react-dom": "^16.8.2", "@types/react-dom": "^16.8.2",
"events": "^3.1.0",
"intl-messageformat": "^8.2.1",
"keymaster": "^1.6.2", "keymaster": "^1.6.2",
"moment": "^2.23.0", "moment": "^2.23.0",
"prop-types": "^15.5.8", "prop-types": "^15.5.8",
"react": "^16.4.1", "react": "^16.8.1",
"react-dom": "^16.4.1", "react-dom": "^16.8.1",
"react-router-dom": "^5.1.2" "react-router-dom": "^5.1.2",
"store": "^2.0.12"
}, },
"devDependencies": { "devDependencies": {
"@ice/spec": "^0.1.1", "@ice/spec": "^0.1.1",
"@types/debug": "^4.1.5",
"@types/events": "^3.0.0",
"@types/store": "^2.0.2",
"css-modules-typescript-loader": "^2.0.4", "css-modules-typescript-loader": "^2.0.4",
"eslint": "^6.0.1", "eslint": "^6.0.1",
"ice-plugin-fusion": "^0.1.4", "ice-plugin-fusion": "^0.1.4",

View File

@ -1,4 +1,3 @@
import topBalloonIcon from '@ali/iceluna-addon-2'; import topBalloonIcon from '@ali/iceluna-addon-2';
import topDialogIcon from '@ali/iceluna-addon-2'; import topDialogIcon from '@ali/iceluna-addon-2';
import leftPanelIcon from '@ali/iceluna-addon-2'; import leftPanelIcon from '@ali/iceluna-addon-2';
@ -15,10 +14,10 @@ export default {
topBalloonIcon: PluginFactory(topBalloonIcon), topBalloonIcon: PluginFactory(topBalloonIcon),
topDialogIcon: PluginFactory(topDialogIcon), topDialogIcon: PluginFactory(topDialogIcon),
leftPanelIcon: PluginFactory(leftPanelIcon), leftPanelIcon: PluginFactory(leftPanelIcon),
leftBalloonIcon:PluginFactory(leftBalloonIcon), leftBalloonIcon: PluginFactory(leftBalloonIcon),
leftDialogIcon:PluginFactory(leftDialogIcon), leftDialogIcon: PluginFactory(leftDialogIcon),
rightPanel1:PluginFactory(rightPanel1), rightPanel1: PluginFactory(rightPanel1),
rightPanel2:PluginFactory(rightPanel2), rightPanel2: PluginFactory(rightPanel2),
rightPanel3: PluginFactory(rightPanel3), rightPanel3: PluginFactory(rightPanel3),
rightPanel4: PluginFactory(rightPanel4), rightPanel4: PluginFactory(rightPanel4)
}; };

View File

@ -1,3 +1,3 @@
export default { export default {
namespace: 'page', namespace: 'page'
}; };

View File

@ -6,5 +6,5 @@ export default {
'en-US': en_us, 'en-US': en_us,
'zh-CN': zh_cn, 'zh-CN': zh_cn,
'zh-TW': zh_tw, 'zh-TW': zh_tw,
'ja-JP': ja_jp, 'ja-JP': ja_jp
}; };

View File

@ -3,12 +3,12 @@ export default {
theme: { theme: {
dpl: { dpl: {
package: '@alife/dpl-iceluna', package: '@alife/dpl-iceluna',
version: '^2.3.0', version: '^2.3.0'
}, },
scss: '', scss: ''
}, },
constants: { constants: {
namespace: 'page', namespace: 'page'
}, },
utils: [], utils: [],
plugins: { plugins: {
@ -21,21 +21,21 @@ export default {
title: 'balloon', title: 'balloon',
icon: 'dengpao', icon: 'dengpao',
balloonProps: { balloonProps: {
triggerType: 'click', triggerType: 'click'
}, }
}, },
config: { config: {
package: '@ali/iceluna-addon-2', package: '@ali/iceluna-addon-2',
version: '^1.0.0', version: '^1.0.0'
}, },
pluginProps: {}, pluginProps: {}
}, },
{ {
pluginKey: 'divider', pluginKey: 'divider',
type: 'Divider', type: 'Divider',
props: { props: {
align: 'left', align: 'left'
}, }
}, },
{ {
pluginKey: 'topDialogIcon', pluginKey: 'topDialogIcon',
@ -43,13 +43,13 @@ export default {
props: { props: {
align: 'left', align: 'left',
title: 'dialog', title: 'dialog',
icon: 'dengpao', icon: 'dengpao'
}, },
config: { config: {
package: '@ali/iceluna-addon-2', package: '@ali/iceluna-addon-2',
version: '^1.0.0', version: '^1.0.0'
}, },
pluginProps: {}, pluginProps: {}
}, },
{ {
pluginKey: 'topLinkIcon', pluginKey: 'topLinkIcon',
@ -60,11 +60,11 @@ export default {
icon: 'dengpao', icon: 'dengpao',
linkProps: { linkProps: {
href: '//www.taobao.com', href: '//www.taobao.com',
target: 'blank', target: 'blank'
}, }
}, },
config: {}, config: {},
pluginProps: {}, pluginProps: {}
}, },
{ {
pluginKey: 'topIcon', pluginKey: 'topIcon',
@ -75,11 +75,11 @@ export default {
icon: 'dengpao', icon: 'dengpao',
onClick: function(editor) { onClick: function(editor) {
alert('icon addon invoke, current activeKey: ' + editor.activeKey); alert('icon addon invoke, current activeKey: ' + editor.activeKey);
}, }
}, },
config: {}, config: {},
pluginProps: {}, pluginProps: {}
}, }
], ],
leftArea: [ leftArea: [
{ {
@ -88,13 +88,13 @@ export default {
props: { props: {
align: 'top', align: 'top',
title: 'panel', title: 'panel',
icon: 'dengpao', icon: 'dengpao'
}, },
config: { config: {
package: '@ali/iceluna-addon-2', package: '@ali/iceluna-addon-2',
version: '^1.0.0', version: '^1.0.0'
}, },
pluginProps: {}, pluginProps: {}
}, },
{ {
pluginKey: 'leftBalloonIcon', pluginKey: 'leftBalloonIcon',
@ -102,13 +102,13 @@ export default {
props: { props: {
align: 'top', align: 'top',
title: 'balloon', title: 'balloon',
icon: 'dengpao', icon: 'dengpao'
}, },
config: { config: {
package: '@ali/iceluna-addon-2', package: '@ali/iceluna-addon-2',
version: '^1.0.0', version: '^1.0.0'
}, },
pluginProps: {}, pluginProps: {}
}, },
{ {
pluginKey: 'leftDialogIcon', pluginKey: 'leftDialogIcon',
@ -116,13 +116,13 @@ export default {
props: { props: {
align: 'bottom', align: 'bottom',
title: 'dialog', title: 'dialog',
icon: 'dengpao', icon: 'dengpao'
}, },
config: { config: {
package: '@ali/iceluna-addon-2', package: '@ali/iceluna-addon-2',
version: '^1.0.0', version: '^1.0.0'
}, },
pluginProps: {}, pluginProps: {}
}, },
{ {
pluginKey: 'leftLinkIcon', pluginKey: 'leftLinkIcon',
@ -133,11 +133,11 @@ export default {
icon: 'dengpao', icon: 'dengpao',
linkProps: { linkProps: {
href: '//www.taobao.com', href: '//www.taobao.com',
target: 'blank', target: 'blank'
}, }
}, },
config: {}, config: {},
pluginProps: {}, pluginProps: {}
}, },
{ {
pluginKey: 'leftIcon', pluginKey: 'leftIcon',
@ -148,11 +148,11 @@ export default {
icon: 'dengpao', icon: 'dengpao',
onClick: function(editor) { onClick: function(editor) {
alert('icon addon invoke, current activeKey: ' + editor.activeKey); alert('icon addon invoke, current activeKey: ' + editor.activeKey);
}, }
}, },
config: {}, config: {},
pluginProps: {}, pluginProps: {}
}, }
], ],
rightArea: [ rightArea: [
{ {
@ -160,56 +160,56 @@ export default {
type: 'Panel', type: 'Panel',
props: { props: {
title: 'panel1', title: 'panel1',
icon: 'dengpao', icon: 'dengpao'
}, },
config: { config: {
package: '@ali/iceluna-addon-2', package: '@ali/iceluna-addon-2',
version: '^1.0.0', version: '^1.0.0'
}, },
pluginProps: {}, pluginProps: {}
}, },
{ {
pluginKey: 'rightPanel2', pluginKey: 'rightPanel2',
type: 'Panel', type: 'Panel',
props: { props: {
title: 'panel2', title: 'panel2',
icon: 'dengpao', icon: 'dengpao'
}, },
config: { config: {
package: '@ali/iceluna-addon-2', package: '@ali/iceluna-addon-2',
version: '^1.0.0', version: '^1.0.0'
}, },
pluginProps: {}, pluginProps: {}
}, },
{ {
pluginKey: 'rightPanel3', pluginKey: 'rightPanel3',
type: 'Panel', type: 'Panel',
props: { props: {
title: 'panel3', title: 'panel3',
icon: 'dengpao', icon: 'dengpao'
}, },
config: { config: {
package: '@ali/iceluna-addon-2', package: '@ali/iceluna-addon-2',
version: '^1.0.0', version: '^1.0.0'
}, },
pluginProps: {}, pluginProps: {}
}, },
{ {
pluginKey: 'rightPanel4', pluginKey: 'rightPanel4',
type: 'Panel', type: 'Panel',
props: { props: {
title: 'panel4', title: 'panel4',
icon: 'dengpao', icon: 'dengpao'
}, },
config: { config: {
package: '@ali/iceluna-addon-2', package: '@ali/iceluna-addon-2',
version: '^1.0.0', version: '^1.0.0'
}, },
pluginProps: {}, pluginProps: {}
}, }
], ],
centerArea: [], centerArea: []
}, },
hooks: [], hooks: [],
shortCuts: [], shortCuts: []
}; };

View File

@ -1,4 +1,17 @@
export interface EditorConfig {} import * as React from 'react';
import Editor from './editor';
export interface EditorConfig {
skeleton?: SkeletonConfig;
theme?: ThemeConfig;
plugins?: PluginsConfig;
hooks?: HooksConfig;
shortCuts?: ShortCutsConfig;
utils?: UtilsConfig;
constants?: ConstantsConfig;
lifeCycles?: lifeCyclesConfig;
i18n?: I18nConfig;
}
export interface NpmConfig { export interface NpmConfig {
version: string; version: string;
@ -25,17 +38,92 @@ export interface ThemeConfig {
} }
export interface PluginsConfig { export interface PluginsConfig {
[key]: Array<PluginConfig>; [propName: string]: Array<PluginConfig>;
} }
export interface PluginConfig { export interface PluginConfig {
pluginKey: string; pluginKey: string;
type: string; type: string;
props: object; props: {
config: NpmConfig; icon?: string;
pluginProps: object; 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<HookConfig>; export type HooksConfig = Array<HookConfig>;
export interface HookConfig {} export interface HookConfig {
message: string;
type: 'on' | 'once';
handler: (editor: Editor, ...args) => void;
}
export type ShortCutsConfig = Array<ShortCutConfig>;
export interface ShortCutConfig {
keyboard: string;
handler: (editor: Editor, ev: React.KeyboardEventHandler<HTMLElement>, keymaster: any) => void;
}
export type UtilsConfig = Array<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 {
[propName: string]: string;
}
export interface I18nConfig {
'zh-CN'?: I18nMessages;
'zh-TW'?: I18nMessages;
'en-US'?: I18nMessages;
'ja-JP'?: I18nMessages;
}
export type I18nFunction = (key: string, params: object) => string;
export interface Utils {
[propName: string]: (...args) => any;
}
export interface PluginClass extends React.Component {
init?: (editor: Editor) => void;
}
export interface PluginComponents {
[propName: string]: PluginClass;
}
export interface PluginStatus {
[propName: string]: {
disabled: boolean;
visible: boolean;
marked: boolean;
};
}

View File

@ -1,13 +1,9 @@
import EventEmitter from 'events'; import EventEmitter from 'events';
import Debug from 'debug'; import Debug from 'debug';
import store from 'store'; import store from 'store';
import { EditorConfig, Utils, PluginComponents, PluginStatus, LocaleType, HooksConfig } from './definitions';
import { import { unRegistShortCuts, registShortCuts, transformToPromise } from './utils';
unRegistShortCuts,
registShortCuts,
transformToPromise,
generateI18n,
} from './utils';
// 根据url参数设置debug选项 // 根据url参数设置debug选项
const res = /_?debug=(.*?)(&|$)/.exec(location.search); const res = /_?debug=(.*?)(&|$)/.exec(location.search);
@ -39,35 +35,47 @@ window.onbeforeunload = function(e) {
return msg; return msg;
}; };
let instance = null; let instance: Editor;
const debug = Debug('editor'); const debug = Debug('editor');
EventEmitter.defaultMaxListeners = 100; EventEmitter.defaultMaxListeners = 100;
export interface HooksFuncs {
[idx: number]: (msg: string, handler: (...args) => void) => void;
}
export default class Editor extends EventEmitter { export default class Editor extends EventEmitter {
static getInstance = () => { static getInstance = (config: EditorConfig, components: PluginComponents, utils?: Utils): Editor => {
if (!instance) { if (!instance) {
instance = new Editor(); instance = new Editor(config, components, utils);
} }
return instance; return instance;
}; };
constructor(config, utils, components) { private hooksFuncs: HooksFuncs;
public pluginStatus: PluginStatus;
public plugins: PluginComponents;
public locale: LocaleType;
public emit: (msg: string, ...args) => void;
public on: (msg: string, handler: (...args) => void) => void;
public once: (msg: string, handler: (...args) => void) => void;
public off: (msg: string, handler: (...args) => void) => void;
constructor(public config: EditorConfig, public components: PluginComponents, public utils?: Utils) {
super(); super();
instance = this; instance = this;
this.config = config;
this.utils = utils;
this.components = components;
this.init(); this.init();
} }
init() { init(): Promise<any> {
const { hooks, shortCuts, lifeCycles } = this.config || {}; const { hooks, shortCuts = [], lifeCycles } = this.config || {};
this.locale = store.get('lowcode-editor-locale') || 'zh-CN'; this.locale = store.get('lowcode-editor-locale') || 'zh-CN';
// this.messages = this.messagesSet[this.locale]; // this.messages = this.messagesSet[this.locale];
// this.i18n = generateI18n(this.locale, this.messages); // this.i18n = generateI18n(this.locale, this.messages);
this.pluginStatus = this.initPluginStatus(); this.pluginStatus = this.initPluginStatus();
this.initHooks(hooks); this.initHooks(hooks || []);
this.emit('editor.beforeInit'); this.emit('editor.beforeInit');
const init = (lifeCycles && lifeCycles.init) || (() => {}); const init = (lifeCycles && lifeCycles.init) || (() => {});
@ -77,6 +85,7 @@ export default class Editor extends EventEmitter {
// 注册快捷键 // 注册快捷键
registShortCuts(shortCuts, this); registShortCuts(shortCuts, this);
this.emit('editor.afterInit'); this.emit('editor.afterInit');
return true;
}) })
.catch(err => { .catch(err => {
console.error(err); console.error(err);
@ -84,11 +93,12 @@ export default class Editor extends EventEmitter {
} }
destroy() { destroy() {
debug('destroy');
try { try {
const { hooks = [], shortCuts = [], lifeCycles = {} } = this.config; const { hooks = [], shortCuts = [], lifeCycles = {} } = this.config;
unRegistShortCuts(shortCuts); unRegistShortCuts(shortCuts);
this.destroyHooks(hooks); this.destroyHooks(hooks);
lifeCycles.destroy && lifeCycles.destroy(); lifeCycles.destroy && lifeCycles.destroy(this);
} catch (err) { } catch (err) {
console.warn(err); console.warn(err);
return; return;
@ -101,20 +111,8 @@ export default class Editor extends EventEmitter {
set(key: string | object, val: any): void { set(key: string | object, val: any): void {
if (typeof key === 'string') { if (typeof key === 'string') {
if ( if (['init', 'destroy', 'get', 'set', 'batchOn', 'batchOff', 'batchOnce'].includes(key)) {
[ console.error('init, destroy, get, set, batchOn, batchOff, batchOnce is private attribute');
'init',
'destroy',
'get',
'set',
'batchOn',
'batchOff',
'batchOnce',
].includes(key)
) {
console.warning(
'init, destroy, get, set, batchOn, batchOff, batchOnce is private attribute',
);
return; return;
} }
this[key] = val; this[key] = val;
@ -125,34 +123,34 @@ export default class Editor extends EventEmitter {
} }
} }
batchOn(events: Array<string>, lisenter: function): void { batchOn(events: Array<string>, lisenter: (...args) => void): void {
if (!Array.isArray(events)) return; if (!Array.isArray(events)) return;
events.forEach(event => this.on(event, lisenter)); events.forEach(event => this.on(event, lisenter));
} }
batchOnce(events: Array<string>, lisenter: function): void { batchOnce(events: Array<string>, lisenter: (...args) => void): void {
if (!Array.isArray(events)) return; if (!Array.isArray(events)) return;
events.forEach(event => this.once(event, lisenter)); events.forEach(event => this.once(event, lisenter));
} }
batchOff(events: Array<string>, lisenter: function): void { batchOff(events: Array<string>, lisenter: (...args) => void): void {
if (!Array.isArray(events)) return; if (!Array.isArray(events)) return;
events.forEach(event => this.off(event, lisenter)); events.forEach(event => this.off(event, lisenter));
} }
//销毁hooks中的消息监听 //销毁hooks中的消息监听
private destroyHooks(hooks = []) { private destroyHooks(hooks: HooksConfig = []) {
hooks.forEach((item, idx) => { hooks.forEach((item, idx) => {
if (typeof this.__hooksFuncs[idx] === 'function') { if (typeof this.hooksFuncs[idx] === 'function') {
this.off(item.message, this.__hooksFuncs[idx]); this.off(item.message, this.hooksFuncs[idx]);
} }
}); });
delete this.__hooksFuncs; delete this.hooksFuncs;
} }
//初始化hooks中的消息监听 //初始化hooks中的消息监听
private initHooks(hooks = []) { private initHooks(hooks: HooksConfig = []): void {
this.__hooksFuncs = hooks.map(item => { this.hooksFuncs = hooks.map(item => {
const func = (...args) => { const func = (...args) => {
item.handler(this, ...args); item.handler(this, ...args);
}; };
@ -168,11 +166,11 @@ export default class Editor extends EventEmitter {
pluginAreas.forEach(area => { pluginAreas.forEach(area => {
(plugins[area] || []).forEach(plugin => { (plugins[area] || []).forEach(plugin => {
if (plugin.type === 'Divider') return; if (plugin.type === 'Divider') return;
const { visible, disabled, dotted } = plugin.props || {}; const { visible, disabled, marked } = plugin.props || {};
res[plugin.pluginKey] = { res[plugin.pluginKey] = {
visible: typeof visible === 'boolean' ? visible : true, visible: typeof visible === 'boolean' ? visible : true,
disabled: typeof disabled === 'boolean' ? disabled : false, disabled: typeof disabled === 'boolean' ? disabled : false,
dotted: typeof dotted === 'boolean' ? dotted : false, marked: typeof marked === 'boolean' ? marked : false
}; };
const pluginClass = this.components[plugin.pluginKey]; const pluginClass = this.components[plugin.pluginKey];
// 判断如果编辑器插件有init静态方法则在此执行init方法 // 判断如果编辑器插件有init静态方法则在此执行init方法

View File

@ -1,3 +1,10 @@
import Editor from './editor'; import Editor from './editor';
export { default as PluginFactory } from './plugin';
export { default as EditorContext } from './context';
import * as editorUtils from './utils';
import * as editorDefinitions from './definitions';
export default Editor; export default Editor;
export const utils = editorUtils;
export const definitions = editorDefinitions;

View File

@ -1,23 +1,41 @@
import React, { PureComponent, creatRef} from 'react'; import React, { PureComponent, createRef } from 'react';
import EditorContext from './context'; import EditorContext from './context';
import { isEmpty, generateI18n, goldlog } from './utils'; import Editor from './editor';
import { isEmpty, generateI18n } from './utils';
import { PluginConfig, I18nFunction } from './definitions';
export interface PluginProps {
editor: Editor;
config: PluginConfig;
}
export default function plugin(Comp) { export interface InjectedPluginProps {
class LowcodePlugin extends PureComponent { i18n?: I18nFunction;
}
export default function plugin(
Comp: React.ComponentType<PluginProps & InjectedPluginProps>
): React.ComponentType<PluginProps> {
class LowcodePlugin extends PureComponent<PluginProps> {
static displayName = 'LowcodeEditorPlugin'; static displayName = 'LowcodeEditorPlugin';
static defaultProps = { static defaultProps = {
config: {}, config: {}
}; };
static contextType = EditorContext; static contextType = EditorContext;
static init = Comp.init;
public ref = createRef<React.Component>();
private editor: Editor;
private pluginKey: string;
private i18n: I18nFunction;
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
if (isEmpty(props.config) || !props.config.pluginKey) { if (isEmpty(props.config) || !props.config.pluginKey) {
console.warn('lowcode editor plugin has wrong config'); console.warn('lowcode editor plugin has wrong config');
return; return;
} }
this.ref = React.createRef();
const { locale, messages, editor } = props; const { locale, messages, editor } = props;
// 注册插件 // 注册插件
this.editor = editor; this.editor = editor;
@ -36,19 +54,9 @@ export default function plugin(Comp) {
render() { render() {
const { config } = this.props; const { config } = this.props;
return ( return <Comp ref={this.ref} i18n={this.i18n} editor={this.editor} config={config} {...config.pluginProps} />;
<Comp
ref={this.ref}
i18n={this.i18n}
editor={this.editor}
config={config}
{...config.pluginProps}
/>
);
} }
} }
LowcodePlugin.init = Comp.init;
return LowcodePlugin; return LowcodePlugin;
} }

View File

@ -1,15 +1,41 @@
import IntlMessageFormat from 'intl-messageformat'; import IntlMessageFormat from 'intl-messageformat';
import keymaster from 'keymaster'; import keymaster from 'keymaster';
import _isEmpty from 'lodash/isEmpty'; import _isEmpty from 'lodash/isEmpty';
import { EditorConfig, LocaleType, I18nMessages, I18nFunction, ShortCutsConfig } from './definitions';
import Editor from './editor';
export const isEmpty = _isEmpty; export const isEmpty = _isEmpty;
const ENV = {
TBE: 'TBE',
WEBIDE: 'WEB-IDE',
VSCODE: 'VSCODE',
WEB: 'WEB'
};
export interface IDEMessageParams {
action: string;
data: {
logKey: string;
gmKey: string;
goKey: string;
};
}
export interface Window {
sendIDEMessage: (IDEMessageParams) => void;
goldlog: {
record: (logKey: string, gmKey: string, goKey: string, method: 'GET' | 'POST') => void;
};
parent: Window;
is_theia: boolean;
vscode: boolean;
}
/** /**
* *
* @param {*} locale zh-CNen-US
* @param {*} messages
*/ */
export function generateI18n(locale = 'zh-CN', messages = {}) { export function generateI18n(locale: LocaleType = 'zh-CN', messages: I18nMessages = {}): I18nFunction {
return (key, values = {}) => { return (key, values = {}) => {
if (!messages || !messages[key]) return ''; if (!messages || !messages[key]) return '';
const formater = new IntlMessageFormat(messages[key], locale); const formater = new IntlMessageFormat(messages[key], locale);
@ -19,18 +45,14 @@ export function generateI18n(locale = 'zh-CN', messages = {}) {
/** /**
* *
* @param {*} obj
*/ */
export function serializeParams(obj: object): string { export function serializeParams(obj: object): string {
if (typeof obj !== 'object') return ''; if (typeof obj !== 'object') return '';
const res: Array<string> = []; const res: Array<string> = [];
Object.entries(obj).forEach(([key, val]) => { Object.entries(obj).forEach(([key, val]) => {
if (val === null || val === undefined || val === '') return; if (val === null || val === undefined || val === '') return;
if (typeof val === 'object') { if (typeof val === 'object') {
res.push( res.push(`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`);
`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`,
);
} else { } else {
res.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`); res.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`);
} }
@ -44,12 +66,12 @@ export function serializeParams(obj: object): string {
* @param {Object} params * @param {Object} params
* @param {String} logKey * @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 global = window as Window;
const sendIDEMessage = global.sendIDEMessage || global.parent.sendIDEMessage;
const goKey = serializeParams({ const goKey = serializeParams({
sdkVersion: pkg.version,
env: getEnv(), env: getEnv(),
...params, ...params
}); });
if (sendIDEMessage) { if (sendIDEMessage) {
sendIDEMessage({ sendIDEMessage({
@ -57,18 +79,17 @@ export function goldlog(gmKey, params = {}, logKey = 'other') {
data: { data: {
logKey: `/iceluna.core.${logKey}`, logKey: `/iceluna.core.${logKey}`,
gmKey, gmKey,
goKey, goKey
}, }
}); });
} }
window.goldlog && global.goldlog && global.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST');
window.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST');
} }
/** /**
* *
*/ */
export function getEnv() { export function getEnv(): string {
const userAgent = navigator.userAgent; const userAgent = navigator.userAgent;
const isVscode = /Electron\//.test(userAgent); const isVscode = /Electron\//.test(userAgent);
if (isVscode) return ENV.VSCODE; if (isVscode) return ENV.VSCODE;
@ -78,131 +99,17 @@ export function getEnv() {
} }
// 注册快捷键 // 注册快捷键
export function registShortCuts(config, editor) { export function registShortCuts(config: ShortCutsConfig, editor: Editor): void {
const keyboardFilter = (keymaster.filter = event => {
let eTarget = event.target || event.srcElement;
let tagName = eTarget.tagName;
let isInput = !!(
tagName == 'INPUT' ||
tagName == 'SELECT' ||
tagName == 'TEXTAREA'
);
let isContenteditable = !!eTarget.getAttribute('contenteditable');
if (isInput || isContenteditable) {
if (event.metaKey === true && [70, 83].includes(event.keyCode))
event.preventDefault(); //禁止触发chrome原生的页面保存或查找
return false;
} else {
return true;
}
});
const ideMessage = editor.utils && editor.utils.ideMessage;
//复制
if (!document.copyListener) {
document.copyListener = e => {
if (!keyboardFilter(e) || editor.isCopying) return;
const schema =
editor.schemaHelper &&
editor.schemaHelper.schemaMap[editor.activeKey];
if (!schema || !isSchema(schema)) return;
editor.isCopying = true;
const schemaStr = serialize(transformSchemaToPure(schema), {
unsafe: true,
});
setClipboardData(schemaStr)
.then(() => {
ideMessage &&
ideMessage(
'success',
'当前内容已复制到剪贴板请使用快捷键Command+v进行粘贴',
);
editor.emit('schema.copy', schemaStr, schema);
editor.isCopying = false;
})
.catch(errMsg => {
ideMessage && ideMessage('error', errMsg);
editor.isCopying = false;
});
};
document.addEventListener('copy', document.copyListener);
if (window.parent.vscode) {
keymaster('command+c', document.copyListener);
}
}
//粘贴
if (!document.pasteListener) {
const doPaste = (e, text) => {
if (!keyboardFilter(e) || editor.isPasting) return;
const schemaHelper = editor.schemaHelper;
let targetKey = editor.activeKey;
let direction = 'after';
const topKey =
schemaHelper.schema &&
schemaHelper.schema.__ctx &&
schemaHelper.schema.__ctx.lunaKey;
if (!targetKey || topKey === targetKey) {
const schemaHelper = editor.schemaHelper;
const topKey =
schemaHelper.schema &&
schemaHelper.schema.__ctx &&
schemaHelper.schema.__ctx.lunaKey;
if (!topKey) return;
targetKey = topKey;
direction = 'in';
}
editor.isPasting = true;
const schema = parseObj(text);
if (!isSchema(schema)) {
editor.emit('illegalSchema.paste', text);
// ideMessage && ideMessage('error', '当前内容不是模型结构,不能粘贴进来!');
console.warn('paste schema illegal');
editor.isPasting = false;
return;
}
editor.emit('material.add', {
schema,
targetKey,
direction,
});
editor.isPasting = false;
editor.emit('schema.paste', schema);
};
document.pasteListener = e => {
const clipboardData = e.clipboardData || window.clipboardData;
const text = clipboardData && clipboardData.getData('text');
doPaste(e, text);
};
document.addEventListener('paste', document.pasteListener);
if (window.parent.vscode) {
keymaster('command+v', e => {
const sendIDEMessage = window.parent.sendIDEMessage;
sendIDEMessage &&
sendIDEMessage({
action: 'readClipboard',
})
.then(text => {
doPaste(e, text);
})
.catch(err => {
console.warn(err);
});
});
}
}
(config || []).forEach(item => { (config || []).forEach(item => {
keymaster(item.keyboard, ev => { keymaster(item.keyboard, ev => {
ev.preventDefault(); ev.preventDefault();
item.handler(ev, editor, keymaster); item.handler(editor, ev, keymaster);
}); });
}); });
} }
// 取消注册快捷 // 取消注册快捷
export function unRegistShortCuts(config) { export function unRegistShortCuts(config: ShortCutsConfig): void {
(config || []).forEach(item => { (config || []).forEach(item => {
keymaster.unbind(item.keyboard); keymaster.unbind(item.keyboard);
}); });
@ -210,18 +117,12 @@ export function unRegistShortCuts(config) {
keymaster.unbind('command+c'); keymaster.unbind('command+c');
keymaster.unbind('command+v'); keymaster.unbind('command+v');
} }
if (document.copyListener) {
document.removeEventListener('copy', document.copyListener);
delete document.copyListener;
}
if (document.pasteListener) {
document.removeEventListener('paste', document.pasteListener);
delete document.pasteListener;
}
} }
// 将函数返回结果转成promise形式如果函数有返回值则根据返回值的bool类型判断是reject还是resolve若函数无返回值默认执行resolve /**
export function transformToPromise(input) { * promise形式bool类型判断是reject还是resolveresolve
*/
export function transformToPromise(input: any): Promise<{}> {
if (input instanceof Promise) return input; if (input instanceof Promise) return input;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (input || input === undefined) { if (input || input === undefined) {
@ -232,7 +133,13 @@ export function transformToPromise(input) {
}); });
} }
export function transformArrayToMap(arr, key, overwrite = true) { /**
* Map类型
*/
interface MapOf<T> {
[propName: string]: T;
}
export function transformArrayToMap<T>(arr: Array<T>, key: string, overwrite: boolean = true): MapOf<T> {
if (isEmpty(arr) || !Array.isArray(arr)) return {}; if (isEmpty(arr) || !Array.isArray(arr)) return {};
const res = {}; const res = {};
arr.forEach(item => { arr.forEach(item => {
@ -244,7 +151,13 @@ export function transformArrayToMap(arr, key, overwrite = true) {
return res; return res;
} }
export function parseSearch(search) { /**
* url的查询参数
*/
interface Query {
[propName: string]: string;
}
export function parseSearch(search: string): Query {
if (!search || typeof search !== 'string') return {}; if (!search || typeof search !== 'string') return {};
const str = search.replace(/^\?/, ''); const str = search.replace(/^\?/, '');
let paramStr = str.split('&'); let paramStr = str.split('&');
@ -258,63 +171,50 @@ export function parseSearch(search) {
return res; return res;
} }
export function comboEditorConfig(defaultConfig = {}, customConfig = {}) { export function comboEditorConfig(defaultConfig: EditorConfig = {}, customConfig: EditorConfig): EditorConfig {
const { const { skeleton, theme, plugins, hooks, shortCuts, lifeCycles, constants, utils, i18n } = customConfig || {};
skeleton,
theme,
plugins,
hooks,
shortCuts,
lifeCycles,
constants,
utils,
i18n,
} = customConfig || {};
if (skeleton && skeleton.handler && typeof skeleton.handler === 'function') { if (skeleton && skeleton.handler && typeof skeleton.handler === 'function') {
return skeleton.handler({ return skeleton.handler({
skeleton, skeleton,
...defaultConfig, ...defaultConfig
}); });
} }
const defaultShortCuts = transformArrayToMap( const defaultShortCuts = transformArrayToMap(defaultConfig.shortCuts || [], 'keyboard');
defaultConfig.shortCuts, const customShortCuts = transformArrayToMap(shortCuts || [], 'keyboard');
'keyboard',
);
const customShortCuts = transformArrayToMap(shortCuts, 'keyboard');
const localeList = ['zh-CN', 'zh-TW', 'en-US', 'ja-JP']; const localeList = ['zh-CN', 'zh-TW', 'en-US', 'ja-JP'];
const i18nConfig = {}; const i18nConfig = {};
localeList.forEach(key => { localeList.forEach(key => {
i18nConfig[key] = { i18nConfig[key] = {
...(defaultConfig.i18n && defaultConfig.i18n[key]), ...(defaultConfig.i18n && defaultConfig.i18n[key]),
...(i18n && i18n[key]), ...(i18n && i18n[key])
}; };
}); });
return { return {
skeleton, skeleton,
theme: { theme: {
...defaultConfig.theme, ...defaultConfig.theme,
...theme, ...theme
}, },
plugins: { plugins: {
...defaultConfig.plugins, ...defaultConfig.plugins,
...plugins, ...plugins
}, },
hooks: [...(defaultConfig.hooks || []), ...(hooks || [])], hooks: [...(defaultConfig.hooks || []), ...(hooks || [])],
shortCuts: Object.values({ shortCuts: Object.values({
...defaultShortCuts, ...defaultShortCuts,
...customShortCuts, ...customShortCuts
}), }),
lifeCycles: { lifeCycles: {
...defaultConfig.lifeCycles, ...defaultConfig.lifeCycles,
...lifeCycles, ...lifeCycles
}, },
constants: { constants: {
...defaultConfig.constants, ...defaultConfig.constants,
...constants, ...constants
}, },
utils: [...(defaultConfig.utils || []), ...(utils || [])], utils: [...(defaultConfig.utils || []), ...(utils || [])],
i18n: i18nConfig, i18n: i18nConfig
}; };
} }

View File

@ -1,6 +1,5 @@
body { body {
font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, Arial, PingFang SC-Light, Microsoft YaHei;
Arial, PingFang SC-Light, Microsoft YaHei;
font-size: 12px; font-size: 12px;
* { * {
box-sizing: border-box; box-sizing: border-box;

View File

@ -28,5 +28,5 @@ ReactDOM.render(
constants={constants} constants={constants}
components={components} components={components}
/>, />,
ICE_CONTAINER, ICE_CONTAINER
); );

View File

@ -1,26 +1,42 @@
import React, { PureComponent, Fragment } from 'react'; import React, { PureComponent, Fragment } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { Balloon, Dialog, Icon, Badge } from '@alife/next'; import { Balloon, Dialog, Icon, Badge } from '@alifd/next';
import './index.scss'; import './index.scss';
export default class LeftPlugin extends PureComponent { import Editor from '../../../framework/editor';
static displayName = 'lowcodeLeftPlugin'; 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<LeftPluginProps, LeftPluginState> {
static displayName = 'LowcodeLeftPlugin';
static defaultProps = { static defaultProps = {
active: false, active: false,
config: {}, config: {},
disabled: false, disabled: false,
dotted: false, marked: false,
locked: false, locked: false,
onClick: () => {}, onClick: () => {}
}; };
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
this.state = { this.state = {
dialogVisible: false, dialogVisible: false
}; };
} }
@ -60,7 +76,7 @@ export default class LeftPlugin extends PureComponent {
handleOpen = () => { handleOpen = () => {
// todo 对话框类型的插件初始时拿不到插件实例 // todo 对话框类型的插件初始时拿不到插件实例
this.setState({ this.setState({
dialogVisible: true, dialogVisible: true
}); });
}; };
@ -75,15 +91,7 @@ export default class LeftPlugin extends PureComponent {
}; };
renderIcon = clickCallback => { renderIcon = clickCallback => {
const { const { active, disabled, marked, locked, onClick, config, editor } = this.props;
active,
disabled,
dotted,
locked,
onClick,
config,
editor,
} = this.props;
const { pluginKey, props } = config || {}; const { pluginKey, props } = config || {};
const { icon, title } = props || {}; const { icon, title } = props || {};
return ( return (
@ -91,7 +99,7 @@ export default class LeftPlugin extends PureComponent {
className={classNames('lowcode-left-plugin', pluginKey, { className={classNames('lowcode-left-plugin', pluginKey, {
active, active,
disabled, disabled,
locked, locked
})} })}
data-tooltip={title} data-tooltip={title}
onClick={() => { onClick={() => {
@ -101,7 +109,7 @@ export default class LeftPlugin extends PureComponent {
onClick && onClick(); onClick && onClick();
}} }}
> >
{dotted ? ( {marked ? (
<Badge dot> <Badge dot>
<Icon type={icon} size="small" /> <Icon type={icon} size="small" />
</Badge> </Badge>
@ -113,15 +121,7 @@ export default class LeftPlugin extends PureComponent {
}; };
render() { render() {
const { const { marked, locked, active, disabled, config, editor, pluginClass: Comp } = this.props;
dotted,
locked,
active,
disabled,
config,
editor,
pluginClass: Comp,
} = this.props;
const { pluginKey, props, type, pluginProps } = config || {}; const { pluginKey, props, type, pluginProps } = config || {};
const { onClick, title } = props || {}; const { onClick, title } = props || {};
const { dialogVisible } = this.state; const { dialogVisible } = this.state;
@ -197,7 +197,7 @@ export default class LeftPlugin extends PureComponent {
this.handleOpen(); this.handleOpen();
}); });
case 'Custom': case 'Custom':
return dotted ? <Badge dot>{node}</Badge> : node; return marked ? <Badge dot>{node}</Badge> : node;
default: default:
return null; return null;
} }

View File

@ -1,8 +1,13 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import './index.scss'; import './index.scss';
export default class Panel extends PureComponent {
static displayName = 'Panel'; export interface PanelProps {
children: Plugin;
}
export default class Panel extends PureComponent<PanelProps> {
static displayName = 'LowcodePanel';
constructor(props) { constructor(props) {
super(props); super(props);
@ -13,7 +18,7 @@ export default class Panel extends PureComponent {
<div <div
className="lowcode-panel" className="lowcode-panel"
style={{ style={{
width: 240, width: 240
}} }}
> >
{this.props.children} {this.props.children}

View File

@ -1,23 +1,25 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import { Icon, Button } from '@alifd/next'; import { Icon, Button } from '@alifd/next';
import './index.scss'; import './index.scss';
export default class TopIcon extends PureComponent {
static displayName = 'TopIcon'; export interface TopIconProps {
static propTypes = { active?: boolean;
active: PropTypes.bool, className?: string;
className: PropTypes.string, disabled?: boolean;
disabled: PropTypes.bool, icon: string;
icon: PropTypes.string, id?: string;
id: PropTypes.string, locked?: boolean;
locked: PropTypes.bool, marked?: boolean;
onClick: PropTypes.func, onClick?: () => void;
showTitle: PropTypes.bool, showTitle?: boolean;
style: PropTypes.object, style?: React.CSSProperties;
title: PropTypes.string, title?: string;
}; }
export default class TopIcon extends PureComponent<TopIconProps> {
static displayName = 'LowcodeTopIcon';
static defaultProps = { static defaultProps = {
active: false, active: false,
className: '', className: '',
@ -28,22 +30,11 @@ export default class TopIcon extends PureComponent {
onClick: () => {}, onClick: () => {},
showTitle: false, showTitle: false,
style: {}, style: {},
title: '', title: ''
}; };
render() { render() {
const { const { active, disabled, icon, locked, title, className, id, style, showTitle, onClick } = this.props;
active,
disabled,
icon,
locked,
title,
className,
id,
style,
showTitle,
onClick,
} = this.props;
return ( return (
<Button <Button
type="normal" type="normal"
@ -52,11 +43,11 @@ export default class TopIcon extends PureComponent {
className={classNames('lowcode-top-btn', className, { className={classNames('lowcode-top-btn', className, {
active, active,
disabled, disabled,
locked, locked
})} })}
id={id} id={id}
style={style} style={style}
onClick={disabled ? null : onClick} onClick={disabled ? undefined : onClick}
> >
<div> <div>
<Icon size="large" type={icon} /> <Icon size="large" type={icon} />

View File

@ -1,26 +1,43 @@
import React, { PureComponent, Fragment } from 'react'; import React, { PureComponent, Fragment } from 'react';
import PropTypes from 'prop-types';
import TopIcon from '../TopIcon'; import TopIcon from '../TopIcon';
import { Balloon, Badge, Dialog } from '@alifd/next'; import { Balloon, Badge, Dialog } from '@alifd/next';
import './index.scss'; import './index.scss';
export default class TopPlugin extends PureComponent { import { PluginConfig, PluginClass } from '../../../framework/definitions';
static displayName = 'lowcodeTopPlugin'; 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<TopPluginProps, TopPluginState> {
static displayName = 'LowcodeTopPlugin';
static defaultProps = { static defaultProps = {
active: false, active: false,
config: {}, config: {},
disabled: false, disabled: false,
dotted: false, marked: false,
locked: false, locked: false,
onClick: () => {}, onClick: () => {}
}; };
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
this.state = { this.state = {
dialogVisible: false, dialogVisible: false
}; };
} }
@ -70,20 +87,12 @@ export default class TopPlugin extends PureComponent {
handleOpen = () => { handleOpen = () => {
// todo dialog类型的插件初始时拿不动插件实例 // todo dialog类型的插件初始时拿不动插件实例
this.setState({ this.setState({
dialogVisible: true, dialogVisible: true
}); });
}; };
renderIcon = clickCallback => { renderIcon = clickCallback => {
const { const { active, disabled, marked, locked, config, onClick, editor } = this.props;
active,
disabled,
dotted,
locked,
config,
onClick,
editor,
} = this.props;
const { pluginKey, props } = config || {}; const { pluginKey, props } = config || {};
const { icon, title } = props || {}; const { icon, title } = props || {};
const node = ( const node = (
@ -103,19 +112,11 @@ export default class TopPlugin extends PureComponent {
}} }}
/> />
); );
return dotted ? <Badge dot>{node}</Badge> : node; return marked ? <Badge dot>{node}</Badge> : node;
}; };
render() { render() {
const { const { active, marked, locked, disabled, config, editor, pluginClass: Comp } = this.props;
active,
dotted,
locked,
disabled,
config,
editor,
pluginClass: Comp,
} = this.props;
const { pluginKey, pluginProps, props, type } = config || {}; const { pluginKey, pluginProps, props, type } = config || {};
const { onClick, title } = props || {}; const { onClick, title } = props || {};
const { dialogVisible } = this.state; const { dialogVisible } = this.state;
@ -184,7 +185,7 @@ export default class TopPlugin extends PureComponent {
</Balloon> </Balloon>
); );
case 'Custom': case 'Custom':
return dotted ? <Badge dot>{node}</Badge> : node; return marked ? <Badge dot>{node}</Badge> : node;
default: default:
return null; return null;
} }

View File

@ -1,6 +1,5 @@
body { body {
font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, Arial, PingFang SC-Light, Microsoft YaHei;
Arial, PingFang SC-Light, Microsoft YaHei;
font-size: 12px; font-size: 12px;
padding: 0; padding: 0;
margin: 0; margin: 0;

View File

@ -1,9 +1,12 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { HashRouter as Router, Route } from 'react-router-dom'; import { HashRouter as Router, Route } from 'react-router-dom';
import Editor from '../framework/editor';
import { comboEditorConfig, parseSearch } from '../framework/utils';
import { Loading, ConfigProvider } from '@alifd/next'; import { 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 defaultConfig from './config/skeleton';
import skeletonUtils from './config/utils'; import skeletonUtils from './config/utils';
@ -16,20 +19,35 @@ import './global.scss';
let renderIdx = 0; let renderIdx = 0;
export default class Skeleton extends PureComponent { export interface SkeletonProps {
components: PluginComponents;
config: EditorConfig;
utils: Utils;
}
export interface SkeletonState {
initReady: boolean;
skeletonKey: string;
__hasError?: boolean;
}
export default class Skeleton extends PureComponent<SkeletonProps, SkeletonState> {
static displayName = 'LowcodeEditorSkeleton'; static displayName = 'LowcodeEditorSkeleton';
static getDerivedStateFromError() { static getDerivedStateFromError() {
return { return {
__hasError: true, __hasError: true
}; };
} }
private editor: Editor;
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
initReady: false, initReady: false,
skeletonKey: `skeleton${renderIdx}`, skeletonKey: `skeleton${renderIdx}`
}; };
this.init(); this.init();
@ -37,31 +55,28 @@ export default class Skeleton extends PureComponent {
componentWillUnmount() { componentWillUnmount() {
this.editor && this.editor.destroy(); this.editor && this.editor.destroy();
this.editor = null;
} }
componentDidCatch(err) { componentDidCatch(err) {
console.error(err); console.error(err);
} }
init = (isReset = false) => { init = (isReset: boolean = false): void => {
if (this.editor) { if (this.editor) {
this.editor.destroy(); this.editor.destroy();
this.editor = null;
} }
const { utils, config, components } = this.props; const { utils, config, components } = this.props;
const editor = (this.editor = new Editor( const editor = (this.editor = new Editor(comboEditorConfig(defaultConfig, config), components, {
comboEditorConfig(defaultConfig, config), ...skeletonUtils,
{ ...skeletonUtils, ...utils }, ...utils
components, }));
));
window.__ctx = { window.__ctx = {
editor, editor,
appHelper: editor appHelper: editor
}; };
editor.once('editor.reset', () => { editor.once('editor.reset', () => {
this.setState({ this.setState({
initReady: false, initReady: false
}); });
editor.emit('editor.beforeReset'); editor.emit('editor.beforeReset');
this.init(true); this.init(true);
@ -72,9 +87,7 @@ export default class Skeleton extends PureComponent {
{ {
initReady: true, initReady: true,
//刷新IDE时生成新的skeletonKey保证插件生命周期重新执行 //刷新IDE时生成新的skeletonKey保证插件生命周期重新执行
skeletonKey: isReset skeletonKey: isReset ? `skeleton${++renderIdx}` : this.state.skeletonKey
? `skeleton${++renderIdx}`
: this.state.skeletonKey,
}, },
() => { () => {
editor.emit('editor.ready'); editor.emit('editor.ready');
@ -86,7 +99,7 @@ export default class Skeleton extends PureComponent {
render() { render() {
const { initReady, skeletonKey, __hasError } = this.state; const { initReady, skeletonKey, __hasError } = this.state;
if (__hasError) { if (__hasError || !this.editor) {
return 'error'; return 'error';
} }
@ -102,13 +115,7 @@ export default class Skeleton extends PureComponent {
this.editor.set('match', match); this.editor.set('match', match);
return ( return (
<ConfigProvider> <ConfigProvider>
<Loading <Loading tip="Loading" size="large" visible={!initReady} shape="fusion-reactor" fullScreen>
tip="Loading"
size="large"
visible={!initReady}
shape="fusion-reactor"
fullScreen
>
<div className="lowcode-editor" key={skeletonKey}> <div className="lowcode-editor" key={skeletonKey}>
<TopArea editor={this.editor} /> <TopArea editor={this.editor} />
<div className="lowcode-main-content"> <div className="lowcode-main-content">

View File

@ -1,31 +1,31 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import Editor from '../../../framework/editor';
import { PluginConfig } from '../../../framework/definitions';
import './index.scss'; import './index.scss';
export default class CenterArea extends PureComponent { export interface CenterAreaProps {
static displayName = 'lowcodeCenterArea'; editor: Editor;
}
export default class CenterArea extends PureComponent<CenterAreaProps> {
static displayName = 'LowcodeCenterArea';
private editor: Editor;
private config: Array<PluginConfig>;
constructor(props) { constructor(props) {
super(props); super(props);
this.editor = props.editor; this.editor = props.editor;
this.config = this.editor.config && this.editor.config.plugins && this.editor.config.plugins.centerArea || []; this.config = (this.editor.config && this.editor.config.plugins && this.editor.config.plugins.centerArea) || [];
} }
render() { render() {
const list = this.config.filter(item => {
return true;
});
return ( return (
<div className="lowcode-center-area"> <div className="lowcode-center-area">
{list.map(item => { {this.config.map(item => {
const Comp = this.editor.components[item.pluginKey]; const Comp = this.editor.components[item.pluginKey];
return ( return <Comp editor={this.editor} config={item} {...item.pluginProps} />;
<Comp
editor={this.editor}
config={item}
{...item.pluginProps}
/>
)
})} })}
</div> </div>
); );

View File

@ -3,5 +3,5 @@ import Panel from './panel';
export default { export default {
Nav, Nav,
Panel, Panel
}; };

View File

@ -1,20 +1,28 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import LeftPlugin from '../../components/LeftPlugin'; import LeftPlugin from '../../components/LeftPlugin';
import './index.scss'; import './index.scss';
import Editor from '../../../framework/editor';
import { PluginConfig } from '../../../framework/definitions';
export default class LeftAreaPanel extends PureComponent { export interface LeftAreaNavProps {
static displayName = 'lowcodeLeftAreaNav'; editor: Editor;
}
export default class LeftAreaNav extends PureComponent<LeftAreaNavProps> {
static displayName = 'LowcodeLeftAreaNav';
private editor: Editor;
private config: Array<PluginConfig>;
constructor(props) { constructor(props) {
super(props); super(props);
this.editor = props.editor; this.editor = props.editor;
this.config = this.config = (this.editor.config.plugins && this.editor.config.plugins.leftArea) || [];
this.editor.config.plugins && this.editor.config.plugins.leftArea;
} }
handlePluginClick = item => {}; handlePluginClick = item => {};
renderPluginList = (list = []) => { renderPluginList = (list: Array<PluginConfig> = []): Array<React.ReactElement> => {
return list.map((item, idx) => { return list.map((item, idx) => {
return ( return (
<LeftPlugin <LeftPlugin
@ -29,11 +37,10 @@ export default class LeftAreaPanel extends PureComponent {
}; };
render() { render() {
const topList = []; const topList: Array<PluginConfig> = [];
const bottomList = []; const bottomList: Array<PluginConfig> = [];
this.config.forEach(item => { this.config.forEach(item => {
const align = const align = item.props && item.props.align === 'bottom' ? 'bottom' : 'top';
item.props && item.props.align === 'bottom' ? 'bottom' : 'top';
if (align === 'bottom') { if (align === 'bottom') {
bottomList.push(item); bottomList.push(item);
} else { } else {

View File

@ -1,18 +1,30 @@
import React, { PureComponent, Fragment } from 'react'; import React, { PureComponent, Fragment } from 'react';
import Panel from '../../components/Panel'; import Panel from '../../components/Panel';
import './index.scss'; import './index.scss';
import Editor from '../../../framework/editor';
import { PluginConfig } from '../../../framework/definitions';
export default class LeftAreaPanel extends PureComponent { export interface LeftAreaPanelProps {
static displayName = 'lowcodeLeftAreaPanel'; editor: Editor;
}
export interface LeftAreaPanelState {
activeKey: string;
}
export default class LeftAreaPanel extends PureComponent<LeftAreaPanelProps, LeftAreaPanelState> {
static displayName = 'LowcodeLeftAreaPanel';
private editor: Editor;
private config: Array<PluginConfig>;
constructor(props) { constructor(props) {
super(props); super(props);
this.editor = props.editor; this.editor = props.editor;
this.config = this.config = (this.editor.config.plugins && this.editor.config.plugins.leftArea) || [];
this.editor.config.plugins && this.editor.config.plugins.leftArea;
this.state = { this.state = {
activeKey: 'leftPanelIcon', activeKey: 'leftPanelIcon'
}; };
} }
@ -25,10 +37,7 @@ export default class LeftAreaPanel extends PureComponent {
{list.map((item, idx) => { {list.map((item, idx) => {
const Comp = this.editor.components[item.pluginKey]; const Comp = this.editor.components[item.pluginKey];
return ( return (
<Panel <Panel key={item.pluginKey} visible={item.pluginKey === this.state.activeKey}>
key={item.pluginKey}
visible={item.pluginKey === this.state.activeKey}
>
<Comp editor={this.editor} config={item} /> <Comp editor={this.editor} config={item} />
</Panel> </Panel>
); );

View File

@ -1,44 +1,52 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { Tab } from '@alifd/next'; import { Tab } from '@alifd/next';
import './index.scss'; import './index.scss';
import Editor from '../../../framework/editor';
import { PluginConfig } from '../../../framework/definitions';
export default class RightArea extends PureComponent { export interface RightAreaProps {
static displayName = 'lowcodeRightArea'; editor: Editor;
}
export interface RightAreaState {
activeKey: string;
}
export default class RightArea extends PureComponent<RightAreaProps, RightAreaState> {
static displayName = 'LowcodeRightArea';
private editor: Editor;
private config: Array<PluginConfig>;
constructor(props) { constructor(props) {
super(props); super(props);
this.editor = props.editor; this.editor = props.editor;
this.config = (this.editor.config.plugins && this.editor.config.plugins.rightArea) || [];
this.state = { this.state = {
activeKey: 'rightPanel1', activeKey: 'rightPanel1'
}; };
} }
handleTabChange = key => { handleTabChange = (key: string): void => {
this.setState({ this.setState({
activeKey: key, activeKey: key
}); });
}; };
render() { render() {
const list =
(this.editor &&
this.editor.config &&
this.editor.config.plugins &&
this.editor.config.plugins.rightArea) ||
[];
return ( return (
<div className="lowcode-right-area"> <div className="lowcode-right-area">
<Tab <Tab
shape="wrapped" shape="wrapped"
className="right-tabs" className="right-tabs"
style={{ style={{
height: '100%', height: '100%'
}} }}
activeKey={this.state.activeKey} activeKey={this.state.activeKey}
lazyLoad={false} lazyLoad={false}
onChange={this.handleTabChange} onChange={this.handleTabChange}
> >
{list.map((item, idx) => { {this.config.map((item, idx) => {
const Comp = this.editor.components[item.pluginKey]; const Comp = this.editor.components[item.pluginKey];
return ( return (
<Tab.Item key={item.pluginKey} title={item.props.title}> <Tab.Item key={item.pluginKey} title={item.props.title}>

View File

@ -2,17 +2,25 @@ import React, { PureComponent } from 'react';
import { Grid } from '@alifd/next'; import { Grid } from '@alifd/next';
import TopPlugin from '../../components/TopPlugin'; import TopPlugin from '../../components/TopPlugin';
import './index.scss'; import './index.scss';
import Editor from '../../../framework/index';
import { PluginConfig } from '../../../framework/definitions';
const { Row, Col } = Grid; const { Row, Col } = Grid;
export default class TopArea extends PureComponent { export interface TopAreaProps {
static displayName = 'lowcodeTopArea'; editor: Editor;
}
export default class TopArea extends PureComponent<TopAreaProps> {
static displayName = 'LowcodeTopArea';
private editor: Editor;
private config: Array<PluginConfig>;
constructor(props) { constructor(props) {
super(props); super(props);
this.editor = props.editor; this.editor = props.editor;
this.config = this.config = (this.editor.config.plugins && this.editor.config.plugins.topArea) || [];
this.editor.config.plugins && this.editor.config.plugins.topArea;
} }
componentDidMount() {} componentDidMount() {}
@ -20,7 +28,7 @@ export default class TopArea extends PureComponent {
handlePluginStatusChange = () => {}; handlePluginStatusChange = () => {};
renderPluginList = (list = []) => { renderPluginList = (list: Array<PluginConfig> = []): Array<React.ReactElement> => {
return list.map((item, idx) => { return list.map((item, idx) => {
const isDivider = item.type === 'Divider'; const isDivider = item.type === 'Divider';
return ( return (
@ -29,15 +37,11 @@ export default class TopArea extends PureComponent {
key={isDivider ? idx : item.pluginKey} key={isDivider ? idx : item.pluginKey}
style={{ style={{
width: (item.props && item.props.width) || 40, width: (item.props && item.props.width) || 40,
flex: 'none', flex: 'none'
}} }}
> >
{!isDivider && ( {!isDivider && (
<TopPlugin <TopPlugin config={item} pluginClass={this.editor.components[item.pluginKey]} editor={this.editor} />
config={item}
pluginClass={this.editor.components[item.pluginKey]}
editor={this.editor}
/>
)} )}
</Col> </Col>
); );
@ -46,19 +50,14 @@ export default class TopArea extends PureComponent {
render() { render() {
if (!this.config) return null; if (!this.config) return null;
const leftList = []; const leftList: Array<PluginConfig> = [];
const rightList = []; const rightList: Array<PluginConfig> = [];
this.config.forEach(item => { this.config.forEach(item => {
const align = const align = item.props && item.props.align === 'right' ? 'right' : 'left';
item.props && item.props.align === 'right' ? 'right' : 'left';
// 分隔符不允许相邻 // 分隔符不允许相邻
if (item.type === 'Divider') { if (item.type === 'Divider') {
const currList = align === 'right' ? rightList : leftList; const currList = align === 'right' ? rightList : leftList;
if ( if (currList.length === 0 || currList[currList.length - 1].type === 'Divider') return;
currList.length === 0 ||
currList[currList.length - 1].type === 'Divider'
)
return;
} }
if (align === 'right') { if (align === 'right') {
rightList.push(item); rightList.push(item);

View File

@ -6,5 +6,5 @@ export default {
pageNotExist: 'The current Page not exist', pageNotExist: 'The current Page not exist',
enterFromAppCenter: 'Please enter from the app center', enterFromAppCenter: 'Please enter from the app center',
noPermission: 'Sorry, you do not have the develop permission', noPermission: 'Sorry, you do not have the develop permission',
getPermission: 'Please connect the app owners {owners} to get the permission', getPermission: 'Please connect the app owners {owners} to get the permission'
}; };

View File

@ -6,5 +6,5 @@ export default {
pageNotExist: '当前访问地址不存在', pageNotExist: '当前访问地址不存在',
enterFromAppCenter: '请从应用中心入口重新进入', enterFromAppCenter: '请从应用中心入口重新进入',
noPermission: '抱歉,您暂无开发权限', noPermission: '抱歉,您暂无开发权限',
getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限', getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限'
}; };