diff --git a/packages/vision-polyfill/README.md b/packages/vision-polyfill/README.md new file mode 100644 index 000000000..e178d197e --- /dev/null +++ b/packages/vision-polyfill/README.md @@ -0,0 +1,68 @@ +子视图 +prototype.view +view.Preview +view.Mobile + +实时切 + +设备 +device +创建多个 simulator + +不同simulator 加载不同视图 + +这样有利于 环境隔离,比如 rax 和 react + +适配规则 + +规则 1 +mobile view.mobile.xxx +rax view.rax.xxx +miniapp view.miniapp.xxx +view..xxx +通配 view.xxx + +规则 2 +urls: "view.js,view2 , view3 ", +urls: [ + "view.js", + "view.js *", + "view1.js mobile|pc", + "view2.js " +] + +环境通用资源 + +"react": { + "urls": [ + "//g.alicdn.com/platform/c/react/16.5.2/react.min.js" + ], + "library": "React", + "package": "react", + "version": "16.5.2", + "devices-for": "*" | ["mobile", "web"] | "rax|mobile" +} + + +load legao assets + load all x-prototype-urls + + +load assets + + build componentMeta + if has x-prototype-urls , + load x-prototype-urls + call Bundle.createPrototype() or something register + got prototypeView + +load schema + + +open schema + +load simulator resources + + + +simulator 中加载资源,根据 componentsMap 构建组件查询字典, diff --git a/packages/vision-polyfill/package.json b/packages/vision-polyfill/package.json new file mode 100644 index 000000000..1e8b2923a --- /dev/null +++ b/packages/vision-polyfill/package.json @@ -0,0 +1,19 @@ +{ + "name": "@ali/lowcode-vision-polyfill", + "version": "0.8.0", + "description": "Vision Polyfill for Ali lowCode engine", + "main": "lib/index.js", + "files": [ + "dist" + ], + "scripts": {}, + "license": "MIT", + "dependencies": { + "@ali/ve-icons": "^4.1.9", + "@ali/ve-popups": "^4.2.5", + "@ali/ve-utils": "^1.1.0", + "@ali/vu-css-style": "^1.1.3", + "@ali/vu-logger": "^1.0.7", + "@ali/vu-style-sheet": "^2.4.0" + } +} diff --git a/packages/vision-polyfill/src/bundle/bundle.ts b/packages/vision-polyfill/src/bundle/bundle.ts new file mode 100644 index 000000000..d2c9ae8f1 --- /dev/null +++ b/packages/vision-polyfill/src/bundle/bundle.ts @@ -0,0 +1,263 @@ +import lg from '@ali/vu-logger'; +import { camelCase, find, findIndex, upperFirst } from 'lodash'; +import { ComponentClass, ReactElement, ComponentType } from 'react'; + +import { UnknownComponent } from '../../ui/placeholders'; +import Trunk, { IComponentBundle } from '../trunk'; +import Prototype from './prototype'; + +function basename(name: string) { + return name ? (/[^\/]+$/.exec(name) || [''])[0] : ''; +} + +function getCamelName(name: string) { + const words = basename(name).replace(/^((vc)-)?(.+)/, '$3').split('-'); + return words.reduce((s, word) => s + word[0].toUpperCase() + word.substring(1), ''); +} + +export declare interface IComponentProto { + name: string; + module: Prototype; + componentName?: string; + category?: string; +} + +export default class Bundle { + public static createPrototype = Prototype.create; + public static addGlobalPropsReducer = Prototype.addGlobalPropsReducer; + public static addGlobalPropsConfigure = Prototype.addGlobalPropsConfigure; + public static addGlobalExtraActions = Prototype.addGlobalExtraActions; + public static addGlobalNodeCanDragConfig = Prototype.addGlobalNodeCanDragConfig; + public static removeGlobalPropsConfigure = Prototype.removeGlobalPropsConfigure; + public static overridePropsConfigure = Prototype.overridePropsConfigure; + public static create = function createBundle( + components: Array, + views: IComponentBundle[], nameSpace: string) { + return new Bundle(components, views, nameSpace); + }; + + /** + * if all components are packed in a single package + * the compositeBundle property shall be true + */ + public compositeBundle: boolean = false; + + private trunk: Trunk; + private nameSpace: string; + private registry: { [name: string]: Prototype }; + private registryById: { [id: string]: Prototype }; + private prototypeList: Prototype[]; + + private viewMap: { [viewName: string]: ComponentClass } = {}; + private viewList: ComponentClass[] = []; + + constructor( + componentPrototypes: Array, + views: IComponentBundle[], + nameSpace: string, + ) { + this.nameSpace = nameSpace || ''; + this.registry = {}; + this.registryById = {}; + this.prototypeList = []; + this.trunk = new Trunk(); + + if (Array.isArray(views)) { + this.recursivelyRegisterViews(views); + } + componentPrototypes.forEach((item: IComponentProto) => { + const prototype = item.module; + if (prototype instanceof Prototype) { + this.revisePrototype(item, prototype); + const matchedView = this.viewMap[item.componentName || prototype.getComponentName()] || null; + if (!prototype.getView() && matchedView) { + prototype.setView(matchedView); + } + this.registerPrototype(prototype); + } else if (Array.isArray(prototype)) { + this.recursivelyRegisterPrototypes(prototype, item); + } + }); + this.prototypeList.forEach((p, idx) => { + if (!p.getView()) { + p.setView(UnknownComponent); + } + }); + // invoke prototype mocker while the prototype does not exist + Object.keys(this.viewMap).forEach((viewName) => { + if (!find(this.prototypeList, (proto) => proto.getComponentName() === viewName)) { + const mockedPrototype = this.trunk.mockComponentPrototype(this.viewMap[viewName]); + if (mockedPrototype) { + if (!mockedPrototype.getPackageName()) { + mockedPrototype.setPackageName((this.viewMap[viewName] as any)._packageName_); + } + this.registry[viewName] = mockedPrototype; + this.registryById[mockedPrototype.getId()] = mockedPrototype; + this.prototypeList.push(mockedPrototype); + } + } + }); + } + + public addComponentBundle(bundles: Array): void; + public addComponentBundle(bundles: any) { + /** + * Normal Component bundle: [ Prototype, PrototypeView ] + * Component without Prototype.js: [ View ] + */ + if (bundles.length >= 2) { + const prototype = bundles[0]; + const prototypeView = bundles[1]; + prototype.setView(prototypeView); + this.registerPrototype(prototype); + } else if (bundles.length === 1) { + // Mock a Prototype for DIY Component load from async build + const proto = this.trunk.mockComponentPrototype(bundles[0]); + if (!proto) { + return; + } + if (!proto.getView()) { + proto.setView(bundles[0]); + } + this.registerPrototype(proto); + } + } + + public removeComponentBundle(componentName: string) { + const cIndex = findIndex(this.prototypeList, (proto) => proto.getComponentName() === componentName); + const id = this.prototypeList[cIndex].getId(); + delete this.registryById[id]; + delete this.registry[componentName]; + this.prototypeList.splice(cIndex, 1); + } + + public getNamespace() { + return this.nameSpace; + } + + public getList() { + return this.prototypeList; + } + + public get(componentName: string) { + return this.registry[componentName]; + } + + public getById(id: string) { + return this.registryById[id]; + } + + public isCompositeBundle() { + return this.isCompositeBundle; + } + + public filter(fn: (item: Prototype) => boolean) { + this.prototypeList = this.prototypeList.filter((item) => { + if (fn(item) === false) { + if (this.registry[item.getComponentName()] === item) { + delete this.registry[item.getComponentName()]; + } + if (this.registryById[item.getId()] === item) { + delete this.registryById[item.getId()]; + } + return false; + } + return true; + }); + } + + public replacePrototype(componentName: string, cp: Prototype) { + const view: any = this.get(componentName).getView(); + this.removeComponentBundle(componentName); + this.registry[cp.getComponentName()] = cp; + this.registryById[cp.getId()] = cp; + this.prototypeList.push(cp); + cp.setView(view); + } + + private recursivelyRegisterPrototypes(list: any[], cp: IComponentProto) { + const propList: IComponentProto[] = list; + propList.forEach((proto: IComponentProto, index: number) => { + if (Array.isArray(proto)) { + this.recursivelyRegisterPrototypes(proto, cp); + return; + } + if (proto instanceof Prototype) { + if (!proto.getView() && this.viewMap[proto.getComponentName()]) { + proto.setView(this.viewMap[proto.getComponentName()]); + } + if (index === 0 && cp.componentName) { + proto.setComponentName(cp.componentName); + } + if (cp.name && !proto.getPackageName()) { + proto.setPackageName(cp.name); + } + this.registerPrototype(proto); + } + }); + } + + /** + * register View + * @param list ViewList + * @param viewName + */ + private recursivelyRegisterViews(list: IComponentBundle[], viewName?: string): void; + private recursivelyRegisterViews(list: any[], viewName?: string): void { + list.forEach((item: any) => { + if (Array.isArray(item.module)) { + return this.recursivelyRegisterViews(item.module, item.name); + } else if (Array.isArray(item)) { + this.compositeBundle = true; + return this.recursivelyRegisterViews(item, viewName); + } + let viewDetail: ComponentClass; + if (item.module && typeof item.module === 'function') { + viewDetail = item.module; + } else { + viewDetail = item; + } + if (!viewDetail.displayName) { + lg.log('ERROR_NO_PROTOTYPE_VIEW'); + lg.error('WARNING: the package without displayName is', item); + viewDetail.displayName = upperFirst( + // mock componentName from packageName + camelCase((viewName || item.name).split('-').slice(1).join('-')), + ); + } + (viewDetail as any)._packageName_ = viewName || item.name; + this.viewMap[viewDetail.displayName] = viewDetail; + this.viewList.push(viewDetail); + }); + } + + private revisePrototype(item: IComponentProto, prototype: Prototype) { + const name = item.name || item.componentName; + if (item.category) { + prototype.setCategory(item.category); + } + if (item.name && !prototype.getPackageName()) { + prototype.setPackageName(item.name); + } + if (item.componentName) { + prototype.setComponentName(item.componentName); + } + if (!prototype.getComponentName()) { + prototype.setComponentName(getCamelName(name)); + } + } + + private registerPrototype(prototype: Prototype) { + if (this.registry[prototype.getComponentName()]) { + lg.warn('WARN: override prototype', prototype, prototype.getComponentName()); + const idx = findIndex(this.prototypeList, (proto) => + prototype.getComponentName() === proto.getComponentName()); + this.prototypeList[idx] = prototype; + delete this.registryById[prototype.getId()]; + } else { + this.prototypeList.push(prototype); + } + this.registry[prototype.getComponentName()] = prototype; + this.registryById[prototype.getId()] = prototype; + } +} diff --git a/packages/vision-polyfill/src/bundle/prototype.ts b/packages/vision-polyfill/src/bundle/prototype.ts new file mode 100644 index 000000000..768aee46f --- /dev/null +++ b/packages/vision-polyfill/src/bundle/prototype.ts @@ -0,0 +1,1027 @@ +import { assign, flow, get, isFunction, isObject } from 'lodash'; +import { Component, ComponentClass } from 'react'; +import { Node } from '../../core/pages/node'; +import Prop from '../../core/pages/prop'; + +export enum DISPLAY_TYPE { + NONE = 'none', + PLAIN = 'plain', + INLINE = 'inline', + BLOCK = 'block', + ACCORDION = 'accordion', + TAB = 'tab', + ENTRY = 'entry', +} + +let PID = 0; +const GlobalNodeCanDragConfig: Array<(node: Node | false) => {}> = []; +const GlobalPropsReducers: any[] = []; +const GlobalPropsConfigure: IGlobalPropConfig[] = [ + { + position: 'bottom', + title: '条件渲染', + type: 'group', + name: '', + display: DISPLAY_TYPE.ACCORDION, + collapsed: true, + tip: { + content: '点击查看详细用法', + url: 'https://yuque.antfin-inc.com/legao/help3.0/conditional-render', + }, + items: [ + { + name: '___condition___', + title: '是否渲染', + display: DISPLAY_TYPE.INLINE, + initialValue: true, + supportVariable: true, + setter: 'Switch', + }, + ], + disabled() { + const proto = this.getProps() + .getNode() + .getPrototype(); + if (!proto) { + return true; + } + if (!proto.canUseCondition()) { + return true; + } + return false; + }, + }, + { + position: 'bottom', + title: '循环渲染', + type: 'group', + name: '', + display: DISPLAY_TYPE.ACCORDION, + collapsed: true, + tip: { + content: '点击查看详细用法', + url: 'https://yuque.antfin-inc.com/legao/help3.0/loop-render', + }, + items: [ + { + name: '___loop___', + title: '循环数据', + display: DISPLAY_TYPE.INLINE, + setter: 'Object', + supportVariable: true, + }, + { + name: '___loopArgs___', + title: '循环参数', + display: DISPLAY_TYPE.INLINE, + initialValue: ['item', 'index'], + setter: 'Object', + }, + { + name: 'key', + title: '循环key111', + display: DISPLAY_TYPE.INLINE, + initialValue: '', + setter: 'Input', + ignore() { + if (this.getValue() || this.getVariableValue()) { + return false; + } + return true; + }, + supportVariable: true, + }, + ], + disabled() { + const proto = this.getProps().getNode().getPrototype(); + if (!proto) { + return true; + } + if (!proto.canLoop()) { + return true; + } + return false; + }, + }, +]; +const GlobalExtraActions: any[] = []; +const Overrides: any = {}; + +function addGlobalPropsReducer(reducer: () => any) { + GlobalPropsReducers.push(reducer); +} + +function addGlobalPropsConfigure(config: IGlobalPropConfig) { + GlobalPropsConfigure.push(config); +} + +function addGlobalExtraActions(actions: () => React.Component[]) { + GlobalExtraActions.push(actions); +} + +function addGlobalNodeCanDragConfig(drag: () => boolean) { + if (!isFunction(drag)) { + /* tslint:disable no-console */ + console.error('ERROR: the global canDrag configure should be a function.'); + return; + } + GlobalNodeCanDragConfig.push(drag); +} + +function removeGlobalPropsConfigure(name: string) { + let l = GlobalPropsConfigure.length; + while (l-- > 0) { + if (GlobalPropsConfigure[l].name === name) { + GlobalPropsConfigure.splice(l, 1); + } + } +} + +function overridePropsConfigure(componentName: string, configure: any) { + Overrides[componentName] = configure; +} + +export interface ISetterConfig { + title?: string; + setter?: ComponentClass; + componentName?: string; + props?: object; + initialValue?: any; + // use value to decide whether this setter is available + condition?: (value: any) => boolean; +} + +export type SetterGetter = (this: Prop, value: any) => ComponentClass; + +type ReturnBooleanFunction = () => boolean; + +export interface IPropConfig { + /** + * composite share the namespace + * group just be tie up together + */ + type?: 'composite' | 'group'; + /** + * when type is composite or group + */ + items?: IPropConfig[]; + /** + * property name: the field key in props of schema + */ + name: string; + title?: string; + tip?: { + title?: string; + content?: string; + url?: string; + }; + initialValue?: any; + defaultValue?: any; + display?: DISPLAY_TYPE; + fieldStyle?: DISPLAY_TYPE; + setter?: ComponentClass | ISetterConfig[] | string | SetterGetter; + /** + * if a prop is dynamicProp, every-time while rendering setting field + * - getValue() will not include value of getHotValue() + * - getValue() will trigger accessor() to calculate a new value + * this prop usually work out when we need to generate prop value + * from node of current page + */ + isDynamicProp?: boolean; + supportVariable?: boolean; + /** + * the prop should be collapsed while display value is accordion + */ + collapse?: boolean; + /** + * alias to collapse + */ + collapsed?: boolean; + fieldCollapsed?: boolean; + /** + * if a prop is declared as disabled, it will not be saved into + * schema + */ + disabled?: boolean | ReturnBooleanFunction; + /** + * will not export data to schema + */ + ignore?: boolean | ReturnBooleanFunction; + /** + * if a prop is declared as virtual, it will not be saved in + * schema props, instead it will be saved into context field + */ + virtual?: boolean | ReturnBooleanFunction; + hidden?: boolean | ReturnBooleanFunction; + /** + * if a prop is a lifeCycle function + */ + lifeCycle?: boolean; + destroy?: () => any; + initial?(this: Prop, value: any, initialValue: any): any; + /** + * when use getValue(), accessor shall be called as initializer + */ + accessor?(this: Prop): any; + /** + * when current prop value mutate, the mutator function shall be called + */ + mutator?( + this: Prop, + value: any, + hotValue: any, + preValue: any, + preHotValue: any, + ): void; + /** + * other values' change will trigger sync function here + */ + sync?(this: Prop, value: any): void; + /** + * transform runtime data between view and setter + * @param toHotValue hot value for the setter + * @param toViewValue static value for the view + */ + transformer?( + toHotValue: (data: any) => any, + toViewValue: (str: string) => any, + ): any; + /** + * user click var to change current field to + * variable setting field + */ + useVariableChange?(data: { isUseVariable: boolean }): any; +} + +export interface IGlobalPropConfig extends IPropConfig { + position?: + | 'top' + | 'bottom' + | { [key in 'before' | 'after']: { prop: string; value: any } }; +} + +export interface SettingFieldConfig { + type?: 'field'; + title?: string; + name: string; + setter: ComponentClass | ISetterConfig[] | string | SetterGetter; + extraProps?: { + [key: string]: any; + }; +} + +export interface SettingGroupConfig { + type: 'group'; + title?: string; + items: Array; + extraProps?: { + [key: string]: any; + }; +} + +export interface ComponentDecoratorSpec { + componentName: string; + title?: string; + description?: string; + docUrl?: string; + screenshot?: string; + icon?: string; + category?: string; + tags?: string | string[]; + mode?: 'lowcode' | 'procode'; + uri?: string; + npm?: { + package: string; + exportName?: string; + subName?: string; + destructuring?: boolean; + main?: string; + version: string; + }; + props?: Array<{ + name: string; + description?: string; + propType: string | { type: string; value: any }; + defaultValue?: any; + }>; + configure?: { + props?: Array; + component?: { + isContainer?: boolean; + isModal?: boolean; + descriptor?: string; + nestingRule?: { + childWhitelist?: string[]; + parentWhitelist?: string[]; + }; + }; + callbacks?: object; + }; +} + +export interface ISnippet { + screenshot: string; + label: string; + schema: any; +} + +function toPropConfig( + configure: Array, +): IPropConfig[] { + if (!configure) { + return []; + } + return configure.map((item) => { + if (item.type === 'group') { + return { + type: 'group', + name: '', + title: item.title, + items: toPropConfig(item.items), + ...item.extraProps, + }; + } else { + return { + name: item.name, + title: item.title, + // defaultValue: any, + setter: item.setter, + supportVariable: true, + ...item.extraProps, + }; + } + }); +} + +const packageMaps: any = {}; + +function accessLibrary(library: string | object) { + if (typeof library !== 'string') { + return library; + } + + // TODO: enhance logic + return (window as any)[library]; +} + +export function setPackages( + packages: Array<{ package: string; library: object | string }>, +) { + packages.forEach((item) => { + let lib: any; + if (packageMaps.hasOwnProperty(item.package)) { + return; + } + Object.defineProperty(packageMaps, item.package, { + get() { + if (lib === undefined) { + lib = accessLibrary(item.library); + } + return lib; + }, + }); + }); +} + +export function getPackage(name: string): object | null { + if (packageMaps.hasOwnProperty(name)) { + return packageMaps[name]; + } + + return null; +} + +function getSubComponent(component: any, paths: string[] | string) { + if (!Array.isArray(paths)) { + paths = paths.split('.'); + } + const l = paths.length; + if (l < 1) { + return component; + } + if (l === 1 && paths[0] === 'default') { + try { + if ((component as any).default) { + return (component as any).default; + } + return component; + } catch (e) { + return null; + } + } + let i = 0; + while (i < l) { + const key = paths[i]!; + try { + component = (component as any)[key]; + } catch (e) { + return null; + } + if (!component) { + return null; + } + i++; + } + return component; +} + +function getViewByURI(uri: string): any { + const [packName, paths] = uri.split(':'); + const component = getPackage(packName); + if (!paths) { + return component; + } + return getSubComponent(component, paths); +} + +function generateConfigure( + props: Array<{ + name: string; + propType: string | { type: string; value: any }; + defaultValue?: any; + description?: string; + }>, +): any { + return []; +} + +function npmToURI(npm: { + package: string; + exportName?: string; + subName?: string; + destructuring?: boolean; + main?: string; + version: string; +}): string { + const pkg = []; + if (npm.package) { + pkg.push(npm.package); + } + if (npm.main) { + if (npm.main[0] === '/') { + pkg.push(npm.main.slice(1)); + } else if (npm.main.slice(0, 2) === './') { + pkg.push(npm.main.slice(2)); + } else { + pkg.push(npm.main); + } + } + + let uri = pkg.join('/'); + uri += `:${npm.destructuring && npm.exportName ? npm.exportName : 'default'}`; + + if (npm.subName) { + uri += `.${npm.subName}`; + } + + return uri; +} + +function toOlderSpec( + options: ComponentDecoratorSpec, +): IComponentPrototypeConfigure { + const uri = options.uri || npmToURI(options.npm); + const spec: any = { + packageName: options.npm ? options.npm.package : '', + category: + options.category || + (Array.isArray(options.tags) ? options.tags[0] : options.tags), + componentName: options.componentName, + docUrl: options.docUrl, + defaultProps: {}, + title: options.title, + icon: options.icon, + uri, + view: getViewByURI(uri), + configure: + options.configure && options.configure.props + ? toPropConfig(options.configure.props) + : generateConfigure(options.props), + }; + + if (options.configure && options.configure.component) { + if (options.configure.component.isModal) { + spec.isModal = true; + } + if (options.configure.component.isContainer) { + spec.isContainer = true; + } + if (options.configure.component.descriptor) { + spec.descriptor = options.configure.component.descriptor; + } + if (options.configure.component.nestingRule) { + if (options.configure.component.nestingRule.childWhitelist) { + spec.canDropIn = options.configure.component.nestingRule.childWhitelist; + } + if (options.configure.component.nestingRule.parentWhitelist) { + spec.canDropTo = + options.configure.component.nestingRule.parentWhitelist; + } + } + } + + return spec; +} + +function isNewSpec(options: any): options is ComponentDecoratorSpec { + return ( + options && + (options.npm || + options.props || + (options.configure && + (options.configure.props || options.configure.component))) + ); +} + +export declare interface IComponentPrototypeConfigure { + packageName: string; + uri?: string; + /** + * category display in the component pane + * component will be hidden while the value is: null + */ + category: string; + componentName: string; + docUrl?: string; + defaultProps?: any; + /** + * extra actions on the outline of current selected node + * by default we have: remove / clone + */ + extraActions?: Component[]; + title?: string; + icon?: Component; + view: Component; + initialChildren?: (props: any) => any[]; + + /** + * Props configurations of node + */ + configure: IPropConfig[]; + snippets?: ISnippet[]; + transducers?: any; + reducers?: any; + /** + * Selector expression rectangle of a node, it is usually a querySelector string + * @example '.classname > div' + */ + rectSelector?: string; + context?: { + [contextInfoName: string]: any; + }; + + isContainer?: boolean; + isInline?: boolean; + isModal?: boolean; + isFloating?: boolean; + descriptor?: string; + + /** + * enable slot-mode + * @see https://yuque.antfin-inc.com/legao/solutions/atgtdl + */ + hasSlot?: boolean; + + // alias to canDragging + canDraging?: boolean; + canDragging?: boolean; + + canOperating?: boolean; + canHovering?: boolean; + canSelecting?: boolean; + canUseCondition?: boolean; + canLoop?: boolean; + canContain?: (dragment: Node) => boolean; + + canDropTo?: ((container: Node) => boolean) | string | string[]; + canDropto?: (container: Node) => boolean; + + canDropIn?: ((dragment: Node) => boolean) | string | string[]; + canDroping?: (dragment: Node) => boolean; + + didDropOut?: (container: any | Prototype, dragment: any) => boolean; + didDropIn?: (container: any | Prototype, dragment: any) => boolean; + + canResizing?: + | ((dragment: Node, triggerDirection: string) => boolean) + | boolean; + onResizeStart?: ( + e: MouseEvent, + triggerDirection: string, + dragment: Node, + ) => void; + onResize?: ( + e: MouseEvent, + triggerDirection: string, + dragment: Node, + moveX: number, + moveY: number, + ) => void; + onResizeEnd?: ( + e: MouseEvent, + triggerDirection: string, + dragment: Node, + ) => void; + + /** + * when sub-node of the current node changed + * including: sub-node insert / remove + */ + subtreeModified?(this: Node): any; +} + +export interface IComponentPrototypeExtraConfigs { + autoGenerated?: boolean; +} + +class Prototype { + public static addGlobalNodeCanDragConfig = addGlobalNodeCanDragConfig; + public static addGlobalPropsReducer = addGlobalPropsReducer; + public static addGlobalPropsConfigure = addGlobalPropsConfigure; + public static addGlobalExtraActions = addGlobalExtraActions; + public static removeGlobalPropsConfigure = removeGlobalPropsConfigure; + public static overridePropsConfigure = overridePropsConfigure; + public static create = function create( + options: IComponentPrototypeConfigure | ComponentDecoratorSpec, + extraConfigs: IComponentPrototypeExtraConfigs = {}, + ) { + return new Prototype(options, extraConfigs); + }; + + private options: IComponentPrototypeConfigure; + private id: string; + private packageName: string; + private componentName: string; + private category: string; + private view: ComponentClass; + + /** + * is this prototype auto-generated by prototypeMocker + * from a normal VIEW file (React Component Class) + */ + private autoGenerated: boolean = false; + + constructor( + options: IComponentPrototypeConfigure | ComponentDecoratorSpec, + extraConfigs: IComponentPrototypeExtraConfigs = {}, + ) { + this.options = isNewSpec(options) ? toOlderSpec(options) : options; + this.id = `prototype-${PID++}`; + if (extraConfigs.autoGenerated) { + this.autoGenerated = extraConfigs.autoGenerated; + } + if (this.options.hasSlot && !this.isContainer()) { + this.options.isContainer = true; + this.options.canDropIn = () => false; + } + } + + public getId() { + return this.id; + } + + public getUri() { + return this.options.uri; + } + + public getConfig(configName?: keyof IComponentPrototypeConfigure) { + if (configName) { + return this.options[configName]; + } + return this.options; + } + + public getPackageName() { + return this.packageName || this.options.packageName; + } + + public getContextInfo(name?: string): any { + return name ? get(this.options, ['context', name], '') : this.options; + } + + public getTitle() { + return this.options.title || this.getComponentName(); + } + + public getComponentName() { + return this.componentName || this.options.componentName || null; + } + + public getDocUrl() { + return this.options.docUrl; + } + + public getDefaultProps() { + return this.options.defaultProps || {}; + } + + public getPropConfigs() { + return this.options; + } + + public getExtraActions() { + let extraActions = this.options.extraActions; + if (!extraActions) { + return GlobalExtraActions; + } + if (!Array.isArray(extraActions)) { + extraActions = [extraActions]; + } + return [...GlobalExtraActions, ...extraActions]; + } + + public getCategory() { + if (this.category !== undefined) { + return this.category; + } + if (this.options.hasOwnProperty('category')) { + return this.options.category; + } + return '*'; + } + + public hasSlot() { + return this.options.hasSlot; + } + + public setCategory(category: string) { + this.category = category; + } + + public getIcon() { + return this.options.icon || ''; + } + + public setView(view: ComponentClass) { + this.view = view; + } + + public getView() { + return this.view || this.options.view || null; + } + + public getInitialChildren() { + return this.options.initialChildren || null; + } + + public getConfigure() { + let res = (this.options.configure || []).slice(); + GlobalPropsConfigure.forEach((config) => { + const position = config.position || 'bottom'; + + if (position === 'top') { + res.unshift(config); + } else if (position === 'bottom') { + res.push(config); + } else if (isObject(position.before || position.after)) { + const { prop, value } = position.before || position.after; + let index = -1; + if (prop && value) { + index = res.findIndex((e: any) => e[prop] === value); + } + if (index > -1) { + if (position.hasOwnProperty('after')) { + index += 1; + } + res.splice(index, 0, config); + } + } + }); + + const override = Overrides[this.getComponentName()]; + if (override) { + if (Array.isArray(override)) { + res = override; + } else { + let l = res.length; + let item; + while (l-- > 0) { + item = res[l]; + if (item.name in override) { + if (override[item.name]) { + res.splice(l, 1, override[item.name]); + } else { + res.splice(l, 1); + } + } + } + } + } + return res; + } + + public getRectSelector() { + return this.options.rectSelector; + } + + public isInline() { + return this.options.isInline != null ? this.options.isInline : null; + } + + public isContainer() { + if (isFunction(this.options.isContainer)) { + return (this.options.isContainer as any)(this); + } + return this.options.isContainer != null ? this.options.isContainer : false; + } + + public isModal() { + return this.options.isModal || false; + } + + public isFloating() { + return this.options.isFloating || false; + } + + public isAutoGenerated() { + return this.autoGenerated; + } + + public setPackageName(name: string) { + this.packageName = name; + } + + public setComponentName(componentName: string) { + this.componentName = componentName; + } + + /** + * transform value from hot-value to view + */ + public reduce(props: any) { + let reducers = this.options.reducers || []; + + if (!Array.isArray(reducers)) { + reducers = [reducers]; + } + + // prepend global reducers + reducers = GlobalPropsReducers.concat(reducers); + + if (reducers.length < 1) { + return props; + } + + props = reducers.reduce((xprops: any, reducer: any) => { + if (typeof reducer === 'function') { + return reducer(xprops); + } + return xprops; + }, props); + return props; + } + + public transformToActive(props: any) { + let transducers = this.options.transducers; + if (!transducers) { + return props; + } + if (!Array.isArray(transducers)) { + transducers = [transducers]; + } + props = transducers.reduce((xprops: any, transducer: any) => { + if (transducer && typeof transducer.toActive === 'function') { + return transducer.toActive(xprops); + } + return xprops; + }, props); + return props; + } + + public transformToStatic(props: any) { + let transducers = this.options.transducers; + if (!transducers) { + return props; + } + if (!Array.isArray(transducers)) { + transducers = [transducers]; + } + props = transducers.reduce((xprops: any, transducer: any) => { + if (transducer && typeof transducer.toStatic === 'function') { + return transducer.toStatic(xprops); + } + return xprops; + }, props); + return props; + } + + public canDragging(node?: Node) { + if (this.canSelecting()) { + const draggable = + this.options.canDragging === false || this.options.canDraging === false + ? false + : true; + if (GlobalNodeCanDragConfig.length > 0) { + if (!flow(GlobalNodeCanDragConfig)(node)) { + return false; + } + } + if (isFunction(draggable)) { + return draggable.call(node, node); + } + return draggable; + } + return false; + } + + public canHovering() { + return this.options.canHovering != null ? this.options.canHovering : true; + } + + public canSelecting() { + return this.options.canSelecting != null ? this.options.canSelecting : true; + } + + public canOperating() { + return this.options.canOperating != null ? this.options.canOperating : true; + } + + public canSetting() { + return this.canSelecting(); + } + + public canUseCondition() { + return this.options.canUseCondition != null + ? this.options.canUseCondition + : true; + } + + public canLoop() { + return this.options.canLoop != null ? this.options.canLoop : true; + } + + public canDropTo(container: Node) { + if (!this.canDragging()) { + return false; + } + + const oCanDropTo = + this.options.canDropTo != null + ? this.options.canDropTo + : this.options.canDropto; + if (oCanDropTo != null) { + return this.xcan(oCanDropTo, container); + } + return true; + } + + public canDropIn(dragment: Node) { + const oCanDropIn = + this.options.canDropIn != null + ? this.options.canDropIn + : this.options.canDroping; + if (oCanDropIn != null) { + return this.xcan(oCanDropIn, dragment); + } + return true; + } + + public didDropIn(dragment: Node, container: any) { + const fn = this.options.didDropIn; + if (typeof fn === 'function') { + fn.call(container || this, dragment); + } + } + + public didDropOut(dragment: Node, container: any) { + const fn = this.options.didDropOut; + if (typeof fn === 'function') { + fn.call(container || this, dragment); + } + } + + public canContain(dragment: Node) { + const oCanContain = this.options.canContain; + if (oCanContain != null) { + return this.xcan(oCanContain, dragment); + } + return true; + } + + public clone(options: IComponentPrototypeConfigure) { + return new Prototype(assign({}, this.options, options || {})); + } + + private xcan( + ocan: ((dragment: Node) => boolean) | string | string[], + placement: Node, + ): boolean; + private xcan(ocan: any, placement: Node): boolean { + const t = typeof ocan; + if (t === 'function') { + return ocan(placement); + } else if (Array.isArray(ocan)) { + return ocan.indexOf(placement.getComponentName()) > -1; + } else if (t === 'string') { + return ( + ocan + .trim() + .split(/[ ,;|]+/) + .indexOf(placement.getComponentName()) > -1 + ); + } + return ocan; + } +} + +export default Prototype; diff --git a/packages/vision-polyfill/src/bus.ts b/packages/vision-polyfill/src/bus.ts new file mode 100644 index 000000000..0d17720b1 --- /dev/null +++ b/packages/vision-polyfill/src/bus.ts @@ -0,0 +1,64 @@ +import logger from '@ali/vu-logger'; +import { EventEmitter } from 'events'; + +/** + * Bus class as an EventEmitter + */ +class Bus { + private emitter = new EventEmitter(); + + getEmitter() { + return this.emitter; + } + + // alias to sub + on(event: string | symbol, func: (...args: any[]) => any): any { + return this.sub(event, func); + } + + // alias to unsub + off(event: string, func: (...args: any[]) => any) { + this.unsub(event, func); + } + + // alias to pub + emit(event: string, ...args: any[]): boolean { + return this.pub(event, ...args); + } + + sub(event: string | symbol, func: (...args: any[]) => any) { + this.emitter.on(event, func); + return () => { + this.emitter.removeListener(event, func); + }; + } + + once(event: string, func: (...args: any[]) => any) { + this.emitter.once(event, func); + return () => { + this.emitter.removeListener(event, func); + }; + } + + unsub(event: string, func: (...args: any[]) => any) { + if (func) { + this.emitter.removeListener(event, func); + } else { + this.emitter.removeAllListeners(event); + } + } + + /** + * Release & Publish Events + */ + pub(event: string, ...args: any[]): boolean { + logger.info('INFO:', 'eventData:', event, ...args); + return this.emitter.emit(event, ...args); + } + + removeListener(eventName: string | symbol, callback: () => any) { + return this.emitter.removeListener(eventName, callback); + } +} + +export default new Bus(); diff --git a/packages/vision-polyfill/src/const.ts b/packages/vision-polyfill/src/const.ts new file mode 100644 index 000000000..1fe234190 --- /dev/null +++ b/packages/vision-polyfill/src/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/legao-assets.json b/packages/vision-polyfill/src/legao-assets.json new file mode 100644 index 000000000..4d0ce81c0 --- /dev/null +++ b/packages/vision-polyfill/src/legao-assets.json @@ -0,0 +1,694 @@ +{ + "version": "1.0.0", + "packages": [ + { + "urls": ["//g.alicdn.com/platform/c/react/16.5.2/react.min.js"], + "library": "React", + "package": "react", + "version": "16.5.2" + }, + { + "urls": ["//g.alicdn.com/platform/c/react-dom/16.5.2/react-dom.min.js"], + "library": "ReactDOM", + "package": "react-dom", + "version": "16.12.0" + }, + { + "urls": ["//g.alicdn.com/platform/c/prop-types/15.6.2/prop-types.js"], + "library": "PropTypes", + "package": "prop-types", + "version": "15.6.2" + }, + { + "library": "ReactRouter", + "package": "react-router" + }, + { + "library": "ReactRouterDOM", + "package": "react-router-dom" + }, + { + "library": "Babel", + "package": "babel-standalone" + }, + { + "library": "Recore", + "package": "@ali/recore" + }, + { + "urls": ["https://g.alicdn.com/code/lib/moment.js/2.24.0/moment-with-locales.min.js"], + "library": "moment", + "package": "moment", + "version": "2.24.0" + }, + { + "library": "VisualEngine", + "package": "engine" + }, + { + "library": "VisualEngine", + "package": "visualengine" + }, + { + "library": "VisualEngine", + "package": "@ali/visualengine" + }, + { + "library": "VisualEngineUtils", + "package": "engine-utils" + }, + { + "library": "VisualEngineUtils", + "package": "@ali/visualengine-utils" + }, + { + "library": "VisualEngine.ui.Popup", + "package": "@ali/ve-popups" + }, + { + "library": "VisualEngineUtils.FieldControl", + "package": "@ali/ve-field" + }, + { + "library": "VisualEngineUtils.BoolControl", + "package": "@ali/ve-bool-control" + }, + { + "library": "VisualEngineUtils.ChoiceControl", + "package": "@ali/ve-choice-control" + }, + { + "library": "VisualEngineUtils.ColorControl", + "package": "@ali/ve-color-control" + }, + { + "library": "VisualEngineUtils.DateControl", + "package": "@ali/ve-date-control" + }, + { + "library": "VisualEngineUtils.I18nControl", + "package": "@ali/ve-i18n-control" + }, + { + "library": "VisualEngineUtils.NumberControl", + "package": "@ali/ve-number-control" + }, + { + "library": "VisualEngineUtils.SelectControl", + "package": "@ali/ve-select-control" + }, + { + "library": "VisualEngineUtils.SortableControl", + "package": "@ali/ve-sortable" + }, + { + "library": "VisualEngineUtils.TextControl", + "package": "@ali/ve-text-control" + }, + { + "library": "VisualEngineUtils.ImageControl", + "package": "@ali/ve-image-control" + }, + { + "library": "VisualEngineUtils.SearchControl", + "package": "@ali/ve-search-control" + }, + { + "library": "VisualEngineUtils.BoolSetter", + "package": "@ali/vs-bool" + }, + { + "library": "VisualEngineUtils.ChoiceSetter", + "package": "@ali/vs-choice" + }, + { + "library": "VisualEngineUtils.CodeSetter", + "package": "@ali/vs-code" + }, + { + "library": "VisualEngineUtils.ColorSetter", + "package": "@ali/vs-color" + }, + { + "library": "VisualEngineUtils.DateSetter", + "package": "@ali/vs-date" + }, + { + "library": "VisualEngineUtils.I18nSetter", + "package": "@ali/vs-i18n" + }, + { + "library": "VisualEngineUtils.JsonSetter", + "package": "@ali/vs-json" + }, + { + "library": "VisualEngineUtils.ListSetter", + "package": "@ali/vs-list" + }, + { + "library": "VisualEngineUtils.NumberSetter", + "package": "@ali/vs-number" + }, + { + "library": "VisualEngineUtils.OptionsSetter", + "package": "@ali/vs-options" + }, + { + "library": "VisualEngineUtils.SelectSetter", + "package": "@ali/vs-select" + }, + { + "library": "VisualEngineUtils.TextSetter", + "package": "@ali/vs-text" + }, + { + "library": "VisualEngineUtils.ValidationSetter", + "package": "@ali/vs-validation" + }, + { + "library": "VisualEngineUtils.ImageSetter", + "package": "@ali/vs-image" + }, + { + "library": "VisualEngineUtils.StyleSetter", + "package": "@ali/vs-style" + }, + { + "library": "VisualEngineUtils.EventSetter", + "package": "@ali/vs-event" + }, + { + "library": "RenderEngine", + "package": "@ali/render-engine" + }, + { + "library": "Highcharts", + "package": "highcharts" + }, + { + "library": "Object", + "package": "highcharts-more" + }, + { + "library": "Highcharts && window.Highcharts.map", + "package": "highcharts/highmaps" + }, + { + "library": "jQuery", + "package": "$" + }, + { + "library": "jQuery", + "package": "jquery" + }, + { + "library": "_", + "package": "lodash" + }, + { + "library": "nattyFetch", + "package": "natty-fetch" + }, + { + "library": "nattyFetch", + "package": "natty-fetch/dist/natty-fetch.pc" + }, + { + "library": "nattyFetch", + "package": "natty-fetch/dist/natty-fetch" + }, + { + "library": "nattyStorage", + "package": "natty-storage" + }, + { + "library": "Rax", + "package": "rax" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-div/1.0.1/view.f31fe6d.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-div/1.0.1/view.d3df802.js" + ], + "library": "AliVcDiv", + "package": "@ali/vc-div", + "version": "1.0.1" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-page/1.0.5/view.3fdc557.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-page/1.0.5/view.ae881b7.js" + ], + "library": "AliVcPage", + "package": "@ali/vc-page", + "version": "1.0.5" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/view.1bae686.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/view.68be89a.js", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/view.mobile.1bae686.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/view.mobile.68a5782.js" + ], + "library": "AliVcDeep", + "package": "@ali/vc-deep", + "version": "2.0.11" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-shell/1.5.6/view.70bac75.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-shell/1.5.6/view.6174a1b.js" + ], + "library": "AliVcShell", + "package": "@ali/vc-shell", + "version": "1.5.6" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-slot/2.0.1/view.0e43387.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-slot/2.0.1/view.890474e.js" + ], + "library": "AliVcSlot", + "package": "@ali/vc-slot", + "version": "2.0.1" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.1/view.764bd38.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.1/view.0dcac71.js", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.1/view.mobile.8a20311.js" + ], + "library": "AliVcText", + "package": "@ali/vc-text", + "version": "4.0.1" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/view.387943c.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/view.ec70dd9.js", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/view.mobile.387943c.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/view.mobile.bc72f32.js" + ], + "library": "AliVcLink", + "package": "@ali/vc-link", + "version": "5.1.1" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/view.82ef4b0.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/view.51d10d6.js", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/view.mobile.82ef4b0.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/view.mobile.d608862.js" + ], + "library": "AliVcLinkBlock", + "package": "@ali/vc-link-block", + "version": "5.1.0" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-column/3.0.5/view.0e5dd32.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-column/3.0.5/view.e8a81f8.js" + ], + "library": "AliVcChartColumn", + "package": "@ali/vc-chart-column", + "version": "3.0.5" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-line/3.0.4/view.b437757.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-line/3.0.4/view.4c5d11a.js" + ], + "library": "AliVcChartLine", + "package": "@ali/vc-chart-line", + "version": "3.0.4" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-pie/3.0.2/view.0e43387.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-pie/3.0.2/view.4498f86.js" + ], + "library": "AliVcChartPie", + "package": "@ali/vc-chart-pie", + "version": "3.0.2" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-radar/3.0.2/view.0e43387.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-radar/3.0.2/view.ff9e48b.js" + ], + "library": "AliVcChartRadar", + "package": "@ali/vc-chart-radar", + "version": "3.0.2" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-markdown/2.0.0/view.3f91095.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-markdown/2.0.0/view.288bb26.js" + ], + "library": "AliVcMarkdown", + "package": "@ali/vc-markdown", + "version": "2.0.0" + }, + { + "urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/ac-amdp-home-consumer/1.0.11/view.f3f24d5.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/ac-amdp-home-consumer/1.0.11/view.f31409e.js" + ], + "library": "AliAcAmdpHomeConsumer", + "package": "@ali/ac-amdp-home-consumer", + "version": "1.0.11" + }, + { + "urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/react-json-view/1.22.0/main.8c072ba.js"], + "library": "AliReactJsonView", + "package": "@ali/react-json-view", + "version": "1.22.0" + } + ], + "components": [ + { + "componentName": "AliVcDiv", + "npm": { + "package": "@ali/vc-div", + "library": "AliVcDiv", + "version": "1.0.1", + "destructuring": false + }, + "props": [], + "x-prototype-urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-div/1.0.1/proto.a264564.js"] + }, + { + "componentName": "AliVcPage", + "npm": { + "package": "@ali/vc-page", + "library": "AliVcPage", + "version": "1.0.5", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-page/1.0.5/proto.899e4b1.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-page/1.0.5/proto.bfe05a5.js" + ] + }, + { + "componentName": "AliVcDeep", + "npm": { + "package": "@ali/vc-deep", + "library": "AliVcDeep", + "version": "2.0.11", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/proto.15be45f.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/proto.039dc00.js" + ] + }, + { + "componentName": "AliVcShell", + "npm": { + "package": "@ali/vc-shell", + "library": "AliVcShell", + "version": "1.5.6", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-shell/1.5.6/proto.70bac75.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-shell/1.5.6/proto.8d105cf.js" + ] + }, + { + "componentName": "AliVcSlot", + "npm": { + "package": "@ali/vc-slot", + "library": "AliVcSlot", + "version": "2.0.1", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-slot/2.0.1/proto.0e43387.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-slot/2.0.1/proto.9e01f34.js" + ] + }, + { + "componentName": "AliVcText", + "npm": { + "package": "@ali/vc-text", + "library": "AliVcText", + "version": "4.0.1", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.1/proto.595bd91.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.1/proto.90c4998.js" + ] + }, + { + "componentName": "AliVcLink", + "npm": { + "package": "@ali/vc-link", + "library": "AliVcLink", + "version": "5.1.1", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/proto.4828821.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/proto.91e063a.js" + ] + }, + { + "componentName": "AliVcLinkBlock", + "npm": { + "package": "@ali/vc-link-block", + "library": "AliVcLinkBlock", + "version": "5.1.0", + "destructuring": false + }, + "props": [], + "x-prototype-urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/proto.4e9a9d2.js"] + }, + { + "componentName": "AliVcChartColumn", + "npm": { + "package": "@ali/vc-chart-column", + "library": "AliVcChartColumn", + "version": "3.0.5", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-column/3.0.5/proto.5973a33.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-column/3.0.5/proto.df847e6.js" + ] + }, + { + "componentName": "AliVcChartLine", + "npm": { + "package": "@ali/vc-chart-line", + "library": "AliVcChartLine", + "version": "3.0.4", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-line/3.0.4/proto.11e8b2c.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-line/3.0.4/proto.bbc1a73.js" + ] + }, + { + "componentName": "AliVcChartPie", + "npm": { + "package": "@ali/vc-chart-pie", + "library": "AliVcChartPie", + "version": "3.0.2", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-pie/3.0.2/proto.81a7751.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-pie/3.0.2/proto.ce342e4.js" + ] + }, + { + "componentName": "AliVcChartRadar", + "npm": { + "package": "@ali/vc-chart-radar", + "library": "AliVcChartRadar", + "version": "3.0.2", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-radar/3.0.2/proto.81a7751.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-radar/3.0.2/proto.638100d.js" + ] + }, + { + "componentName": "AliVcMarkdown", + "npm": { + "package": "@ali/vc-markdown", + "library": "AliVcMarkdown", + "version": "2.0.0", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-markdown/2.0.0/proto.3f91095.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-markdown/2.0.0/proto.1bed9e5.js" + ] + }, + { + "componentName": "AliAcAmdpHomeConsumer", + "npm": { + "package": "@ali/ac-amdp-home-consumer", + "library": "AliAcAmdpHomeConsumer", + "version": "1.0.11", + "destructuring": false + }, + "props": [], + "x-prototype-urls": [ + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/ac-amdp-home-consumer/1.0.11/proto.f3f24d5.css", + "https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/ac-amdp-home-consumer/1.0.11/proto.f12090f.js" + ] + }, + { + "componentName": "AliReactJsonView", + "npm": { + "package": "@ali/react-json-view", + "library": "AliReactJsonView", + "version": "1.22.0", + "destructuring": false + }, + "props": [], + "configure": { + "component": {}, + "props": [ + { + "name": "src", + "title": "src", + "setter": "Object" + }, + { + "name": "name", + "title": "name", + "setter": "Input" + }, + { + "name": "theme", + "title": "theme", + "setter": "Input" + }, + { + "name": "collapsed", + "title": "collapsed", + "setter": "Switch" + }, + { + "name": "collapseStringsAfterLength", + "title": "collapseStringsAfterLength", + "setter": "Switch" + }, + { + "name": "shouldCollapse", + "title": "shouldCollapse", + "setter": "Switch" + }, + { + "name": "sortKeys", + "title": "sortKeys", + "setter": "Switch" + }, + { + "name": "groupArraysAfterLength", + "title": "groupArraysAfterLength", + "setter": "Number" + }, + { + "name": "indentWidth", + "title": "indentWidth", + "setter": "Number" + }, + { + "name": "enableClipboard", + "title": "enableClipboard", + "setter": "Switch" + }, + { + "name": "displayObjectSize", + "title": "displayObjectSize", + "setter": "Switch" + }, + { + "name": "displayDataTypes", + "title": "displayDataTypes", + "setter": "Switch" + }, + { + "name": "onEdit", + "title": "onEdit", + "setter": "Switch" + }, + { + "name": "onDelete", + "title": "onDelete", + "setter": "Switch" + }, + { + "name": "onAdd", + "title": "onAdd", + "setter": "Switch" + }, + { + "name": "onSelect", + "title": "onSelect", + "setter": "Switch" + }, + { + "name": "iconStyle", + "title": "iconStyle", + "setter": "Input" + }, + { + "name": "style", + "title": "style", + "setter": "Object" + }, + { + "name": "validationMessage", + "title": "validationMessage", + "setter": "Input" + }, + { + "name": "defaultValue", + "title": "defaultValue", + "setter": "Input" + } + ] + }, + "title": "json格式化展示" + } + ], + "componentList": [ + { + "title": "自定义", + "icon": "", + "children": [{ + "title": "json格式化展示", + "icon": "", + "snippets": [{ + "schema": { + "componentName": "AliReactJsonView", + "props": {} + }, + "title": "json格式化展示", + "screenshot": "" + }] + }] + } + ] +} diff --git a/packages/vision-polyfill/src/symbols.ts b/packages/vision-polyfill/src/symbols.ts new file mode 100644 index 000000000..4b8493292 --- /dev/null +++ b/packages/vision-polyfill/src/symbols.ts @@ -0,0 +1,17 @@ +export class SymbolManager { + private symbolMap: { [symbolName: string]: symbol } = {}; + + create(name: string): symbol { + if (this.symbolMap[name]) { + return this.symbolMap[name]; + } + this.symbolMap[name] = Symbol(name); + return this.symbolMap[name]; + } + + get(name: string) { + return this.symbolMap[name]; + } +} + +export default new SymbolManager(); diff --git a/packages/vision-polyfill/src/trunk.ts b/packages/vision-polyfill/src/trunk.ts new file mode 100644 index 000000000..a832c02b5 --- /dev/null +++ b/packages/vision-polyfill/src/trunk.ts @@ -0,0 +1,311 @@ +import lg from '@ali/vu-logger'; +import EventEmitter = require('events'); +import { ComponentClass } from 'react'; + +import Bundle from './bundle/bundle'; +import Prototype, { setPackages } from './bundle/prototype'; +import Bus from './bus'; + +interface IComponentInfo { + image?: string; + description?: string; + componentDetail?: string; + newVersion?: string; +} + +interface IComponentLoader { + load: (packageName: string, packageVersion: string, filePath?: string) => Promise; +} + +interface IComponentPrototypeMocker { + mockPrototype: (bundle: ComponentClass) => Prototype; +} + +interface IComponentBundle { + // @ali/vc-xxx + name: string; + // alias to property name + componentName?: string; + category?: string; + module: ComponentClass; +} + +interface IComponentBundleLoadingConfig { + isDIYComponent?: boolean; + // need to emit 'trunk_change' event + isSilence?: boolean; + isNpmComponent?: boolean; +} + +interface IComponentBundleConfigListItem { + name: string; + version: string; + path?: string; + // ac component in LeGao + // FIXME: remove this logic out of Trunk in the future + isDIYComponent?: boolean; + // install comp directly from npm + isNpmComponent?: boolean; +} + +interface IBeforeLoad extends IComponentBundleLoadingConfig { + name: string; + version: string; + path: string; +} + +type beforeLoadFn = (loadingConfig: IBeforeLoad) => IBeforeLoad; +type afterLoadFn = (bundle: IComponentBundle, loadingConfig: IBeforeLoad) => IComponentBundle; + +class Trunk { + private trunk: any[]; + private list: any[]; + private emitter: EventEmitter; + private componentBundleLoader: IComponentLoader; + private componentPrototypeMocker: IComponentPrototypeMocker; + + private beforeLoad: beforeLoadFn; + private afterLoad: afterLoadFn; + + constructor() { + this.trunk = []; + this.emitter = new EventEmitter(); + this.componentBundleLoader = null; + } + + isReady() { + return this.getList().length > 0; + } + + addBundle(bundle: Bundle, bundleOptions: { + before?: (bundle: Bundle) => Promise; + after?: (bundle: Bundle) => any; + } = {}) { + // filter exsits + bundle.filter((item) => { + const componentName = item.getComponentName(); + if (componentName && this.getPrototype(componentName)) { + return false; + } + return true; + }); + if (bundleOptions.before) { + bundleOptions.before.call(this, bundle).then((processedBundle: Bundle) => { + this.trunk.push(processedBundle); + this.emitter.emit('trunkchange'); + }); + } else { + this.trunk.push(bundle); + this.emitter.emit('trunkchange'); + } + if (bundleOptions.after) { + bundleOptions.after.call(this, bundle); + } + } + + /** + * 注册组件信息加载器 + */ + registerComponentBundleLoader(loader: IComponentLoader) { + // warn replacement method + this.componentBundleLoader = loader; + } + + registerComponentPrototypeMocker() { + console.warn('Trunk.registerComponentPrototypeMocker is deprecated'); + } + + getBundle(nameSpace?: string): Bundle { + console.warn('Trunk.getTrunk is deprecated'); + if (!nameSpace) { + return this.trunk[0]; + } + return find(this.trunk, (b: any) => b.getNamespace() === nameSpace); + } + + public getList(): any[] { + return this.list || this.trunk.reduceRight((prev, cur) => prev.concat(cur.getList()), []); + } + + /** + * 列出所有组件列表 + * + */ + listByCategory() { + console.warn('Trunk.listByCategory is deprecated'); + const categories: any[] = []; + const categoryMap: any = {}; + const categoryItems: any[] = []; + const defaultCategory = { + items: categoryItems, + name: '*', + }; + categories.push(defaultCategory); + categoryMap['*'] = defaultCategory; + this.getList().forEach((prototype) => { + const cat = prototype.getCategory(); + if (!cat) { + return; + } + if (!categoryMap.hasOwnProperty(cat)) { + const categoryMapItems: any[] = []; + categoryMap[cat] = { + items: categoryMapItems, + name: cat, + }; + categories.push(categoryMap[cat]); + } + categoryMap[cat].items.push(prototype); + }); + return categories; + } + + /** + * 获取仓库 + * + * @returns {Array} + */ + getTrunk() { + // legao-design 中有用 + console.warn('Trunk.getTrunk is deprecated'); + return this.trunk; + } + + /** + * 根据 componentName 查找对应的 prototype + * + * @param {string} componentName + * @returns {Prototype} + */ + getPrototype(componentName: string) { + if (!componentName) { + lg.error('ERROR: no component name found while get Prototype'); + return null; + } + const name = componentName.split('.'); + const namespace = name.length > 1 ? name[0] : ''; + let i = this.trunk.length; + let bundle; + let ns; + let prototype; + while (i-- > 0) { + bundle = this.trunk[i]; + ns = bundle.getNamespace(); + if (ns === '' || namespace === ns) { + prototype = bundle.get(componentName); + if (prototype) { + return prototype; + } + } + } + return null; + } + + public getPrototypeById(id: string) { + let i = this.trunk.length; + let bundle; + let prototype; + while (i-- > 0) { + bundle = this.trunk[i]; + prototype = bundle.getById(id); + if (prototype) { + return prototype; + } + } + return null; + } + + public getPrototypeView(componentName: string) { + const prototype = this.getPrototype(componentName); + return prototype ? prototype.getView() : null; + } + + public loadComponentBundleList(componentBundleList: IComponentBundleConfigListItem[]) { + Promise.all(componentBundleList.map((componentBundle) => { + const { name, version, path, ...bundleContextInfo } = componentBundle; + return this.loadComponentBundle(name, version, path, { + ...bundleContextInfo, + isDIYComponent: componentBundle.isDIYComponent, + isSilence: true, + }); + })).then((results) => { + results.forEach((r: any) => this.getBundle().addComponentBundle(r)); + this.emitter.emit('trunkchange'); + }); + } + + public loadComponentBundle( + name: string, + version: string, + path?: string, + options?: IComponentBundleLoadingConfig) { + const bundleList: IComponentBundle[] = []; + return new Promise((resolve: any, reject: any) => { + if (options && options.isDIYComponent) { + let result: IBeforeLoad = { name, version, path, ...options }; + if (isFunction(this.beforeLoad)) { + result = this.beforeLoad(result); + } + return this.componentBundleLoader.load(result.name, result.version, result.path) + .then((b: IComponentBundle) => { + if (isFunction(this.afterLoad)) { + this.afterLoad(b, { name, path, version, ...options }); + } + if (!options.isSilence) { + this.getBundle().addComponentBundle([b]); + this.emitter.emit('trunkchange'); + } + resolve([b]); + }) + .catch((e: Error) => { + Bus.emit('ve.error.networkError', e); + reject(e); + }); + } else { + this.componentBundleLoader.load(name, version, 'build/prototype.js') + .then((b: IComponentBundle) => { + bundleList.push(b); + return this.componentBundleLoader.load(name, version, 'build/prototypeView.js'); + }) + .then((b: IComponentBundle) => { + bundleList.push(b); + if (!options.isSilence) { + this.getBundle().addComponentBundle(bundleList); + this.emitter.emit('trunkchange'); + } + resolve(bundleList); + }) + .catch((e: Error) => { + Bus.emit('ve.error.networkError', e); + reject(e); + }); + } + }); + } + + removeComponentBundle(name: string) { + this.getBundle().removeComponentBundle(name); + this.emitter.emit('trunkchange'); + } + + beforeLoadBundle(fn: beforeLoadFn) { + this.beforeLoad = fn; + } + + afterLoadBundle(fn: afterLoadFn) { + this.afterLoad = fn; + } + + onTrunkChange(func: () => any) { + this.emitter.on('trunkchange', func); + return () => { + this.emitter.removeListener('trunkchange', func); + }; + } + + setPackages(packages: Array<{ package: string; library: object | string }>) { + setPackages(packages); + } +} + +export default new Trunk(); diff --git a/packages/vision-polyfill/src/vision.ts b/packages/vision-polyfill/src/vision.ts new file mode 100644 index 000000000..180bb361b --- /dev/null +++ b/packages/vision-polyfill/src/vision.ts @@ -0,0 +1,159 @@ +import * as utils from '@ali/ve-utils'; +import popups from '@ali/ve-popups'; +import Icons from '@ali/ve-icons'; +import { VE_EVENTS, VE_HOOKS } from './const'; +import Bus from './bus'; +import Symbols from './symbols'; + +const VEOldAPIs = { + /** + * VE.Popup + */ + Popup: popups, + /** + * VE.ui.xxx + * + * Core UI Components + */ + ui: { + Field: { + SettingField, + Stage, + CaptionField, + PopupField, + EntryField, + AccordionField, + BlockField, + InlineField, + PlainField + }, + Icon: Icons, + Icons, + Popup: popups, + /* + // for rax visualpage, will not support any + FaultComponent, + HiddenComponent, + UnknownComponent, + InsertionGhost, + */ + }, + + /** + * VE.context DI 实现 + * + * 默认未初始化,需要等待 init 之后 + */ + context: new VisualEngineContext(), + + /** + * VE.init + * + * Initialized the whole VisualEngine UI + */ + init: once((container?: Element, contextConfigs?: any) => { + if (!container) { + container = document.createElement('div'); + document.body.appendChild(container); + } + container.id = 'engine'; + ReactDOM.render(, container); + }), + + /** + * VE.modules.xxx + * + * VE BuildIn Modules + */ + modules: { + // SchemaManager, 没看到使用的地方 + // VisualDesigner, 没看到使用的地方 + VisualManager // legao-designer 有用 + // VisualRender, 没看到使用的地方 + I18nUtil, // vs-list vs-rhino-widget-mapping + Prop, // vs-list vs-rhino-widget-mapping + /* 没看到使用的地方 + Node, + Props, + + Scroller, + Insertion, + */ + }, + + /** + * VE Utils + */ + utils, + /* 包抽象 */ + Bundle, + + /* pub/sub 集线器 */ + // ve-quick-search-pane, ve-section-pane vp-in-place-editing ve-action-save + // ve-page-history vs-q-chart-data ve-action-pane ve-page-lock-pane + // ve-datapool-pane ve-youshu-card-param-pane + Bus, + + /* 拖拽引擎 */ + DragEngine, // 在 ve-metadata-pane, ve-section-pane, ve-trunk-pane-simple, ve-trunk-pane 中有用 + + /* 环境变量 */ + // vu-oneapi-parser vu-events-property ve-section-pane vs-formula vu-link-property + // ve-datapool-pane vs-form-validator vs-style vs-link vs-micro-link vs-link-options vu-field-property + Env, + + /* 状态交换 */ + // + Exchange, + + /* 状态 Flags */ + // legao-design tree-pane + Flags, + + /* 快捷键 */ + // legao-design + Hotkey, + + /* 多语言文案 */ + I18nUtil, + + /* 页面管理 */ + Pages, + + /* 面板管理 */ + Panes, + + /* 应用管理 */ + Project, + + /* 包原型 */ + Prototype, + + /* 组件仓库 */ + Trunk, + + /* 事件 */ + EVENTS: VE_EVENTS, // legao-design + + /* 修饰方法 */ + HOOKS: VE_HOOKS, // legao-design vu-visualpage-rax + + /* 视图管理 */ + Viewport, + + /* Symbol 管理类 */ + Symbols, // legao-design vu-action-util + + /** + * VisualEngine Logger Tool + */ + // lg: logger, 没看到使用的地方 + // logger, + + /* 版本号 */ + // Version: VERSION, + + // Location, + // Node, + // VirtualRenderingNode +} diff --git a/packages/vision-polyfill/x.js b/packages/vision-polyfill/x.js new file mode 100644 index 000000000..6d2990653 --- /dev/null +++ b/packages/vision-polyfill/x.js @@ -0,0 +1,46 @@ +const x = require('./assets1.json'); +const fs = require('fs'); + +const metadatas = []; +const libraries = []; +x.content.componentDependencies.forEach((item) => { + const componentName = item.alias || item.library; + const metadata = { + componentName, + npm: { + package: item.packageName, + library: item.library, + version: item.version, + destructuring: false + }, + props: [], + }; + metadatas.push(metadata); + + libraries.push({ + "urls": item.urls, + "library": item.library, + "package": item.packageName, + "version": item.version + }); + + if (item.prototypeConfigsUrl) { + // componentName 基本无效 内部设置为 pending + // 设置 componentMeta 实体为 pendingComponentMeta,以获取 prototype 的值 + // 技术处理手段: url?processId=xxxx + // createPrototype 时,可获取 currentScript, 拿到 processId, 进行关联 + metadata['x-prototype-urls'] = item.prototypeConfigsUrl; + } + + // 出现在没有prototypeConfig 的情况 + else if (item.components) { + const meta = item.components[0]; + metadata.componentName = meta.componentName; + metadata.configure = meta.configure; + metadata.title = meta.title; + } +}); + + +fs.writeFileSync('./metadatas.json', JSON.stringify(metadatas, null, 2), 'utf-8'); +fs.writeFileSync('./libraries.json', JSON.stringify(libraries, null, 2), 'utf-8'); diff --git a/scripts/deploy.bak.sh b/scripts/deploy.bak.sh index e2775fd55..937aef3bf 100755 --- a/scripts/deploy.bak.sh +++ b/scripts/deploy.bak.sh @@ -31,5 +31,5 @@ mv packages/demo/build $BUILD_DEST mv packages/react-simulator-renderer/dist/* $BUILD_DEST mv packages/globals/dist/* $BUILD_DEST -cp html/* $BUILD_DEST +cp deploy-space/html/* $BUILD_DEST echo "complete"