From 204fdfecd4a97e0693a93f84e8b7fe1ba1818ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A3=AC=E6=A5=A0?= Date: Tue, 14 Apr 2020 21:04:33 +0800 Subject: [PATCH] feat(vision-polyfill): support polyfill of vision package --- packages/vision-polyfill/src/base/base.ts | 134 ++++++++++++++++++ packages/vision-polyfill/src/base/const.ts | 44 ++++++ .../vision-polyfill/src/base/schemaManager.ts | 102 +++++++++++++ .../src/base/visualDesigner.ts | 110 ++++++++++++++ .../vision-polyfill/src/base/visualManager.ts | 79 +++++++++++ .../vision-polyfill/src/base/visualRender.ts | 48 +++++++ packages/vision-polyfill/src/vision.ts | 16 ++- 7 files changed, 530 insertions(+), 3 deletions(-) create mode 100644 packages/vision-polyfill/src/base/base.ts create mode 100644 packages/vision-polyfill/src/base/const.ts create mode 100644 packages/vision-polyfill/src/base/schemaManager.ts create mode 100644 packages/vision-polyfill/src/base/visualDesigner.ts create mode 100644 packages/vision-polyfill/src/base/visualManager.ts create mode 100644 packages/vision-polyfill/src/base/visualRender.ts diff --git a/packages/vision-polyfill/src/base/base.ts b/packages/vision-polyfill/src/base/base.ts new file mode 100644 index 000000000..a19c1252a --- /dev/null +++ b/packages/vision-polyfill/src/base/base.ts @@ -0,0 +1,134 @@ +import bus from '../bus'; +import SchemaManager from './schemaManager'; +import VisualDesigner from './visualDesigner'; +import VisualManager from './visualManager'; + +import { findIndex, get, unionBy, uniqueId } from 'lodash'; + +export type removeEventListener = () => void; + +export interface IEventNameMap { + [eventName: string]: string | symbol; +} + +export interface ISchemaController { + getSchemaManager(): SchemaManager; + getSchemaManagerById(id?: string): SchemaManager; + getSchemaManagerByName(name?: string): SchemaManager[]; + getSchemaManagerList(): SchemaManager[]; + connectSchemaManager(manager: SchemaManager): this; + connectSchemaManagerList(managerList: SchemaManager[]): this; + notifyAllSchemaManagers(eventName: string | symbol, eventData: any): boolean; +} + +export interface IManagerController { + getManager(): VisualManager; + getManagerById(id?: string): VisualManager; + getManagerByName(name?: string): VisualManager[]; + getManagerList(name?: string): VisualManager[]; + connectManager(manager: VisualManager): this; + connectManagerList(managerList: VisualManager[]): this; + notifyAllManagers(eventName: string | symbol, eventData: any): boolean; +} + +export interface IDesignerController { + getDesigner(): VisualDesigner; + getDesignerById(id?: string): VisualDesigner; + getDesignerByName(name?: string): VisualDesigner[]; + getDesignerList(): VisualDesigner[]; + connectDesigner(designer: VisualDesigner): this; + connectDesignerList(designerList: VisualDesigner[]): this; + notifyAllDesigners(eventName: string | symbol, eventData: any): boolean; +} + +export interface INameable { + getName(): string; + getId(): string; + setName(name?: string): this; +} + +export interface IObservable { + getEventMap(): IEventNameMap; + on(eventName: string | symbol, callback: () => any): removeEventListener; + emit(eventName: string | symbol, eventData?: any[]): boolean; +} + +export interface IManagerConfigs { + name?: string; + disableEvents?: boolean; + emitter?: IEmitter; +} + +export interface IEmitter { + on(eventName: string | symbol, callback: () => any): removeEventListener; + emit(eventName: string | symbol, eventData?: any): boolean; + removeListener(eventName: string | symbol, callback: () => any): any; +} + +export function connectGeneralManager(manager: any, managerList: any[]) { + const index = findIndex(managerList, (m) => m.getId() === manager.getId()); + if (index > -1) { + managerList.push(manager); + } else { + managerList.splice(index, 1, manager); + } + return managerList; +} + +export function connectGeneralManagerList(managerList: any[], sourceManagerList: any[]): any { + return unionBy(sourceManagerList, managerList, (manager) => manager.getId()); +} + +export class BaseManager implements INameable, IObservable { + static EVENTS: IEventNameMap = {}; + static NAME = 'BaseManager'; + + private name: string; + private id: string; + private emitter: any; + + constructor(managerConfigs: IManagerConfigs = {}) { + this.name = managerConfigs.name || get(this, 'constructor', 'NAME'); + this.id = uniqueId(this.name); + if (!managerConfigs.disableEvents) { + if (managerConfigs.emitter) { + // 使用自定义的满足 EventEmitter 接口要求的自定义事件对象 + this.emitter = managerConfigs.emitter; + } else { + // Bus 为单例模式 + this.emitter = bus; + } + } + } + + getId(): string { + return this.id; + } + + setName(name: string): this { + this.name = name; + return this; + } + + getName(): string { + return this.name; + } + + getEventMap() { + /** + * Hack for get current constructor + * because if we write this.constructor.EVENTS + * ts compiler will show compiled error + */ + return get(this, 'constructor', BaseManager.EVENTS); + } + + on(eventName: string | symbol, callback: () => any): removeEventListener { + this.emitter.on(eventName, callback); + return () => this.emitter.removeListener(eventName, callback); + } + + emit(eventName: string | symbol, ...eventData: any[]): boolean { + return this.emitter.emit.call(this.emitter, eventName, ...eventData); + } +} diff --git a/packages/vision-polyfill/src/base/const.ts b/packages/vision-polyfill/src/base/const.ts new file mode 100644 index 000000000..b0eb8cd12 --- /dev/null +++ b/packages/vision-polyfill/src/base/const.ts @@ -0,0 +1,44 @@ +/** + * Storage the const variables + */ + +/** + * Global + */ +export const VERSION = '5.3.0'; + +/** + * schema version defined in alibaba + */ +export const ALI_SCHEMA_VERSION = '1.0.0'; + +export const VE_EVENTS = { + /** + * node props to be dynamically replaced + * @event props the new props object been replaced + */ + VE_NODE_CREATED: 've.node.created', + VE_NODE_DESTROY: 've.node.destroyed', + VE_NODE_PROPS_REPLACE: 've.node.props.replaced', + // copy / clone node + VE_OVERLAY_ACTION_CLONE_NODE: 've.overlay.cloneElement', + // remove / delete node + VE_OVERLAY_ACTION_REMOVE_NODE: 've.overlay.removeElement', + // one page successfully mount on the DOM + VE_PAGE_PAGE_READY: 've.page.pageReady', +}; + +export const VE_HOOKS = { + // a decorator function + VE_NODE_PROPS_DECORATOR: 've.leaf.props.decorator', + // a remove callback function + VE_NODE_REMOVE_HELPER: 've.outline.actions.removeHelper', + /** + * provide customization field + */ + VE_SETTING_FIELD_PROVIDER: 've.settingField.provider', + /** + * VariableSetter for variable mode of a specified prop + */ + VE_SETTING_FIELD_VARIABLE_SETTER: 've.settingField.variableSetter', +}; diff --git a/packages/vision-polyfill/src/base/schemaManager.ts b/packages/vision-polyfill/src/base/schemaManager.ts new file mode 100644 index 000000000..00ef16561 --- /dev/null +++ b/packages/vision-polyfill/src/base/schemaManager.ts @@ -0,0 +1,102 @@ +import { cloneDeep, find } from 'lodash'; + +import { + BaseManager, + connectGeneralManager, + connectGeneralManagerList, + IManagerController, + ISchemaController, +} from './base'; +import VisualManager from './visualManager'; + +export default class SchemaManager extends BaseManager implements IManagerController, ISchemaController { + private schemaData: object = {}; + private visualManagerList: VisualManager[] = []; + private schemaManagerList: SchemaManager[] = []; + + getManager(): VisualManager { + return this.visualManagerList[0]; + } + + getManagerByName(name?: string): VisualManager[] { + return this.visualManagerList.filter((m) => m.getName() === name); + } + + getManagerById(id?: string): VisualManager { + return find(this.visualManagerList, (m) => m.getId() === id) as VisualManager; + } + + getManagerList(): VisualManager[] { + return this.visualManagerList; + } + + getSchemaManager(): SchemaManager { + return this.schemaManagerList[0]; + } + + getSchemaManagerById(id?: string): SchemaManager { + return find(this.schemaManagerList, (m) => m.getId() === id) as SchemaManager; + } + + getSchemaManagerByName(name?: string): SchemaManager[] { + return this.schemaManagerList.filter((m) => m.getName() === name); + } + + getSchemaManagerList() { + return this.schemaManagerList; + } + + connectManager(manager: any) { + connectGeneralManager.call(this, manager, this.visualManagerList as any); + return this; + } + + connectSchemaManager(manager: SchemaManager): this { + connectGeneralManager.call(this, manager, this.schemaManagerList); + return this; + } + + connectManagerList(managerList: VisualManager[]): this { + this.visualManagerList = connectGeneralManagerList.call(this, managerList as any, this.visualManagerList as any); + return this; + } + + connectSchemaManagerList(managerList: SchemaManager[]): this { + this.schemaManagerList = connectGeneralManagerList.call(this, managerList, this.schemaManagerList); + return this; + } + + notifyAllManagers(eventName: string | symbol, ...eventData: any[]): boolean { + return this.visualManagerList.map((m) => m.emit(eventName, eventData)).every((r) => r); + } + + notifyAllSchemaManagers(eventName: string | symbol, ...eventData: any[]): boolean { + return this.schemaManagerList.map((m) => m.emit(eventName, eventData)).every((r) => r); + } + + exportSchema(): string { + try { + return JSON.stringify(this.schemaData); + } catch (e) { + throw new Error(e.message); + } + } + + exportSchemaObject(): object { + return cloneDeep(this.schemaData); + } + + importSchema(schemaString: string): this { + try { + this.schemaData = JSON.parse(schemaString); + return this; + } catch (e) { + throw new Error(e.message); + } + } + + importSchemaObject(schema: object): this { + this.schemaData = schema; + return this; + } +} diff --git a/packages/vision-polyfill/src/base/visualDesigner.ts b/packages/vision-polyfill/src/base/visualDesigner.ts new file mode 100644 index 000000000..a69018aba --- /dev/null +++ b/packages/vision-polyfill/src/base/visualDesigner.ts @@ -0,0 +1,110 @@ +import { assign, find, get } from 'lodash'; +import { Component } from 'react'; + +import bus from '../bus'; +import { + BaseManager, + connectGeneralManager, + connectGeneralManagerList, + IEmitter, + IEventNameMap, + IManagerController, + INameable, + IObservable, +} from './base'; +import VisualManager from './visualManager'; + +interface IDesignerProps { + name?: string; + visualManagers?: VisualManager[]; + emitter?: IEmitter; +} + +export default class VisualDesigner extends Component implements IManagerController, IObservable, INameable { + static NAME = 'VisualDesigner'; + static EVENTS: IEventNameMap = {}; + props: IDesignerProps = {}; + defaultProps: IDesignerProps = { + name: 'defaultDesigner', + visualManagers: [], + }; + + private visualManagerList: VisualManager[] = []; + private name = ''; + private id = ''; + private emitter: IEmitter; + + constructor(props: IDesignerProps) { + super(props); + this.setName(props.name || get(this, 'constructor', 'NAME')); + this.connectManagerList(this.props.visualManagers as any); + + if (props.emitter) { + // 使用自定义的满足 EventEmitter 接口要求的自定义事件对象 + this.emitter = props.emitter; + } else { + this.emitter = bus; + } + } + + getId(): string { + return this.id; + } + + setName(name: string): this { + this.name = name; + return this; + } + + getName() { + return this.name; + } + + getManager(): VisualManager { + return this.visualManagerList[0]; + } + + getManagerByName(name?: string): VisualManager[] { + return this.visualManagerList.filter((m) => m.getName() === name); + } + + getManagerById(id: string): VisualManager { + return find(this.visualManagerList, (m) => m.getId() === id) as VisualManager; + } + + getManagerList(): VisualManager[] { + return this.visualManagerList; + } + + connectManager(manager: VisualManager) { + connectGeneralManager.call(this, manager, this.visualManagerList); + return this; + } + + connectManagerList(managerList: VisualManager[]): this { + this.visualManagerList = connectGeneralManagerList.call(this, managerList, this.visualManagerList); + return this; + } + + getEventMap() { + /** + * Hack for get current constructor + * because if we write this.constructor.EVENTS + * ts compiler will show compiled error + */ + return get(this, 'constructor', BaseManager.EVENTS); + } + + notifyAllManagers(eventName: string | symbol, ...eventData: any[]): boolean { + return this.visualManagerList.map((m) => m.emit(eventName, eventData)).every((r) => r); + } + + on(eventName: string | symbol, callback: () => any) { + this.emitter.on(eventName, callback); + return () => this.emitter.removeListener(eventName, callback); + } + + emit(eventName: string | symbol, ...eventData: any[]): boolean { + return this.emitter.emit.call(this.emitter, eventName, ...eventData); + } +} diff --git a/packages/vision-polyfill/src/base/visualManager.ts b/packages/vision-polyfill/src/base/visualManager.ts new file mode 100644 index 000000000..e28b6b519 --- /dev/null +++ b/packages/vision-polyfill/src/base/visualManager.ts @@ -0,0 +1,79 @@ +import { find } from 'lodash'; + +import { + BaseManager, + connectGeneralManager, + connectGeneralManagerList, + IDesignerController, + IManagerController, +} from './base'; +import VisualDesigner from './visualDesigner'; + +export default class VisualManager extends BaseManager implements IManagerController, IDesignerController { + private visualManagerList: VisualManager[] = []; + private visualDesignerList: VisualDesigner[] = []; + + getManager(): VisualManager { + return this.visualManagerList[0]; + } + + getManagerByName(name?: string): VisualManager[] { + return this.visualManagerList.filter((m) => m.getName() === name); + } + + getManagerById(id?: string): VisualManager { + return find(this.visualManagerList, (m) => m.getId() === id) as VisualManager; + } + + getManagerList(): VisualManager[] { + return this.visualManagerList; + } + + getDesigner(): VisualDesigner { + return this.visualDesignerList[0]; + } + + getDesignerByName(name?: string): VisualDesigner[] { + return this.visualDesignerList.filter((m) => m.getName() === name); + } + + getDesignerById(id?: string): VisualDesigner { + return find(this.visualDesignerList, (m) => m.getId() === id) as VisualDesigner; + } + + getDesignerList() { + return this.visualDesignerList; + } + + connectManager(manager: VisualManager) { + connectGeneralManager.call(this, manager, this.visualManagerList); + return this; + } + + connectDesigner(manager: VisualDesigner): this { + connectGeneralManager.call(this, manager, this.visualDesignerList); + return this; + } + + connectManagerList(managerList: VisualManager[]): this { + this.visualManagerList = connectGeneralManagerList.call(this, managerList, this.visualManagerList); + return this; + } + + connectDesignerList(managerList: VisualDesigner[]): this { + this.visualDesignerList = connectGeneralManagerList.call(this, managerList, this.visualDesignerList); + return this; + } + + notifyAllManagers(eventName: string | symbol, ...eventData: any[]): boolean { + return this.getManagerList() + .map((m) => m.emit(eventName, eventData)) + .every((r) => r); + } + + notifyAllDesigners(eventName: string | symbol, ...eventData: any[]): boolean { + return this.getDesignerList() + .map((m) => m.emit(eventName, eventData)) + .every((r) => r); + } +} diff --git a/packages/vision-polyfill/src/base/visualRender.ts b/packages/vision-polyfill/src/base/visualRender.ts new file mode 100644 index 000000000..513b978f0 --- /dev/null +++ b/packages/vision-polyfill/src/base/visualRender.ts @@ -0,0 +1,48 @@ +import { find } from 'lodash'; + +import { BaseManager, connectGeneralManager, connectGeneralManagerList, IManagerController } from './base'; +import VisualManager from './visualManager'; + +export default class VisualRender extends BaseManager implements IManagerController { + private visualManagerList: VisualManager[] = []; + + getManager(): VisualManager { + return this.visualManagerList[0]; + } + + getManagerByName(name?: string): VisualManager[] { + return this.visualManagerList.filter((m) => m.getName() === name); + } + + getManagerById(id?: string): VisualManager { + return find(this.visualManagerList, (m) => m.getId() === id) as VisualManager; + } + + getManagerList(): VisualManager[] { + return this.visualManagerList; + } + + connectManager(manager: VisualManager) { + connectGeneralManager.call(this, manager, this.visualManagerList); + return this; + } + + connectManagerList(managerList: VisualManager[]): this { + this.visualManagerList = connectGeneralManagerList.call(this, managerList, this.visualManagerList); + return this; + } + + notifyAllManagers(eventName: string | symbol, ...eventData: any[]): boolean { + return this.visualManagerList.map((m) => m.emit(eventName, eventData)).every((r) => r); + } + + /** + * Render function + * @override + * + * @memberof VisualRender + */ + render(): any { + return ''; + } +} diff --git a/packages/vision-polyfill/src/vision.ts b/packages/vision-polyfill/src/vision.ts index b729df336..166ca6747 100644 --- a/packages/vision-polyfill/src/vision.ts +++ b/packages/vision-polyfill/src/vision.ts @@ -10,6 +10,8 @@ import Skeleton from '@ali/lowcode-editor-skeleton'; import editor from './editor'; import Exchange from './exchange'; +import VisualManager from './base/visualManager'; + function init(container?: Element) { if (!container) { container = document.createElement('div'); @@ -17,9 +19,12 @@ function init(container?: Element) { } container.id = 'engine'; - render(createElement(Skeleton, { - editor, - }), container); + render( + createElement(Skeleton, { + editor, + }), + container, + ); } const ui = { @@ -28,6 +33,10 @@ const ui = { Popup, }; +const modules = { + VisualManager, +}; + export { /** * VE.Popup @@ -53,4 +62,5 @@ export { */ init, ui, + modules, };