From 3812a6c0021cffb5c2e54491a2483af485e1d149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8B=E7=BE=8A?= Date: Tue, 10 Mar 2020 14:29:29 +0800 Subject: [PATCH] feat:daily tag --- packages/editor/src/config/components.js | 6 + packages/editor/src/config/skeleton.js | 29 +++++ packages/editor/src/framework/areaManager.ts | 15 +-- packages/editor/src/framework/definitions.ts | 2 + .../editor/src/framework/pluginFactory.tsx | 26 ++++- packages/editor/src/framework/utils.ts | 12 ++ .../editor/src/plugins/designer/index.scss | 0 .../editor/src/plugins/designer/index.tsx | 19 ++++ packages/editor/src/plugins/logo/index.scss | 9 ++ packages/editor/src/plugins/logo/index.tsx | 18 +++ .../skeleton/components/LeftPlugin/index.tsx | 62 +++++------ .../src/skeleton/components/Panel/index.tsx | 38 ++++++- .../skeleton/components/TopPlugin/index.tsx | 58 +++++----- .../src/skeleton/layouts/CenterArea/index.tsx | 1 - .../src/skeleton/layouts/LeftArea/nav.tsx | 104 +++++++++++++++++- .../src/skeleton/layouts/LeftArea/panel.tsx | 40 +++++-- .../src/skeleton/layouts/RightArea/index.tsx | 9 +- 17 files changed, 353 insertions(+), 95 deletions(-) create mode 100644 packages/editor/src/plugins/designer/index.scss create mode 100644 packages/editor/src/plugins/designer/index.tsx create mode 100644 packages/editor/src/plugins/logo/index.scss create mode 100644 packages/editor/src/plugins/logo/index.tsx diff --git a/packages/editor/src/config/components.js b/packages/editor/src/config/components.js index b834c7fba..7c7041401 100644 --- a/packages/editor/src/config/components.js +++ b/packages/editor/src/config/components.js @@ -1,6 +1,9 @@ +import logo from '../plugins/logo'; +import designer from '../plugins/designer'; import topBalloonIcon from '@ali/iceluna-addon-2'; import topDialogIcon from '@ali/iceluna-addon-2'; import leftPanelIcon from '@ali/iceluna-addon-2'; +import leftPanelIcon2 from '@ali/iceluna-addon-2'; import leftBalloonIcon from '@ali/iceluna-addon-2'; import leftDialogIcon from '@ali/iceluna-addon-2'; import rightPanel1 from '@ali/iceluna-addon-2'; @@ -11,9 +14,12 @@ import rightPanel4 from '@ali/iceluna-addon-2'; import PluginFactory from '../framework/pluginFactory'; export default { + logo: PluginFactory(logo), + designer: PluginFactory(designer), topBalloonIcon: PluginFactory(topBalloonIcon), topDialogIcon: PluginFactory(topDialogIcon), leftPanelIcon: PluginFactory(leftPanelIcon), + leftPanelIcon2: PluginFactory(leftPanelIcon2), leftBalloonIcon: PluginFactory(leftBalloonIcon), leftDialogIcon: PluginFactory(leftDialogIcon), rightPanel1: PluginFactory(rightPanel1), diff --git a/packages/editor/src/config/skeleton.js b/packages/editor/src/config/skeleton.js index d01f86973..499283d61 100644 --- a/packages/editor/src/config/skeleton.js +++ b/packages/editor/src/config/skeleton.js @@ -13,6 +13,21 @@ export default { utils: [], plugins: { topArea: [ + { + pluginKey: 'logo', + type: 'Custom', + props: { + align: 'left', + width: 100 + }, + config: { + package: '@ali/lowcode-plugin-logo', + version: '1.0.0' + }, + pluginProps: { + logo: 'https://img.alicdn.com/tfs/TB1mHYDxQP2gK0jSZPxXXacQpXa-112-64.png' + } + }, { pluginKey: 'topBalloonIcon', type: 'BalloonIcon', @@ -110,6 +125,20 @@ export default { }, pluginProps: {} }, + { + pluginKey: 'leftPanelIcon2', + type: 'PanelIcon', + props: { + align: 'top', + title: 'panel2', + icon: 'dengpao' + }, + config: { + package: '@ali/iceluna-addon-2', + version: '^1.0.0' + }, + pluginProps: {} + }, { pluginKey: 'leftDialogIcon', type: 'DialogIcon', diff --git a/packages/editor/src/framework/areaManager.ts b/packages/editor/src/framework/areaManager.ts index f65b25de6..6dd029b45 100644 --- a/packages/editor/src/framework/areaManager.ts +++ b/packages/editor/src/framework/areaManager.ts @@ -1,6 +1,6 @@ import Editor from './index'; import { PluginConfig, PluginStatus } from './definitions'; -import { clone, deepEqual } from './utils'; +import { clone, deepEqual, transformToPromise } from './utils'; export default class AreaManager { private pluginStatus: PluginStatus; @@ -10,19 +10,20 @@ export default class AreaManager { this.pluginStatus = clone(editor.pluginStatus); } - isPluginStatusUpdate(): boolean { + isPluginStatusUpdate(pluginType?: string): boolean { const { pluginStatus } = this.editor; - const isUpdate = this.config.some( - item => !deepEqual(pluginStatus[item.pluginKey], this.pluginStatus[item.pluginKey]) - ); + const list = pluginType ? this.config.filter(item => item.type === pluginType) : this.config; + + const isUpdate = list.some(item => !deepEqual(pluginStatus[item.pluginKey], this.pluginStatus[item.pluginKey])); this.pluginStatus = clone(pluginStatus); return isUpdate; } - getVisiblePluginList(): Array { - return this.config.filter(item => { + getVisiblePluginList(pluginType?: string): Array { + const res = this.config.filter(item => { return !this.pluginStatus[item.pluginKey] || this.pluginStatus[item.pluginKey].visible; }); + return pluginType ? res.filter(item => item.type === pluginType) : res; } getPluginConfig(): Array { diff --git a/packages/editor/src/framework/definitions.ts b/packages/editor/src/framework/definitions.ts index e43d85d4c..5e30a4466 100644 --- a/packages/editor/src/framework/definitions.ts +++ b/packages/editor/src/framework/definitions.ts @@ -114,6 +114,8 @@ export interface Utils { export interface PluginClass extends React.Component { init?: (editor: Editor) => void; + open?: () => any; + close?: () => any; } export interface PluginComponents { diff --git a/packages/editor/src/framework/pluginFactory.tsx b/packages/editor/src/framework/pluginFactory.tsx index c5b038013..b3a83ee83 100644 --- a/packages/editor/src/framework/pluginFactory.tsx +++ b/packages/editor/src/framework/pluginFactory.tsx @@ -2,8 +2,9 @@ import React, { PureComponent, createRef } from 'react'; import EditorContext from './context'; import Editor from './editor'; -import { isEmpty, generateI18n } from './utils'; +import { isEmpty, generateI18n, transformToPromise, acceptsRef } from './utils'; import { PluginConfig, I18nFunction } from './definitions'; +import Editor from './index'; export interface PluginProps { editor: Editor; @@ -53,17 +54,32 @@ export default function pluginFactory( } } - open = () => { - return this.ref && this.ref.open && this.ref.open(); + open = (): Promise => { + if (this.ref && this.ref.open && typeof this.ref.open === 'function') { + return transformToPromise(this.ref.open()); + } + return Promise.resolve(); }; close = () => { - return this.ref && this.ref.close && this.ref.close(); + if (this.ref && this.ref.close && typeof this.ref.close === 'function') { + return transformToPromise(this.ref.close()); + } + return Promise.resolve(); }; render() { const { config } = this.props; - return ; + const props = { + i18n: this.i18n, + editor: this.editor, + config, + ...config.pluginProps + }; + if (acceptsRef(Comp)) { + props.ref = this.ref; + } + return ; } } diff --git a/packages/editor/src/framework/utils.ts b/packages/editor/src/framework/utils.ts index 22b556e4c..d4a6e4061 100644 --- a/packages/editor/src/framework/utils.ts +++ b/packages/editor/src/framework/utils.ts @@ -232,3 +232,15 @@ export function comboEditorConfig(defaultConfig: EditorConfig = {}, customConfig i18n: i18nConfig }; } + +/** + * 判断当前组件是否能够设置ref + * @param {*} Comp 需要判断的组件 + */ +export function acceptsRef(Comp: React.ComponentType) { + const hasSymbol = typeof Symbol === 'function' && Symbol['for']; + const REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol['for']('react.forward_ref') : 0xead0; + return ( + (Comp.$$typeof && Comp.$$typeof === REACT_FORWARD_REF_TYPE) || (Comp.prototype && Comp.prototype.isReactComponent) + ); +} diff --git a/packages/editor/src/plugins/designer/index.scss b/packages/editor/src/plugins/designer/index.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/editor/src/plugins/designer/index.tsx b/packages/editor/src/plugins/designer/index.tsx new file mode 100644 index 000000000..d2bbc2ff8 --- /dev/null +++ b/packages/editor/src/plugins/designer/index.tsx @@ -0,0 +1,19 @@ +import React, { PureComponent } from 'react'; + +import Editor from '../../framework/index'; +import { PluginConfig } from '../../framework/definitions'; + +import './index.scss'; + +export interface PluginProps { + editor: Editor; + config: PluginConfig; +} + +export default class DesignerPlugin extends PureComponent { + static displayName: 'LowcodePluginDesigner'; + + constructor(props) {} + + render() {} +} diff --git a/packages/editor/src/plugins/logo/index.scss b/packages/editor/src/plugins/logo/index.scss new file mode 100644 index 000000000..e2701fbca --- /dev/null +++ b/packages/editor/src/plugins/logo/index.scss @@ -0,0 +1,9 @@ +.lowcode-plugin-logo { + padding: 8px 16px; + .logo { + width: 56px; + height: 32px; + background-size: contain; + background-position: center; + } +} diff --git a/packages/editor/src/plugins/logo/index.tsx b/packages/editor/src/plugins/logo/index.tsx new file mode 100644 index 000000000..c02bf7a71 --- /dev/null +++ b/packages/editor/src/plugins/logo/index.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import './index.scss'; +import Editor from '../../framework/index'; +import { PluginConfig } from '../../framework/definitions'; + +export interface PluginProps { + editor: Editor; + config: PluginConfig; + logo?: string; +} + +export default function(props: PluginProps) { + return ( +
+
+
+ ); +} diff --git a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx b/packages/editor/src/skeleton/components/LeftPlugin/index.tsx index 7721ad3c2..6d807548c 100644 --- a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx +++ b/packages/editor/src/skeleton/components/LeftPlugin/index.tsx @@ -41,36 +41,34 @@ export default class LeftPlugin extends PureComponent { - // const addonKey = this.props.config && this.props.config.addonKey; - // const currentAddon = - // this.appHelper.addons && this.appHelper.addons[addonKey]; - // if (currentAddon) { - // this.utils.transformToPromise(currentAddon.close()).then(() => { - // this.setState({ - // dialogVisible: false, - // }); - // }); - // } + const { config, editor } = this.props; + const pluginKey = config && config.pluginKey; + const plugin = editor.plugins && editor.plugins[pluginKey]; + if (plugin) { + plugin.close().then(() => { + this.setState({ + dialogVisible: false + }); + }); + } }; handleOpen = () => { @@ -81,17 +79,17 @@ export default class LeftPlugin extends PureComponent { - // const { disabled, config, onClick } = this.props; - // const addonKey = config && config.addonKey; - // if (disabled || !addonKey) return; - // //考虑到弹窗情况,延时发送消息 - // setTimeout(() => this.appHelper.emit(`${addonKey}.addon.activate`), 0); - // this.handleOpen(); - // onClick && onClick(); + const { disabled, config, onClick, editor } = this.props; + const pluginKey = config && config.pluginKey; + if (disabled || !pluginKey) return; + //考虑到弹窗情况,延时发送消息 + setTimeout(() => editor.emit(`${pluginKey}.addon.activate`), 0); + this.handleOpen(); + onClick && onClick(); }; renderIcon = clickCallback => { - const { active, disabled, marked, locked, onClick, config, editor } = this.props; + const { active, disabled, marked, locked, onClick, config } = this.props; const { pluginKey, props } = config || {}; const { icon, title } = props || {}; return ( diff --git a/packages/editor/src/skeleton/components/Panel/index.tsx b/packages/editor/src/skeleton/components/Panel/index.tsx index f057e2a20..a1b9a9a8e 100644 --- a/packages/editor/src/skeleton/components/Panel/index.tsx +++ b/packages/editor/src/skeleton/components/Panel/index.tsx @@ -1,27 +1,59 @@ import React, { PureComponent } from 'react'; +import classNames from 'classnames'; import './index.scss'; export interface PanelProps { + align: 'left' | 'right'; + defaultWidth: number; + minWidth: number; + draggable: boolean; + floatable: boolean; children: Plugin; + visible: boolean; } -export default class Panel extends PureComponent { +export interface PanelState { + width: number; +} + +export default class Panel extends PureComponent { static displayName = 'LowcodePanel'; + static defaultProps = { + align: 'left', + defaultWidth: 240, + minWidth: 100, + draggable: true, + floatable: false, + visible: true + }; + constructor(props) { super(props); + + this.state = { + width: props.defaultWidth + }; } render() { + const { align, draggable, floatable, visible } = this.props; + const { width } = this.state; return (
{this.props.children} +
); } diff --git a/packages/editor/src/skeleton/components/TopPlugin/index.tsx b/packages/editor/src/skeleton/components/TopPlugin/index.tsx index 3c531524c..6c4f280d0 100644 --- a/packages/editor/src/skeleton/components/TopPlugin/index.tsx +++ b/packages/editor/src/skeleton/components/TopPlugin/index.tsx @@ -42,46 +42,44 @@ export default class TopPlugin extends PureComponent { - // const { disabled, config, onClick, editor } = this.props; - // const pluginKey = config && config.pluginKey; - // if (disabled || !pluginKey) return; - // //考虑到弹窗情况,延时发送消息 - // setTimeout(() => editor.emit(`${pluginKey}.addon.activate`), 0); - // this.handleOpen(); - // onClick && onClick(); + const { disabled, config, onClick, editor } = this.props; + const pluginKey = config && config.pluginKey; + if (disabled || !pluginKey) return; + //考虑到弹窗情况,延时发送消息 + setTimeout(() => editor.emit(`${pluginKey}.addon.activate`), 0); + this.handleOpen(); + onClick && onClick(); }; handleClose = () => { - // const pluginKey = this.props.config && this.props.config.pluginKey; - // const currentAddon = - // this.appHelper.addons && this.appHelper.addons[pluginKey]; - // if (currentAddon) { - // this.utils.transformToPromise(currentAddon.close()).then(() => { - // this.setState({ - // dialogVisible: false, - // }); - // }); - // } + const { config, editor } = this.props; + const pluginKey = config && config.pluginKey; + const plugin = editor.plugins && editor.plugins[pluginKey]; + if (plugin) { + plugin.close().then(() => { + this.setState({ + dialogVisible: false + }); + }); + } }; handleOpen = () => { diff --git a/packages/editor/src/skeleton/layouts/CenterArea/index.tsx b/packages/editor/src/skeleton/layouts/CenterArea/index.tsx index 95748c593..21e6a7a71 100644 --- a/packages/editor/src/skeleton/layouts/CenterArea/index.tsx +++ b/packages/editor/src/skeleton/layouts/CenterArea/index.tsx @@ -1,7 +1,6 @@ import React, { PureComponent } from 'react'; import Editor from '../../../framework/editor'; -import { PluginConfig } from '../../../framework/definitions'; import './index.scss'; import AreaManager from '../../../framework/areaManager'; diff --git a/packages/editor/src/skeleton/layouts/LeftArea/nav.tsx b/packages/editor/src/skeleton/layouts/LeftArea/nav.tsx index 10ea81f0b..a5ed5cd8f 100644 --- a/packages/editor/src/skeleton/layouts/LeftArea/nav.tsx +++ b/packages/editor/src/skeleton/layouts/LeftArea/nav.tsx @@ -9,23 +9,38 @@ export interface LeftAreaNavProps { editor: Editor; } -export default class LeftAreaNav extends PureComponent { +export interface LeftAreaNavState { + activeKey: string; +} + +export default class LeftAreaNav extends PureComponent { static displayName = 'LowcodeLeftAreaNav'; private editor: Editor; private areaManager: AreaManager; + private cacheActiveKey: string; constructor(props) { super(props); this.editor = props.editor; this.areaManager = new AreaManager(this.editor, 'leftArea'); + + this.state = { + activeKey: 'none' + }; + this.cacheActiveKey = 'none'; } componentDidMount() { this.editor.on('skeleton.update', this.handleSkeletonUpdate); + this.editor.on('leftNav.change', this.handlePluginChange); + const visiblePanelPluginList = this.areaManager.getVisiblePluginList().filter(item => item.type === 'IconPanel'); + const defaultKey = (visiblePanelPluginList[0] && visiblePanelPluginList[0].pluginKey) || 'componentAttr'; + this.handlePluginChange(defaultKey); } componentWillUnmount() { this.editor.off('skeleton.update', this.handleSkeletonUpdate); + this.editor.off('leftNav.change', this.handlePluginChange); } handleSkeletonUpdate = (): void => { @@ -35,10 +50,75 @@ export default class LeftAreaNav extends PureComponent { } }; - handlePluginClick = (item: PluginConfig): void => {}; + handlePluginChange = (key: string): void => { + const { activeKey } = this.state; + const plugins = this.editor.plugins; + const prePlugin = plugins[activeKey]; + const nextPlugin = plugins[key]; + if (activeKey === 'none') { + if (nextPlugin) { + nextPlugin.open().then(() => { + this.updateActiveKey(key); + }); + } + } else if (activeKey === key) { + if (prePlugin) { + prePlugin.close().then(() => { + this.updateActiveKey('none'); + }); + } + } else { + // 先关后开 + if (prePlugin) { + prePlugin.close().then(() => { + if (nextPlugin) { + nextPlugin.open().then(() => { + this.updateActiveKey(key); + }); + } + }); + } + } + }; + + handleCollapseClick = (): void => { + const { activeKey } = this.state; + if (activeKey === 'none') { + const plugin = this.editor.plugins[this.cacheActiveKey]; + if (plugin) { + plugin.open().then(() => { + this.updateActiveKey(this.cacheActiveKey); + }); + } + } else { + const plugin = this.editor.plugins[activeKey]; + if (plugin) { + plugin.close().then(() => { + this.updateActiveKey('none'); + }); + } + } + }; + + handlePluginClick = (item: PluginConfig): void => { + if (item.type === 'PanelIcon') { + this.handlePluginChange(item.pluginKey); + } + }; + + updateActiveKey = (key: string): void => { + if (key === 'none') { + this.cacheActiveKey = this.state.activeKey; + } + this.editor.set('leftNav', key); + this.setState({ activeKey: key }); + this.editor.emit('leftPanel.show', key); + }; renderPluginList = (list: Array = []): Array => { + const { activeKey } = this.state; return list.map((item, idx) => { + const pluginStatus = this.editor.pluginStatus[item.pluginKey]; return ( { editor={this.editor} pluginClass={this.editor.components[item.pluginKey]} onClick={() => this.handlePluginClick(item)} + active={activeKey === item.pluginKey} + {...pluginStatus} /> ); }); }; render() { + const { activeKey } = this.state; const topList: Array = []; const bottomList: Array = []; const visiblePluginList = this.areaManager.getVisiblePluginList(); @@ -67,7 +150,22 @@ export default class LeftAreaNav extends PureComponent { return (
{this.renderPluginList(bottomList)}
-
{this.renderPluginList(topList)}
+
+ + {this.renderPluginList(topList)} +
); } diff --git a/packages/editor/src/skeleton/layouts/LeftArea/panel.tsx b/packages/editor/src/skeleton/layouts/LeftArea/panel.tsx index 2921ab274..8c03247fd 100644 --- a/packages/editor/src/skeleton/layouts/LeftArea/panel.tsx +++ b/packages/editor/src/skeleton/layouts/LeftArea/panel.tsx @@ -2,7 +2,7 @@ import React, { PureComponent, Fragment } from 'react'; import Panel from '../../components/Panel'; import './index.scss'; import Editor from '../../../framework/editor'; -import { PluginConfig } from '../../../framework/definitions'; +import AreaManager from '../../../framework/areaManager'; export interface LeftAreaPanelProps { editor: Editor; @@ -16,29 +16,51 @@ export default class LeftAreaPanel extends PureComponent; + private areaManager: AreaManager; constructor(props) { super(props); this.editor = props.editor; - this.config = (this.editor.config.plugins && this.editor.config.plugins.leftArea) || []; + this.areaManager = new AreaManager(this.editor, 'leftArea'); this.state = { - activeKey: 'leftPanelIcon' + activeKey: 'none' }; } - render() { - const list = this.config.filter(item => { - return item.type === 'PanelIcon'; + componentDidMount() { + this.editor.on('skeleton.update', this.handleSkeletonUpdate); + this.editor.on('leftPanel.show', this.handlePluginChange); + } + componentWillUnmount() { + this.editor.off('skeleton.update', this.handleSkeletonUpdate); + this.editor.off('leftPanel.show', this.handlePluginChange); + } + + handleSkeletonUpdate = (): void => { + // 当前区域插件状态改变是更新区域 + if (this.areaManager.isPluginStatusUpdate('PanelIcon')) { + this.forceUpdate(); + } + }; + + handlePluginChange = (key: string): void => { + this.setState({ + activeKey: key }); + }; + + render() { + const { activeKey } = this.state; + const list = this.areaManager.getVisiblePluginList('PanelIcon'); + return ( {list.map((item, idx) => { const Comp = this.editor.components[item.pluginKey]; return ( - - + + ); })} diff --git a/packages/editor/src/skeleton/layouts/RightArea/index.tsx b/packages/editor/src/skeleton/layouts/RightArea/index.tsx index 77b0fda0b..5b51d02fb 100644 --- a/packages/editor/src/skeleton/layouts/RightArea/index.tsx +++ b/packages/editor/src/skeleton/layouts/RightArea/index.tsx @@ -2,7 +2,6 @@ import React, { PureComponent } from 'react'; import { Tab, Badge, Icon } from '@alifd/next'; import './index.scss'; import Editor from '../../../framework/editor'; -import { transformToPromise } from '../../../framework/utils'; import AreaManager from '../../../framework/areaManager'; import { PluginConfig } from '../../../framework/definitions'; @@ -33,7 +32,7 @@ export default class RightArea extends PureComponent { + currentPlugin.close().then(() => { this.setState( { activeKey: '' @@ -78,7 +77,7 @@ export default class RightArea extends PureComponent { + plugins[key].open().then(() => { this.editor.set('rightNav', key); this.setState({ activeKey: key @@ -87,7 +86,7 @@ export default class RightArea extends PureComponent { + plugins[activeKey].close().then(() => { openPlugin(); }); } else {