From baa6adb19999107f30df0dea0c22214f181a7b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8B=E7=BE=8A?= Date: Mon, 9 Mar 2020 15:58:46 +0800 Subject: [PATCH] daily tag --- packages/editor/.prettierrc | 4 + packages/editor/package.json | 12 +- packages/editor/src/config/components.js | 11 +- packages/editor/src/config/constants.js | 2 +- packages/editor/src/config/locale/index.js | 2 +- packages/editor/src/config/skeleton.js | 96 +++---- packages/editor/src/framework/definitions.ts | 100 +++++++- packages/editor/src/framework/editor.ts | 82 +++--- packages/editor/src/framework/index.ts | 7 + .../src/framework/{plugin.js => plugin.tsx} | 42 +-- packages/editor/src/framework/utils.ts | 242 +++++------------- packages/editor/src/global.scss | 3 +- packages/editor/src/index.tsx | 2 +- .../skeleton/components/LeftPlugin/index.tsx | 60 ++--- .../src/skeleton/components/Panel/index.tsx | 11 +- .../src/skeleton/components/TopIcon/index.tsx | 53 ++-- .../skeleton/components/TopPlugin/index.tsx | 55 ++-- packages/editor/src/skeleton/global.scss | 3 +- packages/editor/src/skeleton/index.tsx | 57 +++-- .../src/skeleton/layouts/CenterArea/index.tsx | 28 +- .../src/skeleton/layouts/LeftArea/index.tsx | 2 +- .../src/skeleton/layouts/LeftArea/nav.tsx | 25 +- .../src/skeleton/layouts/LeftArea/panel.tsx | 27 +- .../src/skeleton/layouts/RightArea/index.tsx | 34 ++- .../src/skeleton/layouts/TopArea/index.tsx | 39 ++- packages/editor/src/skeleton/locale/en-US.js | 2 +- packages/editor/src/skeleton/locale/zh-CN.js | 2 +- 27 files changed, 519 insertions(+), 484 deletions(-) create mode 100644 packages/editor/.prettierrc rename packages/editor/src/framework/{plugin.js => plugin.tsx} (52%) diff --git a/packages/editor/.prettierrc b/packages/editor/.prettierrc new file mode 100644 index 000000000..d3c963559 --- /dev/null +++ b/packages/editor/.prettierrc @@ -0,0 +1,4 @@ +{ + "printWidth": 120, + "singleQuote": true +} \ No newline at end of file diff --git a/packages/editor/package.json b/packages/editor/package.json index df3f52b9c..f9f4648af 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -8,15 +8,21 @@ "@icedesign/theme": "^1.x", "@types/react": "^16.8.3", "@types/react-dom": "^16.8.2", + "events": "^3.1.0", + "intl-messageformat": "^8.2.1", "keymaster": "^1.6.2", "moment": "^2.23.0", "prop-types": "^15.5.8", - "react": "^16.4.1", - "react-dom": "^16.4.1", - "react-router-dom": "^5.1.2" + "react": "^16.8.1", + "react-dom": "^16.8.1", + "react-router-dom": "^5.1.2", + "store": "^2.0.12" }, "devDependencies": { "@ice/spec": "^0.1.1", + "@types/debug": "^4.1.5", + "@types/events": "^3.0.0", + "@types/store": "^2.0.2", "css-modules-typescript-loader": "^2.0.4", "eslint": "^6.0.1", "ice-plugin-fusion": "^0.1.4", diff --git a/packages/editor/src/config/components.js b/packages/editor/src/config/components.js index 901075ab6..938c7c842 100644 --- a/packages/editor/src/config/components.js +++ b/packages/editor/src/config/components.js @@ -1,4 +1,3 @@ - import topBalloonIcon from '@ali/iceluna-addon-2'; import topDialogIcon from '@ali/iceluna-addon-2'; import leftPanelIcon from '@ali/iceluna-addon-2'; @@ -15,10 +14,10 @@ export default { topBalloonIcon: PluginFactory(topBalloonIcon), topDialogIcon: PluginFactory(topDialogIcon), leftPanelIcon: PluginFactory(leftPanelIcon), - leftBalloonIcon:PluginFactory(leftBalloonIcon), - leftDialogIcon:PluginFactory(leftDialogIcon), - rightPanel1:PluginFactory(rightPanel1), - rightPanel2:PluginFactory(rightPanel2), + leftBalloonIcon: PluginFactory(leftBalloonIcon), + leftDialogIcon: PluginFactory(leftDialogIcon), + rightPanel1: PluginFactory(rightPanel1), + rightPanel2: PluginFactory(rightPanel2), rightPanel3: PluginFactory(rightPanel3), - rightPanel4: PluginFactory(rightPanel4), + rightPanel4: PluginFactory(rightPanel4) }; diff --git a/packages/editor/src/config/constants.js b/packages/editor/src/config/constants.js index d20e24f41..d7e16fed3 100644 --- a/packages/editor/src/config/constants.js +++ b/packages/editor/src/config/constants.js @@ -1,3 +1,3 @@ export default { - namespace: 'page', + namespace: 'page' }; diff --git a/packages/editor/src/config/locale/index.js b/packages/editor/src/config/locale/index.js index 5bc4373a4..f4ad6c5da 100644 --- a/packages/editor/src/config/locale/index.js +++ b/packages/editor/src/config/locale/index.js @@ -6,5 +6,5 @@ export default { 'en-US': en_us, 'zh-CN': zh_cn, 'zh-TW': zh_tw, - 'ja-JP': ja_jp, + 'ja-JP': ja_jp }; diff --git a/packages/editor/src/config/skeleton.js b/packages/editor/src/config/skeleton.js index 87d035f4a..7c59d154b 100644 --- a/packages/editor/src/config/skeleton.js +++ b/packages/editor/src/config/skeleton.js @@ -3,12 +3,12 @@ export default { theme: { dpl: { package: '@alife/dpl-iceluna', - version: '^2.3.0', + version: '^2.3.0' }, - scss: '', + scss: '' }, constants: { - namespace: 'page', + namespace: 'page' }, utils: [], plugins: { @@ -21,21 +21,21 @@ export default { title: 'balloon', icon: 'dengpao', balloonProps: { - triggerType: 'click', - }, + triggerType: 'click' + } }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'divider', type: 'Divider', props: { - align: 'left', - }, + align: 'left' + } }, { pluginKey: 'topDialogIcon', @@ -43,13 +43,13 @@ export default { props: { align: 'left', title: 'dialog', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'topLinkIcon', @@ -60,11 +60,11 @@ export default { icon: 'dengpao', linkProps: { href: '//www.taobao.com', - target: 'blank', - }, + target: 'blank' + } }, config: {}, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'topIcon', @@ -75,11 +75,11 @@ export default { icon: 'dengpao', onClick: function(editor) { alert('icon addon invoke, current activeKey: ' + editor.activeKey); - }, + } }, config: {}, - pluginProps: {}, - }, + pluginProps: {} + } ], leftArea: [ { @@ -88,13 +88,13 @@ export default { props: { align: 'top', title: 'panel', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'leftBalloonIcon', @@ -102,13 +102,13 @@ export default { props: { align: 'top', title: 'balloon', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'leftDialogIcon', @@ -116,13 +116,13 @@ export default { props: { align: 'bottom', title: 'dialog', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'leftLinkIcon', @@ -133,11 +133,11 @@ export default { icon: 'dengpao', linkProps: { href: '//www.taobao.com', - target: 'blank', - }, + target: 'blank' + } }, config: {}, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'leftIcon', @@ -148,11 +148,11 @@ export default { icon: 'dengpao', onClick: function(editor) { alert('icon addon invoke, current activeKey: ' + editor.activeKey); - }, + } }, config: {}, - pluginProps: {}, - }, + pluginProps: {} + } ], rightArea: [ { @@ -160,56 +160,56 @@ export default { type: 'Panel', props: { title: 'panel1', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'rightPanel2', type: 'Panel', props: { title: 'panel2', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'rightPanel3', type: 'Panel', props: { title: 'panel3', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, + pluginProps: {} }, { pluginKey: 'rightPanel4', type: 'Panel', props: { title: 'panel4', - icon: 'dengpao', + icon: 'dengpao' }, config: { package: '@ali/iceluna-addon-2', - version: '^1.0.0', + version: '^1.0.0' }, - pluginProps: {}, - }, + pluginProps: {} + } ], - centerArea: [], + centerArea: [] }, hooks: [], - shortCuts: [], + shortCuts: [] }; diff --git a/packages/editor/src/framework/definitions.ts b/packages/editor/src/framework/definitions.ts index 73655b11e..e406cd6fa 100644 --- a/packages/editor/src/framework/definitions.ts +++ b/packages/editor/src/framework/definitions.ts @@ -1,4 +1,17 @@ -export interface EditorConfig {} +import * as React from 'react'; +import Editor from './editor'; + +export interface EditorConfig { + skeleton?: SkeletonConfig; + theme?: ThemeConfig; + plugins?: PluginsConfig; + hooks?: HooksConfig; + shortCuts?: ShortCutsConfig; + utils?: UtilsConfig; + constants?: ConstantsConfig; + lifeCycles?: lifeCyclesConfig; + i18n?: I18nConfig; +} export interface NpmConfig { version: string; @@ -25,17 +38,92 @@ export interface ThemeConfig { } export interface PluginsConfig { - [key]: Array; + [propName: string]: Array; } export interface PluginConfig { pluginKey: string; type: string; - props: object; - config: NpmConfig; - pluginProps: object; + props: { + icon?: string; + title?: string; + width?: number; + height?: number; + visible?: boolean; + disabled?: boolean; + marked?: boolean; + align?: 'left' | 'right' | 'top' | 'bottom'; + onClick?: () => void; + dialogProps?: object; + balloonProps?: object; + panelProps?: object; + linkProps?: object; + }; + config?: NpmConfig; + pluginProps?: object; } export type HooksConfig = Array; -export interface HookConfig {} +export interface HookConfig { + message: string; + type: 'on' | 'once'; + handler: (editor: Editor, ...args) => void; +} + +export type ShortCutsConfig = Array; + +export interface ShortCutConfig { + keyboard: string; + handler: (editor: Editor, ev: React.KeyboardEventHandler, keymaster: any) => void; +} + +export type UtilsConfig = Array; + +export interface UtilConfig { + name: string; + type: 'npm' | 'function'; + content: NpmConfig | ((...args) => any); +} + +export type ConstantsConfig = object; + +export interface lifeCyclesConfig { + init?: (editor: Editor) => any; + destroy?: (editor: Editor) => any; +} + +export type LocaleType = 'zh-CN' | 'zh-TW' | 'en-US' | 'ja-JP'; + +export interface I18nMessages { + [propName: string]: string; +} + +export interface I18nConfig { + 'zh-CN'?: I18nMessages; + 'zh-TW'?: I18nMessages; + 'en-US'?: I18nMessages; + 'ja-JP'?: I18nMessages; +} + +export type I18nFunction = (key: string, params: object) => string; + +export interface Utils { + [propName: string]: (...args) => any; +} + +export interface PluginClass extends React.Component { + init?: (editor: Editor) => void; +} + +export interface PluginComponents { + [propName: string]: PluginClass; +} + +export interface PluginStatus { + [propName: string]: { + disabled: boolean; + visible: boolean; + marked: boolean; + }; +} diff --git a/packages/editor/src/framework/editor.ts b/packages/editor/src/framework/editor.ts index 78f1209ab..4918a4f4b 100644 --- a/packages/editor/src/framework/editor.ts +++ b/packages/editor/src/framework/editor.ts @@ -1,13 +1,9 @@ import EventEmitter from 'events'; import Debug from 'debug'; import store from 'store'; +import { EditorConfig, Utils, PluginComponents, PluginStatus, LocaleType, HooksConfig } from './definitions'; -import { - unRegistShortCuts, - registShortCuts, - transformToPromise, - generateI18n, -} from './utils'; +import { unRegistShortCuts, registShortCuts, transformToPromise } from './utils'; // 根据url参数设置debug选项 const res = /_?debug=(.*?)(&|$)/.exec(location.search); @@ -39,35 +35,47 @@ window.onbeforeunload = function(e) { return msg; }; -let instance = null; +let instance: Editor; + const debug = Debug('editor'); EventEmitter.defaultMaxListeners = 100; +export interface HooksFuncs { + [idx: number]: (msg: string, handler: (...args) => void) => void; +} export default class Editor extends EventEmitter { - static getInstance = () => { + static getInstance = (config: EditorConfig, components: PluginComponents, utils?: Utils): Editor => { if (!instance) { - instance = new Editor(); + instance = new Editor(config, components, utils); } return instance; }; - constructor(config, utils, components) { + private hooksFuncs: HooksFuncs; + + public pluginStatus: PluginStatus; + public plugins: PluginComponents; + public locale: LocaleType; + + public emit: (msg: string, ...args) => void; + public on: (msg: string, handler: (...args) => void) => void; + public once: (msg: string, handler: (...args) => void) => void; + public off: (msg: string, handler: (...args) => void) => void; + + constructor(public config: EditorConfig, public components: PluginComponents, public utils?: Utils) { super(); instance = this; - this.config = config; - this.utils = utils; - this.components = components; this.init(); } - init() { - const { hooks, shortCuts, lifeCycles } = this.config || {}; + init(): Promise { + const { hooks, shortCuts = [], lifeCycles } = this.config || {}; this.locale = store.get('lowcode-editor-locale') || 'zh-CN'; // this.messages = this.messagesSet[this.locale]; // this.i18n = generateI18n(this.locale, this.messages); this.pluginStatus = this.initPluginStatus(); - this.initHooks(hooks); + this.initHooks(hooks || []); this.emit('editor.beforeInit'); const init = (lifeCycles && lifeCycles.init) || (() => {}); @@ -77,6 +85,7 @@ export default class Editor extends EventEmitter { // 注册快捷键 registShortCuts(shortCuts, this); this.emit('editor.afterInit'); + return true; }) .catch(err => { console.error(err); @@ -84,11 +93,12 @@ export default class Editor extends EventEmitter { } destroy() { + debug('destroy'); try { const { hooks = [], shortCuts = [], lifeCycles = {} } = this.config; unRegistShortCuts(shortCuts); this.destroyHooks(hooks); - lifeCycles.destroy && lifeCycles.destroy(); + lifeCycles.destroy && lifeCycles.destroy(this); } catch (err) { console.warn(err); return; @@ -101,20 +111,8 @@ export default class Editor extends EventEmitter { set(key: string | object, val: any): void { if (typeof key === 'string') { - if ( - [ - 'init', - 'destroy', - 'get', - 'set', - 'batchOn', - 'batchOff', - 'batchOnce', - ].includes(key) - ) { - console.warning( - 'init, destroy, get, set, batchOn, batchOff, batchOnce is private attribute', - ); + if (['init', 'destroy', 'get', 'set', 'batchOn', 'batchOff', 'batchOnce'].includes(key)) { + console.error('init, destroy, get, set, batchOn, batchOff, batchOnce is private attribute'); return; } this[key] = val; @@ -125,34 +123,34 @@ export default class Editor extends EventEmitter { } } - batchOn(events: Array, lisenter: function): void { + batchOn(events: Array, lisenter: (...args) => void): void { if (!Array.isArray(events)) return; events.forEach(event => this.on(event, lisenter)); } - batchOnce(events: Array, lisenter: function): void { + batchOnce(events: Array, lisenter: (...args) => void): void { if (!Array.isArray(events)) return; events.forEach(event => this.once(event, lisenter)); } - batchOff(events: Array, lisenter: function): void { + batchOff(events: Array, lisenter: (...args) => void): void { if (!Array.isArray(events)) return; events.forEach(event => this.off(event, lisenter)); } //销毁hooks中的消息监听 - private destroyHooks(hooks = []) { + private destroyHooks(hooks: HooksConfig = []) { hooks.forEach((item, idx) => { - if (typeof this.__hooksFuncs[idx] === 'function') { - this.off(item.message, this.__hooksFuncs[idx]); + if (typeof this.hooksFuncs[idx] === 'function') { + this.off(item.message, this.hooksFuncs[idx]); } }); - delete this.__hooksFuncs; + delete this.hooksFuncs; } //初始化hooks中的消息监听 - private initHooks(hooks = []) { - this.__hooksFuncs = hooks.map(item => { + private initHooks(hooks: HooksConfig = []): void { + this.hooksFuncs = hooks.map(item => { const func = (...args) => { item.handler(this, ...args); }; @@ -168,11 +166,11 @@ export default class Editor extends EventEmitter { pluginAreas.forEach(area => { (plugins[area] || []).forEach(plugin => { if (plugin.type === 'Divider') return; - const { visible, disabled, dotted } = plugin.props || {}; + const { visible, disabled, marked } = plugin.props || {}; res[plugin.pluginKey] = { visible: typeof visible === 'boolean' ? visible : true, disabled: typeof disabled === 'boolean' ? disabled : false, - dotted: typeof dotted === 'boolean' ? dotted : false, + marked: typeof marked === 'boolean' ? marked : false }; const pluginClass = this.components[plugin.pluginKey]; // 判断如果编辑器插件有init静态方法,则在此执行init方法 diff --git a/packages/editor/src/framework/index.ts b/packages/editor/src/framework/index.ts index 8b82edf2e..547d9db52 100644 --- a/packages/editor/src/framework/index.ts +++ b/packages/editor/src/framework/index.ts @@ -1,3 +1,10 @@ import Editor from './editor'; +export { default as PluginFactory } from './plugin'; +export { default as EditorContext } from './context'; +import * as editorUtils from './utils'; +import * as editorDefinitions from './definitions'; export default Editor; + +export const utils = editorUtils; +export const definitions = editorDefinitions; diff --git a/packages/editor/src/framework/plugin.js b/packages/editor/src/framework/plugin.tsx similarity index 52% rename from packages/editor/src/framework/plugin.js rename to packages/editor/src/framework/plugin.tsx index 43bf5ef30..c89a386bb 100644 --- a/packages/editor/src/framework/plugin.js +++ b/packages/editor/src/framework/plugin.tsx @@ -1,23 +1,41 @@ -import React, { PureComponent, creatRef} from 'react'; +import React, { PureComponent, createRef } from 'react'; import EditorContext from './context'; -import { isEmpty, generateI18n, goldlog } from './utils'; +import Editor from './editor'; +import { isEmpty, generateI18n } from './utils'; +import { PluginConfig, I18nFunction } from './definitions'; +export interface PluginProps { + editor: Editor; + config: PluginConfig; +} -export default function plugin(Comp) { - class LowcodePlugin extends PureComponent { +export interface InjectedPluginProps { + i18n?: I18nFunction; +} + +export default function plugin( + Comp: React.ComponentType +): React.ComponentType { + class LowcodePlugin extends PureComponent { static displayName = 'LowcodeEditorPlugin'; static defaultProps = { - config: {}, + config: {} }; static contextType = EditorContext; + static init = Comp.init; + public ref = createRef(); + private editor: Editor; + private pluginKey: string; + private i18n: I18nFunction; + constructor(props, context) { super(props, context); + if (isEmpty(props.config) || !props.config.pluginKey) { console.warn('lowcode editor plugin has wrong config'); return; } - this.ref = React.createRef(); const { locale, messages, editor } = props; // 注册插件 this.editor = editor; @@ -36,19 +54,9 @@ export default function plugin(Comp) { render() { const { config } = this.props; - return ( - - ); + return ; } } - LowcodePlugin.init = Comp.init; - return LowcodePlugin; } diff --git a/packages/editor/src/framework/utils.ts b/packages/editor/src/framework/utils.ts index 59a32d270..dd6ace7d4 100644 --- a/packages/editor/src/framework/utils.ts +++ b/packages/editor/src/framework/utils.ts @@ -1,15 +1,41 @@ import IntlMessageFormat from 'intl-messageformat'; import keymaster from 'keymaster'; import _isEmpty from 'lodash/isEmpty'; +import { EditorConfig, LocaleType, I18nMessages, I18nFunction, ShortCutsConfig } from './definitions'; +import Editor from './editor'; export const isEmpty = _isEmpty; +const ENV = { + TBE: 'TBE', + WEBIDE: 'WEB-IDE', + VSCODE: 'VSCODE', + WEB: 'WEB' +}; + +export interface IDEMessageParams { + action: string; + data: { + logKey: string; + gmKey: string; + goKey: string; + }; +} + +export interface Window { + sendIDEMessage: (IDEMessageParams) => void; + goldlog: { + record: (logKey: string, gmKey: string, goKey: string, method: 'GET' | 'POST') => void; + }; + parent: Window; + is_theia: boolean; + vscode: boolean; +} + /** * 用于构造国际化字符串处理函数 - * @param {*} locale 国际化标识,例如 zh-CN、en-US - * @param {*} messages 国际化语言包 */ -export function generateI18n(locale = 'zh-CN', messages = {}) { +export function generateI18n(locale: LocaleType = 'zh-CN', messages: I18nMessages = {}): I18nFunction { return (key, values = {}) => { if (!messages || !messages[key]) return ''; const formater = new IntlMessageFormat(messages[key], locale); @@ -19,18 +45,14 @@ export function generateI18n(locale = 'zh-CN', messages = {}) { /** * 序列化参数 - * @param {*} obj 参数 */ export function serializeParams(obj: object): string { if (typeof obj !== 'object') return ''; - const res: Array = []; Object.entries(obj).forEach(([key, val]) => { if (val === null || val === undefined || val === '') return; if (typeof val === 'object') { - res.push( - `${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`, - ); + res.push(`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`); } else { res.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`); } @@ -44,12 +66,12 @@ export function serializeParams(obj: object): string { * @param {Object} params 参数 * @param {String} logKey 属性串 */ -export function goldlog(gmKey, params = {}, logKey = 'other') { - const sendIDEMessage = window.sendIDEMessage || window.parent.sendIDEMessage; +export function goldlog(gmKey: string, params: object = {}, logKey: string = 'other'): void { + const global = window as Window; + const sendIDEMessage = global.sendIDEMessage || global.parent.sendIDEMessage; const goKey = serializeParams({ - sdkVersion: pkg.version, env: getEnv(), - ...params, + ...params }); if (sendIDEMessage) { sendIDEMessage({ @@ -57,18 +79,17 @@ export function goldlog(gmKey, params = {}, logKey = 'other') { data: { logKey: `/iceluna.core.${logKey}`, gmKey, - goKey, - }, + goKey + } }); } - window.goldlog && - window.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST'); + global.goldlog && global.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST'); } /** * 获取当前编辑器环境 */ -export function getEnv() { +export function getEnv(): string { const userAgent = navigator.userAgent; const isVscode = /Electron\//.test(userAgent); if (isVscode) return ENV.VSCODE; @@ -78,131 +99,17 @@ export function getEnv() { } // 注册快捷键 -export function registShortCuts(config, editor) { - const keyboardFilter = (keymaster.filter = event => { - let eTarget = event.target || event.srcElement; - let tagName = eTarget.tagName; - let isInput = !!( - tagName == 'INPUT' || - tagName == 'SELECT' || - tagName == 'TEXTAREA' - ); - let isContenteditable = !!eTarget.getAttribute('contenteditable'); - if (isInput || isContenteditable) { - if (event.metaKey === true && [70, 83].includes(event.keyCode)) - event.preventDefault(); //禁止触发chrome原生的页面保存或查找 - return false; - } else { - return true; - } - }); - - const ideMessage = editor.utils && editor.utils.ideMessage; - - //复制 - if (!document.copyListener) { - document.copyListener = e => { - if (!keyboardFilter(e) || editor.isCopying) return; - const schema = - editor.schemaHelper && - editor.schemaHelper.schemaMap[editor.activeKey]; - if (!schema || !isSchema(schema)) return; - editor.isCopying = true; - const schemaStr = serialize(transformSchemaToPure(schema), { - unsafe: true, - }); - setClipboardData(schemaStr) - .then(() => { - ideMessage && - ideMessage( - 'success', - '当前内容已复制到剪贴板,请使用快捷键Command+v进行粘贴', - ); - editor.emit('schema.copy', schemaStr, schema); - editor.isCopying = false; - }) - .catch(errMsg => { - ideMessage && ideMessage('error', errMsg); - editor.isCopying = false; - }); - }; - document.addEventListener('copy', document.copyListener); - if (window.parent.vscode) { - keymaster('command+c', document.copyListener); - } - } - - //粘贴 - if (!document.pasteListener) { - const doPaste = (e, text) => { - if (!keyboardFilter(e) || editor.isPasting) return; - const schemaHelper = editor.schemaHelper; - let targetKey = editor.activeKey; - let direction = 'after'; - const topKey = - schemaHelper.schema && - schemaHelper.schema.__ctx && - schemaHelper.schema.__ctx.lunaKey; - if (!targetKey || topKey === targetKey) { - const schemaHelper = editor.schemaHelper; - const topKey = - schemaHelper.schema && - schemaHelper.schema.__ctx && - schemaHelper.schema.__ctx.lunaKey; - if (!topKey) return; - targetKey = topKey; - direction = 'in'; - } - editor.isPasting = true; - const schema = parseObj(text); - if (!isSchema(schema)) { - editor.emit('illegalSchema.paste', text); - // ideMessage && ideMessage('error', '当前内容不是模型结构,不能粘贴进来!'); - console.warn('paste schema illegal'); - editor.isPasting = false; - return; - } - editor.emit('material.add', { - schema, - targetKey, - direction, - }); - editor.isPasting = false; - editor.emit('schema.paste', schema); - }; - document.pasteListener = e => { - const clipboardData = e.clipboardData || window.clipboardData; - const text = clipboardData && clipboardData.getData('text'); - doPaste(e, text); - }; - document.addEventListener('paste', document.pasteListener); - if (window.parent.vscode) { - keymaster('command+v', e => { - const sendIDEMessage = window.parent.sendIDEMessage; - sendIDEMessage && - sendIDEMessage({ - action: 'readClipboard', - }) - .then(text => { - doPaste(e, text); - }) - .catch(err => { - console.warn(err); - }); - }); - } - } - +export function registShortCuts(config: ShortCutsConfig, editor: Editor): void { (config || []).forEach(item => { keymaster(item.keyboard, ev => { ev.preventDefault(); - item.handler(ev, editor, keymaster); + item.handler(editor, ev, keymaster); }); }); } // 取消注册快捷 -export function unRegistShortCuts(config) { +export function unRegistShortCuts(config: ShortCutsConfig): void { (config || []).forEach(item => { keymaster.unbind(item.keyboard); }); @@ -210,18 +117,12 @@ export function unRegistShortCuts(config) { keymaster.unbind('command+c'); keymaster.unbind('command+v'); } - if (document.copyListener) { - document.removeEventListener('copy', document.copyListener); - delete document.copyListener; - } - if (document.pasteListener) { - document.removeEventListener('paste', document.pasteListener); - delete document.pasteListener; - } } -// 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve -export function transformToPromise(input) { +/** + * 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve + */ +export function transformToPromise(input: any): Promise<{}> { if (input instanceof Promise) return input; return new Promise((resolve, reject) => { if (input || input === undefined) { @@ -232,7 +133,13 @@ export function transformToPromise(input) { }); } -export function transformArrayToMap(arr, key, overwrite = true) { +/** + * 将数组类型转换为Map类型 + */ +interface MapOf { + [propName: string]: T; +} +export function transformArrayToMap(arr: Array, key: string, overwrite: boolean = true): MapOf { if (isEmpty(arr) || !Array.isArray(arr)) return {}; const res = {}; arr.forEach(item => { @@ -244,7 +151,13 @@ export function transformArrayToMap(arr, key, overwrite = true) { return res; } -export function parseSearch(search) { +/** + * 解析url的查询参数 + */ +interface Query { + [propName: string]: string; +} +export function parseSearch(search: string): Query { if (!search || typeof search !== 'string') return {}; const str = search.replace(/^\?/, ''); let paramStr = str.split('&'); @@ -258,63 +171,50 @@ export function parseSearch(search) { return res; } -export function comboEditorConfig(defaultConfig = {}, customConfig = {}) { - const { - skeleton, - theme, - plugins, - hooks, - shortCuts, - lifeCycles, - constants, - utils, - i18n, - } = customConfig || {}; +export function comboEditorConfig(defaultConfig: EditorConfig = {}, customConfig: EditorConfig): EditorConfig { + const { skeleton, theme, plugins, hooks, shortCuts, lifeCycles, constants, utils, i18n } = customConfig || {}; if (skeleton && skeleton.handler && typeof skeleton.handler === 'function') { return skeleton.handler({ skeleton, - ...defaultConfig, + ...defaultConfig }); } - const defaultShortCuts = transformArrayToMap( - defaultConfig.shortCuts, - 'keyboard', - ); - const customShortCuts = transformArrayToMap(shortCuts, 'keyboard'); + const defaultShortCuts = transformArrayToMap(defaultConfig.shortCuts || [], 'keyboard'); + const customShortCuts = transformArrayToMap(shortCuts || [], 'keyboard'); const localeList = ['zh-CN', 'zh-TW', 'en-US', 'ja-JP']; const i18nConfig = {}; localeList.forEach(key => { i18nConfig[key] = { ...(defaultConfig.i18n && defaultConfig.i18n[key]), - ...(i18n && i18n[key]), + ...(i18n && i18n[key]) }; }); return { skeleton, theme: { ...defaultConfig.theme, - ...theme, + ...theme }, plugins: { ...defaultConfig.plugins, - ...plugins, + ...plugins }, hooks: [...(defaultConfig.hooks || []), ...(hooks || [])], shortCuts: Object.values({ ...defaultShortCuts, - ...customShortCuts, + ...customShortCuts }), lifeCycles: { ...defaultConfig.lifeCycles, - ...lifeCycles, + ...lifeCycles }, constants: { ...defaultConfig.constants, - ...constants, + ...constants }, utils: [...(defaultConfig.utils || []), ...(utils || [])], - i18n: i18nConfig, + i18n: i18nConfig }; } diff --git a/packages/editor/src/global.scss b/packages/editor/src/global.scss index 9b8705bdc..4802a89d4 100644 --- a/packages/editor/src/global.scss +++ b/packages/editor/src/global.scss @@ -1,6 +1,5 @@ body { - font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, - Arial, PingFang SC-Light, Microsoft YaHei; + font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, Arial, PingFang SC-Light, Microsoft YaHei; font-size: 12px; * { box-sizing: border-box; diff --git a/packages/editor/src/index.tsx b/packages/editor/src/index.tsx index 43efc5e8e..1716f05a8 100644 --- a/packages/editor/src/index.tsx +++ b/packages/editor/src/index.tsx @@ -28,5 +28,5 @@ ReactDOM.render( constants={constants} components={components} />, - ICE_CONTAINER, + ICE_CONTAINER ); diff --git a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx b/packages/editor/src/skeleton/components/LeftPlugin/index.tsx index e32a4dcc0..7721ad3c2 100644 --- a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx +++ b/packages/editor/src/skeleton/components/LeftPlugin/index.tsx @@ -1,26 +1,42 @@ import React, { PureComponent, Fragment } from 'react'; - -import PropTypes from 'prop-types'; import classNames from 'classnames'; -import { Balloon, Dialog, Icon, Badge } from '@alife/next'; +import { Balloon, Dialog, Icon, Badge } from '@alifd/next'; import './index.scss'; -export default class LeftPlugin extends PureComponent { - static displayName = 'lowcodeLeftPlugin'; +import Editor from '../../../framework/editor'; +import { PluginConfig, PluginClass } from '../../../framework/definitions'; + +export interface LeftPluginProps { + active?: boolean; + config: PluginConfig; + disabled?: boolean; + editor: Editor; + locked?: boolean; + marked?: boolean; + onClick?: () => void; + pluginClass: PluginClass; +} + +export interface LeftPluginState { + dialogVisible: boolean; +} + +export default class LeftPlugin extends PureComponent { + static displayName = 'LowcodeLeftPlugin'; static defaultProps = { active: false, config: {}, disabled: false, - dotted: false, + marked: false, locked: false, - onClick: () => {}, + onClick: () => {} }; constructor(props, context) { super(props, context); this.state = { - dialogVisible: false, + dialogVisible: false }; } @@ -60,7 +76,7 @@ export default class LeftPlugin extends PureComponent { handleOpen = () => { // todo 对话框类型的插件初始时拿不到插件实例 this.setState({ - dialogVisible: true, + dialogVisible: true }); }; @@ -75,15 +91,7 @@ export default class LeftPlugin extends PureComponent { }; renderIcon = clickCallback => { - const { - active, - disabled, - dotted, - locked, - onClick, - config, - editor, - } = this.props; + const { active, disabled, marked, locked, onClick, config, editor } = this.props; const { pluginKey, props } = config || {}; const { icon, title } = props || {}; return ( @@ -91,7 +99,7 @@ export default class LeftPlugin extends PureComponent { className={classNames('lowcode-left-plugin', pluginKey, { active, disabled, - locked, + locked })} data-tooltip={title} onClick={() => { @@ -101,7 +109,7 @@ export default class LeftPlugin extends PureComponent { onClick && onClick(); }} > - {dotted ? ( + {marked ? ( @@ -113,15 +121,7 @@ export default class LeftPlugin extends PureComponent { }; render() { - const { - dotted, - locked, - active, - disabled, - config, - editor, - pluginClass: Comp, - } = this.props; + const { marked, locked, active, disabled, config, editor, pluginClass: Comp } = this.props; const { pluginKey, props, type, pluginProps } = config || {}; const { onClick, title } = props || {}; const { dialogVisible } = this.state; @@ -197,7 +197,7 @@ export default class LeftPlugin extends PureComponent { this.handleOpen(); }); case 'Custom': - return dotted ? {node} : node; + return marked ? {node} : node; default: return null; } diff --git a/packages/editor/src/skeleton/components/Panel/index.tsx b/packages/editor/src/skeleton/components/Panel/index.tsx index 12b44c51b..f057e2a20 100644 --- a/packages/editor/src/skeleton/components/Panel/index.tsx +++ b/packages/editor/src/skeleton/components/Panel/index.tsx @@ -1,8 +1,13 @@ import React, { PureComponent } from 'react'; import './index.scss'; -export default class Panel extends PureComponent { - static displayName = 'Panel'; + +export interface PanelProps { + children: Plugin; +} + +export default class Panel extends PureComponent { + static displayName = 'LowcodePanel'; constructor(props) { super(props); @@ -13,7 +18,7 @@ export default class Panel extends PureComponent {
{this.props.children} diff --git a/packages/editor/src/skeleton/components/TopIcon/index.tsx b/packages/editor/src/skeleton/components/TopIcon/index.tsx index 8e81c9c3a..afd1955f5 100644 --- a/packages/editor/src/skeleton/components/TopIcon/index.tsx +++ b/packages/editor/src/skeleton/components/TopIcon/index.tsx @@ -1,23 +1,25 @@ import React, { PureComponent } from 'react'; - -import PropTypes from 'prop-types'; import classNames from 'classnames'; import { Icon, Button } from '@alifd/next'; + import './index.scss'; -export default class TopIcon extends PureComponent { - static displayName = 'TopIcon'; - static propTypes = { - active: PropTypes.bool, - className: PropTypes.string, - disabled: PropTypes.bool, - icon: PropTypes.string, - id: PropTypes.string, - locked: PropTypes.bool, - onClick: PropTypes.func, - showTitle: PropTypes.bool, - style: PropTypes.object, - title: PropTypes.string, - }; + +export interface TopIconProps { + active?: boolean; + className?: string; + disabled?: boolean; + icon: string; + id?: string; + locked?: boolean; + marked?: boolean; + onClick?: () => void; + showTitle?: boolean; + style?: React.CSSProperties; + title?: string; +} + +export default class TopIcon extends PureComponent { + static displayName = 'LowcodeTopIcon'; static defaultProps = { active: false, className: '', @@ -28,22 +30,11 @@ export default class TopIcon extends PureComponent { onClick: () => {}, showTitle: false, style: {}, - title: '', + title: '' }; render() { - const { - active, - disabled, - icon, - locked, - title, - className, - id, - style, - showTitle, - onClick, - } = this.props; + const { active, disabled, icon, locked, title, className, id, style, showTitle, onClick } = this.props; return (