feat:daily tag

This commit is contained in:
下羊 2020-03-10 14:29:29 +08:00
parent 8eb0851e4c
commit 3812a6c002
17 changed files with 353 additions and 95 deletions

View File

@ -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),

View File

@ -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',

View File

@ -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<PluginConfig> {
return this.config.filter(item => {
getVisiblePluginList(pluginType?: string): Array<PluginConfig> {
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<PluginConfig> {

View File

@ -114,6 +114,8 @@ export interface Utils {
export interface PluginClass extends React.Component {
init?: (editor: Editor) => void;
open?: () => any;
close?: () => any;
}
export interface PluginComponents {

View File

@ -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<any> => {
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 <Comp ref={this.ref} i18n={this.i18n} editor={this.editor} config={config} {...config.pluginProps} />;
const props = {
i18n: this.i18n,
editor: this.editor,
config,
...config.pluginProps
};
if (acceptsRef(Comp)) {
props.ref = this.ref;
}
return <Comp {...props} />;
}
}

View File

@ -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)
);
}

View File

@ -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<PluginProps> {
static displayName: 'LowcodePluginDesigner';
constructor(props) {}
render() {}
}

View File

@ -0,0 +1,9 @@
.lowcode-plugin-logo {
padding: 8px 16px;
.logo {
width: 56px;
height: 32px;
background-size: contain;
background-position: center;
}
}

View File

@ -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 (
<div className="lowcode-plugin-logo">
<div className="logo" style={{ backgroundImage: `url(${props.logo})` }} />
</div>
);
}

View File

@ -41,36 +41,34 @@ export default class LeftPlugin extends PureComponent<LeftPluginProps, LeftPlugi
}
componentDidMount() {
// const { config } = this.props;
// const addonKey = config && config.addonKey;
// const appHelper = this.appHelper;
// if (appHelper && addonKey) {
// appHelper.on(`${addonKey}.dialog.show`, this.handleShow);
// appHelper.on(`${addonKey}.dialog.close`, this.handleClose);
// }
const { config, editor } = this.props;
const pluginKey = config && config.pluginKey;
if (editor && pluginKey) {
editor.on(`${pluginKey}.dialog.show`, this.handleShow);
editor.on(`${pluginKey}.dialog.close`, this.handleClose);
}
}
componentWillUnmount() {
// const { config } = this.props;
// const appHelper = this.appHelper;
// const addonKey = config && config.addonKey;
// if (appHelper && addonKey) {
// appHelper.off(`${addonKey}.dialog.show`, this.handleShow);
// appHelper.off(`${addonKey}.dialog.close`, this.handleClose);
// }
const { config, editor } = this.props;
const pluginKey = config && config.pluginKey;
if (editor && pluginKey) {
editor.off(`${pluginKey}.dialog.show`, this.handleShow);
editor.off(`${pluginKey}.dialog.close`, this.handleClose);
}
}
handleClose = () => {
// const addonKey = this.props.config && this.props.config.addonKey;
// const currentAddon =
// this.appHelper.addons && this.appHelper.addons[addonKey];
// if (currentAddon) {
// this.utils.transformToPromise(currentAddon.close()).then(() => {
// 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<LeftPluginProps, LeftPlugi
};
handleShow = () => {
// 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 (

View File

@ -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<PanelProps> {
export interface PanelState {
width: number;
}
export default class Panel extends PureComponent<PanelProps, PanelState> {
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 (
<div
className="lowcode-panel"
className={classNames('lowcode-panel', align, {
draggable,
floatable,
visible
})}
style={{
width: 240
width,
display: visible ? '' : 'none'
}}
>
{this.props.children}
<div className="drag-area" />
</div>
);
}

View File

@ -42,46 +42,44 @@ export default class TopPlugin extends PureComponent<TopPluginProps, TopPluginSt
}
componentDidMount() {
const { config } = this.props;
const { config, editor } = this.props;
const pluginKey = config && config.pluginKey;
// const appHelper = this.appHelper;
// if (appHelper && pluginKey) {
// appHelper.on(`${pluginKey}.dialog.show`, this.handleShow);
// appHelper.on(`${pluginKey}.dialog.close`, this.handleClose);
// }
if (editor && pluginKey) {
editor.on(`${pluginKey}.dialog.show`, this.handleShow);
editor.on(`${pluginKey}.dialog.close`, this.handleClose);
}
}
componentWillUnmount() {
// const { config } = this.props;
// const pluginKey = config && config.pluginKey;
// const appHelper = this.appHelper;
// if (appHelper && pluginKey) {
// appHelper.off(`${pluginKey}.dialog.show`, this.handleShow);
// appHelper.off(`${pluginKey}.dialog.close`, this.handleClose);
// }
const { config, editor } = this.props;
const pluginKey = config && config.pluginKey;
if (editor && pluginKey) {
editor.off(`${pluginKey}.dialog.show`, this.handleShow);
editor.off(`${pluginKey}.dialog.close`, this.handleClose);
}
}
handleShow = () => {
// const { disabled, config, onClick, 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 = () => {

View File

@ -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';

View File

@ -9,23 +9,38 @@ export interface LeftAreaNavProps {
editor: Editor;
}
export default class LeftAreaNav extends PureComponent<LeftAreaNavProps> {
export interface LeftAreaNavState {
activeKey: string;
}
export default class LeftAreaNav extends PureComponent<LeftAreaNavProps, LeftAreaNavState> {
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<LeftAreaNavProps> {
}
};
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<PluginConfig> = []): Array<React.ReactElement> => {
const { activeKey } = this.state;
return list.map((item, idx) => {
const pluginStatus = this.editor.pluginStatus[item.pluginKey];
return (
<LeftPlugin
key={item.pluginKey}
@ -46,12 +126,15 @@ export default class LeftAreaNav extends PureComponent<LeftAreaNavProps> {
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<PluginConfig> = [];
const bottomList: Array<PluginConfig> = [];
const visiblePluginList = this.areaManager.getVisiblePluginList();
@ -67,7 +150,22 @@ export default class LeftAreaNav extends PureComponent<LeftAreaNavProps> {
return (
<div className="lowcode-left-area-nav">
<div className="bottom-area">{this.renderPluginList(bottomList)}</div>
<div className="top-area">{this.renderPluginList(topList)}</div>
<div className="top-area">
<LeftPlugin
editor={this.editor}
key="collapse"
config={{
pluginKey: 'collapse',
type: 'Icon',
props: {
icon: activeKey === 'none' ? 'zhankaizhuangtai' : 'shouqizhuangtai',
title: activeKey === 'none' ? '展开' : '收起'
}
}}
onClick={this.handleCollapseClick}
/>
{this.renderPluginList(topList)}
</div>
</div>
);
}

View File

@ -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<LeftAreaPanelProps, Lef
static displayName = 'LowcodeLeftAreaPanel';
private editor: Editor;
private config: Array<PluginConfig>;
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 (
<Fragment>
{list.map((item, idx) => {
const Comp = this.editor.components[item.pluginKey];
return (
<Panel key={item.pluginKey} visible={item.pluginKey === this.state.activeKey}>
<Comp editor={this.editor} config={item} />
<Panel key={item.pluginKey} visible={item.pluginKey === activeKey}>
<Comp editor={this.editor} config={item} {...item.pluginProps} />
</Panel>
);
})}

View File

@ -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<RightAreaProps, RightAreaSt
this.editor.on('skeleton.update', this.handleSkeletonUpdate);
this.editor.on('rightNav.change', this.handlePluginChange);
const visiblePluginList = this.areaManager.getVisiblePluginList();
const defaultKey = (visiblePluginList[0] && visiblePluginList[0].pluginKey) || '';
const defaultKey = (visiblePluginList[0] && visiblePluginList[0].pluginKey) || 'componentAttr';
this.handlePluginChange(defaultKey, true);
}
componentWillUnmount() {
@ -51,7 +50,7 @@ export default class RightArea extends PureComponent<RightAreaProps, RightAreaSt
} else {
const currentPlugin = this.editor.plugins[activeKey];
if (currentPlugin) {
transformToPromise(currentPlugin.close()).then(() => {
currentPlugin.close().then(() => {
this.setState(
{
activeKey: ''
@ -78,7 +77,7 @@ export default class RightArea extends PureComponent<RightAreaProps, RightAreaSt
console.error(`plugin ${key} has not regist in the editor`);
return;
}
transformToPromise(plugins[key].open()).then(() => {
plugins[key].open().then(() => {
this.editor.set('rightNav', key);
this.setState({
activeKey: key
@ -87,7 +86,7 @@ export default class RightArea extends PureComponent<RightAreaProps, RightAreaSt
};
if (key === activeKey && !isinit) return;
if (activeKey && plugins[activeKey]) {
transformToPromise(plugins[activeKey].close()).then(() => {
plugins[activeKey].close().then(() => {
openPlugin();
});
} else {