diff --git a/packages/designer/src/component-meta.ts b/packages/designer/src/component-meta.ts index 4b2eeed96..3c531acb7 100644 --- a/packages/designer/src/component-meta.ts +++ b/packages/designer/src/component-meta.ts @@ -9,6 +9,7 @@ import { getRegisteredMetadataTransducers, registerMetadataTransducer, computed, + NestingFilter, } from '@ali/lowcode-globals'; import { Node, NodeParent } from './document'; import { Designer } from './designer'; @@ -24,6 +25,9 @@ function ensureAList(list?: string | string[]): string[] | null { return null; } if (!Array.isArray(list)) { + if (typeof list !== 'string') { + return null; + } list = list.split(/ *[ ,|] */).filter(Boolean); } if (list.length < 1) { @@ -32,6 +36,27 @@ function ensureAList(list?: string | string[]): string[] | null { return list; } +function isRegExp(obj: any): obj is RegExp { + return obj && obj.test && obj.exec && obj.compile; +} + +function buildFilter(rule?: string | string[] | RegExp | NestingFilter) { + if (!rule) { + return null; + } + if (typeof rule === 'function') { + return rule; + } + if (isRegExp(rule)) { + return (testNode: Node | NodeSchema) => rule.test(testNode.componentName); + } + const list = ensureAList(rule); + if (!list) { + return null; + } + return (testNode: Node | NodeSchema) => list.includes(testNode.componentName); +} + export class ComponentMeta { readonly isComponentMeta = true; private _npm?: NpmInfo; @@ -64,8 +89,8 @@ export class ComponentMeta { return config?.combined || config?.props || []; } - private parentWhitelist?: string[] | null; - private childWhitelist?: string[] | null; + private parentWhitelist?: NestingFilter | null; + private childWhitelist?: NestingFilter | null; private _title?: TitleContent; get title() { @@ -123,8 +148,8 @@ export class ComponentMeta { this._rectSelector = component.rectSelector; if (component.nestingRule) { const { parentWhitelist, childWhitelist } = component.nestingRule; - this.parentWhitelist = ensureAList(parentWhitelist); - this.childWhitelist = ensureAList(childWhitelist); + this.parentWhitelist = buildFilter(parentWhitelist); + this.childWhitelist = buildFilter(childWhitelist); } } else { this._isContainer = false; @@ -172,7 +197,7 @@ export class ComponentMeta { checkNestingUp(my: Node | NodeData, parent: NodeParent) { // 检查父子关系,直接约束型,在画布中拖拽直接掠过目标容器 if (this.parentWhitelist) { - return this.parentWhitelist.includes(parent.componentName); + return this.parentWhitelist(parent, my); } return true; } @@ -180,7 +205,7 @@ export class ComponentMeta { checkNestingDown(my: Node, target: Node | NodeSchema) { // 检查父子关系,直接约束型,在画布中拖拽直接掠过目标容器 if (this.childWhitelist) { - return this.childWhitelist.includes(target.componentName); + return this.childWhitelist(target, my); } return true; } diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts index 28439db23..a53002477 100644 --- a/packages/designer/src/designer/designer.ts +++ b/packages/designer/src/designer/designer.ts @@ -12,7 +12,7 @@ import { import { Project } from '../project'; import { Node, DocumentModel, insertChildren, isRootNode, NodeParent } from '../document'; import { ComponentMeta } from '../component-meta'; -import { INodeSelector } from '../simulator'; +import { INodeSelector, Component } from '../simulator'; import { Scroller, IScrollable } from './scroller'; import { Dragon, isDragNodeObject, isDragNodeDataObject, LocateEvent, DragObject } from './dragon'; import { ActiveTracker } from './active-tracker'; @@ -306,6 +306,7 @@ export class Designer { private _lostComponentMetasMap = new Map(); private buildComponentMetasMap(metas: ComponentMetadata[]) { + console.info(this._componentMetasMap); metas.forEach((data) => this.createComponentMeta(data)); } @@ -352,10 +353,13 @@ export class Designer { return meta; } - @computed get componentsMap(): { [key: string]: NpmInfo } { + @computed get componentsMap(): { [key: string]: NpmInfo | Component } { const maps: any = {}; this._componentMetasMap.forEach((config, key) => { - if (config.npm) { + const view = config.getMetadata().experimental?.view; + if (view) { + maps[key] = view; + } else if (config.npm) { maps[key] = config.npm; } }); diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index c52329dfa..84f9080be 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -466,6 +466,36 @@ export class Node { this.props.purge(); this.document.internalRemoveAndPurgeNode(this); } + + // ======= compatibles ==== + isEmpty(): boolean { + return this.children?.isEmpty() || true; + } + getStatus() { + return 'default'; + } + setStatus() { + + } + getDOMNode() { + const instance = this.document.simulator?.getComponentInstances(this)?.[0]; + if (!instance) { + return; + } + return this.document.simulator?.findDOMNodes(instance)?.[0]; + } + getChildren() { + return this.children; + } + getPage() { + return this.document; + } + getComponentName() { + return this.componentName; + } + insertBefore(node: Node, ref?: Node) { + this.children?.insert(node, ref ? ref.index : null); + } } export interface NodeParent extends Node { diff --git a/packages/editor-core/src/definitions.ts b/packages/editor-core/src/definitions.ts index 6feee78eb..52fff0e7b 100644 --- a/packages/editor-core/src/definitions.ts +++ b/packages/editor-core/src/definitions.ts @@ -109,7 +109,7 @@ export interface I18nConfig { export type I18nFunction = (key: string, params: any) => string; export interface Utils { - [key: string]: (...args: []) => any; + [key: string]: (...args: any[]) => any; } export interface PluginProps { diff --git a/packages/plugin-designer/src/index.tsx b/packages/plugin-designer/src/index.tsx index 6a45e663f..c05b32500 100644 --- a/packages/plugin-designer/src/index.tsx +++ b/packages/plugin-designer/src/index.tsx @@ -35,8 +35,8 @@ export default class DesignerPlugin extends PureComponent item.type === 'ComponentMetadata') : [], - library: packages ? Object.values(packages) : [], + componentMetadatas: components || [], + library: packages || [], }; this.setState(state); }; @@ -63,6 +63,8 @@ export default class DesignerPlugin extends PureComponent svg { width: 16px; height: 16px; + * { + fill: var(--color-icon-normal, rgba(31, 56, 88, 0.4)); + } } } diff --git a/packages/plugin-settings-pane/src/index.tsx b/packages/plugin-settings-pane/src/index.tsx index 93e435ae3..f516bb307 100644 --- a/packages/plugin-settings-pane/src/index.tsx +++ b/packages/plugin-settings-pane/src/index.tsx @@ -32,7 +32,7 @@ export default class SettingsMainView extends Component { if (this.main.isMulti) { return (
- {createIcon(this.main.componentMeta?.icon)} + {createIcon(this.main.componentMeta?.icon, { className: 'lc-settings-navigator-icon'})} <span>x {this.main.nodes.length}</span> </div> @@ -57,7 +57,7 @@ export default class SettingsMainView extends Component { return ( <div className="lc-settings-navigator"> - {createIcon(this.main.componentMeta?.icon)} + {createIcon(this.main.componentMeta?.icon, { className: 'lc-settings-navigator-icon'})} <Breadcrumb className="lc-settings-node-breadcrumb">{items}</Breadcrumb> </div> ); diff --git a/packages/plugin-settings-pane/src/main.ts b/packages/plugin-settings-pane/src/main.ts index 790627a70..d7236e842 100644 --- a/packages/plugin-settings-pane/src/main.ts +++ b/packages/plugin-settings-pane/src/main.ts @@ -4,6 +4,7 @@ import { ComponentMeta, Node, Designer, Selection } from '@ali/lowcode-designer' import { TitleContent, FieldExtraProps, SetterType, CustomView, FieldConfig, isCustomView } from '@ali/lowcode-globals'; import { getTreeMaster } from '@ali/lowcode-plugin-outline-pane'; import Editor from '@ali/lowcode-editor-core'; +import { Transducer } from './utils'; export interface SettingTarget { // 所设置的节点集,至少一个 @@ -91,6 +92,7 @@ export class SettingField implements SettingTarget { readonly componentMeta: ComponentMeta | null; readonly designer: Designer; readonly top: SettingTarget; + readonly transducer: Transducer; get path() { const path = this.parent.path.slice(); if (this.type === 'field') { @@ -139,6 +141,8 @@ export class SettingField implements SettingTarget { if (this.type === 'group' && items) { this.initItems(items); } + + this.transducer = new Transducer(this, { setter }); } onEffect(action: () => void): () => void { @@ -272,6 +276,27 @@ export class SettingField implements SettingTarget { purge() { this.disposeItems(); } + + // ======= compatibles ==== + getHotValue(): any { + return this.transducer.toHot(this.getValue()); + } + + setHotValue(data: any) { + this.setValue(this.transducer.toNative(data)); + } + + getNode() { + return this.nodes[0]; + } + + getProps() { + return this.parent; + } + + onValueChange() { + return () => {}; + } } export function isSettingField(obj: any): obj is SettingField { diff --git a/packages/plugin-settings-pane/src/settings-pane.tsx b/packages/plugin-settings-pane/src/settings-pane.tsx index 2f91c9450..8ff5459c0 100644 --- a/packages/plugin-settings-pane/src/settings-pane.tsx +++ b/packages/plugin-settings-pane/src/settings-pane.tsx @@ -111,7 +111,7 @@ class SettingFieldView extends Component<{ field: SettingField }> { forceInline: extraProps.forceInline, key: field.id, // === injection - prop: field, // for compatible + prop: field, // for compatible vision field, // === IO value, // reaction point diff --git a/packages/plugin-settings-pane/src/style.less b/packages/plugin-settings-pane/src/style.less index a6b98ebcf..ccc6c34fb 100644 --- a/packages/plugin-settings-pane/src/style.less +++ b/packages/plugin-settings-pane/src/style.less @@ -1,36 +1,3 @@ -:root { - --color-brand: #006cff; - --color-brand-light: #197aff; - --color-brand-dark: #0060e5; - --color-icon-normal: rgba(31, 56, 88, 0.4); - --color-icon-hover: rgba(31, 56, 88, 0.6); - --color-icon-active: #006cff; - --color-icon-reverse: #ffffff; - --color-line-light: rgba(31, 56, 88, 0.05); - --color-line-normal: rgba(31, 56, 88, 0.1); - --color-line-darken: rgba(18, 32, 50, 0.1); - --color-title: rgba(0, 0, 0, 0.8); - --color-text: rgba(0, 0, 0, 0.6); - --color-text-dark: rgba(0, 0, 0, 0.6); - --color-text-light: rgba(26, 26, 26, 0.6); - --color-text-reverse: rgba(255, 255, 255, 0.8); - --color-text-regular: rgba(31, 56, 88, 0.8); - --color-field-label: rgba(0, 0, 0, 0.4); - --color-field-text: rgba(0, 0, 0, 0.6); - --color-field-placeholder: rgba(31, 56, 88, 0.3); - --color-field-border: rgba(31, 56, 88, 0.3); - --color-field-border-hover: rgba(31, 56, 88, 0.4); - --color-field-border-active: rgba(31, 56, 88, 0.6); - --color-field-background: #ffffff; - --color-pane-background: #ffffff; - --color-block-background-normal: #ffffff; - --color-block-background-light: rgba(31, 56, 88, 0.03); - --color-block-background-shallow: rgba(31, 56, 88, 0.06); - --color-block-background-dark: rgba(31, 56, 88, 0.1); - --color-block-background-disabled: rgba(31, 56, 88, 0.2); - --color-block-background-deep-dark: #BAC3CC; -} - .lc-settings-main { position: relative; height: 100%; @@ -50,6 +17,13 @@ align-items: center; padding-left: 5px; border-bottom: 1px solid var(--color-line-normal); + .lc-settings-navigator-icon { + width: 16px; + height: 16px; + * { + fill: var(--color-icon-normal, rgba(31, 56, 88, 0.4)); + } + } .lc-settings-node-breadcrumb { margin-left: 5px; .next-breadcrumb { @@ -126,6 +100,8 @@ left: 0; bottom: 0; overflow-y: auto; + outline: none !important; + box-shadow: none !important; } } .lc-outline-pane { diff --git a/packages/plugin-settings-pane/src/utils.js b/packages/plugin-settings-pane/src/utils.js new file mode 100644 index 000000000..b8f5bbdcc --- /dev/null +++ b/packages/plugin-settings-pane/src/utils.js @@ -0,0 +1,41 @@ +function getHotterFromSetter(setter) { + return setter && (setter.Hotter || (setter.type && setter.type.Hotter)) || []; // eslint-disable-line +} + +function getTransducerFromSetter(setter) { + return setter && ( + setter.transducer || setter.Transducer + || (setter.type && (setter.type.transducer || setter.type.Transducer)) + ) || null; // eslint-disable-line +} + +function combineTransducer(transducer, arr, context) { + if (!transducer && Array.isArray(arr)) { + const [toHot, toNative] = arr; + transducer = { toHot, toNative }; + } + + return { + toHot: (transducer && transducer.toHot || (x => x)).bind(context), // eslint-disable-line + toNative: (transducer && transducer.toNative || (x => x)).bind(context), // eslint-disable-line + }; +} + +export class Transducer { + constructor(context, config) { + this.setterTransducer = combineTransducer( + getTransducerFromSetter(config.setter), + getHotterFromSetter(config.setter), + context, + ); + this.context = context; + } + + toHot(data) { + return this.setterTransducer.toHot(data); + } + + toNative(data) { + return this.setterTransducer.toNative(data); + } +} diff --git a/packages/react-renderer/src/engine/base.jsx b/packages/react-renderer/src/engine/base.jsx index a7990a401..60787073d 100644 --- a/packages/react-renderer/src/engine/base.jsx +++ b/packages/react-renderer/src/engine/base.jsx @@ -1,4 +1,4 @@ -import React, { PureComponent, createElement as reactCreateElement } from 'react'; +import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import Debug from 'debug'; import Div from '@ali/iceluna-comp-div'; @@ -312,9 +312,9 @@ export default class BaseEngine extends PureComponent { } else if (typeof idx === 'number' && !props.key) { props.key = idx; } - const createElement = engine.props.customCreateElement || reactCreateElement; + props.__id = schema.id; const renderComp = (props) => { - return createElement( + return engine.createElement( Comp, props, (!isFileSchema(schema) && diff --git a/packages/react-renderer/src/engine/index.jsx b/packages/react-renderer/src/engine/index.jsx index 00930d670..deda45d03 100644 --- a/packages/react-renderer/src/engine/index.jsx +++ b/packages/react-renderer/src/engine/index.jsx @@ -1,4 +1,4 @@ -import React, { PureComponent } from 'react'; +import React, { PureComponent, createElement as reactCreateElement } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import Debug from 'debug'; @@ -85,8 +85,12 @@ export default class Engine extends PureComponent { } }; + createElement(Component, props, children) { + return (this.props.customCreateElement || reactCreateElement)(Component, props, children); + } + render() { - const { schema, designMode, appHelper, components } = this.props; + const { schema, designMode, appHelper, components, customCreateElement } = this.props; if (isEmpty(schema)) { return null; } @@ -94,7 +98,7 @@ export default class Engine extends PureComponent { return '模型结构异常'; } debug('entry.render'); - const allComponents = { ...ENGINE_COMPS, ...components }; + const allComponents = { ...components, ...ENGINE_COMPS }; const Comp = allComponents[schema.componentName]; if (Comp) { return ( diff --git a/packages/react-simulator-renderer/src/renderer-view.tsx b/packages/react-simulator-renderer/src/renderer-view.tsx index 48c2be875..bedec13f8 100644 --- a/packages/react-simulator-renderer/src/renderer-view.tsx +++ b/packages/react-simulator-renderer/src/renderer-view.tsx @@ -3,6 +3,7 @@ import { ReactInstance, Fragment, Component, createElement } from 'react'; import { observer } from '@recore/obx-react'; import { SimulatorRenderer } from './renderer'; import './renderer.less'; +import { host } from './host'; export default class SimulatorRendererView extends Component<{ renderer: SimulatorRenderer }> { render() { @@ -50,8 +51,11 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> { designMode={renderer.designMode} suspended={renderer.suspended} self={renderer.scope} - customCreateElement={(Component, props, children) => { - return createElement(Component, props, children); + customCreateElement={(Component: any, props: any, children: any) => { + const { __id, __desingMode, ...viewProps } = props; + viewProps.componentId = __id; + viewProps._leaf = host.document.getNode(__id); + return createElement(Component, viewProps, children); }} onCompGetRef={(schema: any, ref: ReactInstance | null) => { renderer.mountInstance(schema.id, ref); diff --git a/packages/react-simulator-renderer/src/renderer.ts b/packages/react-simulator-renderer/src/renderer.ts index 77f2a091b..06e7787a2 100644 --- a/packages/react-simulator-renderer/src/renderer.ts +++ b/packages/react-simulator-renderer/src/renderer.ts @@ -1,9 +1,9 @@ -import { createElement, ReactInstance } from 'react'; +import { createElement, ReactInstance, ComponentType } from 'react'; import { render as reactRender } from 'react-dom'; import { host } from './host'; import SimulatorRendererView from './renderer-view'; import { computed, obx } from '@recore/obx'; -import { Asset } from '@ali/lowcode-globals'; +import { Asset, isReactComponent } from '@ali/lowcode-globals'; import { getClientRects } from './utils/get-client-rects'; import loader from './utils/loader'; import { reactFindDOMNodes, FIBER_KEY } from './utils/react-find-dom-nodes'; @@ -311,12 +311,17 @@ export interface LibraryMap { [key: string]: string; } -function buildComponents(libraryMap: LibraryMap, componentsMap: { [componentName: string]: NpmInfo }) { +function buildComponents(libraryMap: LibraryMap, componentsMap: { [componentName: string]: NpmInfo | ComponentType<any> }) { const components: any = {}; Object.keys(componentsMap).forEach((componentName) => { - const component = findComponent(libraryMap, componentName, componentsMap[componentName]); - if (component) { + let component = componentsMap[componentName]; + if (isReactComponent(component)) { components[componentName] = component; + } else { + component = findComponent(libraryMap, componentName, component); + if (component) { + components[componentName] = component; + } } }); return components; diff --git a/packages/vision-polyfill/build.json b/packages/vision-polyfill/build.json index d7df7c427..27e93581d 100644 --- a/packages/vision-polyfill/build.json +++ b/packages/vision-polyfill/build.json @@ -1,6 +1,8 @@ { "entry": { - "index": "src/demo/index.ts" + "index": "src/demo/index.ts", + "react-simulator-renderer": "../react-simulator-renderer/src/index.ts", + "vision": "src/vision.ts" }, "vendor": false, "devServer": { @@ -12,7 +14,8 @@ "react-dom": "window.ReactDOM", "prop-types": "window.PropTypes", "@alifd/next": "window.Next", - "@ali/lowcode-globals": "window.LCEGlobals" + "@ali/visualengine": "window.VisualEngine", + "@ali/visualengine-utils": "window.VisualEngineUtils" }, "plugins": [ [ diff --git a/packages/vision-polyfill/package.json b/packages/vision-polyfill/package.json index 607ad4181..b5aa88517 100644 --- a/packages/vision-polyfill/package.json +++ b/packages/vision-polyfill/package.json @@ -21,6 +21,7 @@ "@ali/lowcode-plugin-undo-redo": "^0.8.6", "@ali/lowcode-plugin-zh-en": "^0.8.8", "@ali/lowcode-setters": "^0.8.8", + "@ali/ve-i18n-util": "^2.0.2", "@ali/ve-icons": "^4.1.9", "@ali/ve-less-variables": "2.0.3", "@ali/ve-popups": "^4.2.5", diff --git a/packages/vision-polyfill/public/index.html b/packages/vision-polyfill/public/index.html index 1667778be..96ebe9089 100644 --- a/packages/vision-polyfill/public/index.html +++ b/packages/vision-polyfill/public/index.html @@ -9,17 +9,67 @@ <script src="https://g.alicdn.com/code/lib/react/16.9.0/umd/react.development.js"></script> <script src="https://g.alicdn.com/code/lib/react-dom/16.9.0/umd/react-dom.development.js"></script> <script src="https://g.alicdn.com/code/lib/prop-types/15.7.2/prop-types.js"></script> - <script> React.PropTypes = PropTypes; </script> + <script> + React.PropTypes = PropTypes; + </script> + <script src="https://g.alicdn.com/platform/c/??react15-polyfill/0.0.1/dist/index.js,lodash/4.6.1/lodash.min.js,immutable/3.7.6/dist/immutable.min.js,natty-storage/2.0.2/dist/natty-storage.min.js,natty-fetch/2.6.0/dist/natty-fetch.pc.min.js,tinymce/4.2.5/tinymce-full.js"></script> <script src="https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"></script> <link rel="stylesheet" href="https://alifd.alicdn.com/npm/@alifd/next/1.11.6/next.min.css" /> <script src="https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.js"></script> - <script src="https://g.alicdn.com/vision/visualengine-utils/4.3.1/engine-utils.js"></script> <!-- lowcode engine globals --> - <link rel="stylesheet" href="https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/0.9.0/globals.css" /> + <link href="/css/vision.css" rel="stylesheet" /> + <script> + window.pageConfig = { + env: 'release', + locale: 'zh_CN', + pageType: 'single', + deviceType: 'web', + appName: '基础包管理后台', + appType: 'legao_base_packages', + templateType: '', + pageId: 'FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V', + slug: 'test', + appMode: 'back', + isAppAdmin: 'y', + isSuperAdmin: 'n', + isBetaDeveloper: 'n', + formType: 'display', + title: { en_US: '测试', type: 'i18n', zh_CN: '测试' }, + urlPrefix: 'https://go.alibaba-inc.com', + APIUrlPrefix: 'https://go.alibaba-inc.com', + devVersion: '0.1.0', // 这个是子应用的变更 id + subAppType: '0.1.0', + appKey: 'legao_base_packages', + RE_VERSION: '7.1.1', + appSource: '', + isDomainDefault: 'n', + useReleaseBundle: 'n', + isDomainPkg: 'n', + medusaAppName: '', + domainCode: 'kS6SyH', + aecp: { + mdcDomain: '', + projectId: '', + appCode: '', + }, + designerConfigs: {}, + navConfig: + '{"appName":{"en_US":"基础包管理后台","key":"","type":"i18n","zh_CN":"基础包管理后台"},"bgColor":"white","data":[{"children":[],"hidden":false,"icon":"","inner":true,"navUuid":"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V","relateUuid":"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V","slug":"test","targetNew":false,"title":{"en_US":"测试","type":"i18n","zh_CN":"测试"}}],"isFixed":"y","isFold":"y","isFoldHorizontal":"n","languageChangeUrl":{"en_US":"/common/account/changeAccountLanguage.json","type":"i18n","zh_CN":"/common/account/changeAccountLanguage.json"},"layout":"auto","navStyle":"orange","navTheme":"light","openSubMode":false,"showAppTitle":true,"showCrumb":true,"showIcon":false,"showLanguageChange":true,"showNav":true,"showSearch":"n","singletons":{"FORM-3KYJN7RV-DIOD8LLK1WGQ89S7NHA92-QJVH497K-V":{"isFixed":"n","isFold":"n","isFoldHorizontal":"n","showAppTitle":false,"showCrumb":false,"showLanguageChange":false,"showNav":false,"showSearch":"n","singleton":false},"test":{"$ref":"$.singletons.FORM\\-3KYJN7RV\\-DIOD8LLK1WGQ89S7NHA92\\-QJVH497K\\-V"}},"type":"top_fold"}', + historyType: 'HASH', + isSinglePage: 'n', + rhino: 'n', + isMiniApp: '', + taskId: '', + appSchema: 'V5', + openSubMode: 'n', + }; + window.g_config = {}; + </script> </head> <body> <!-- lowcode engine globals --> - <script src="https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/0.9.0/globals.js"></script> + <script src="/js/vision.js"></script> + <script src="https://g.alicdn.com/vision/visualengine-utils/4.3.1/engine-utils.js"></script> </body> </html> diff --git a/packages/vision-polyfill/src/bundle/prototype.ts b/packages/vision-polyfill/src/bundle/prototype.ts index 40b25b520..090b58031 100644 --- a/packages/vision-polyfill/src/bundle/prototype.ts +++ b/packages/vision-polyfill/src/bundle/prototype.ts @@ -148,7 +148,7 @@ class Prototype { static overridePropsConfigure = overridePropsConfigure; static create(config: OldPrototypeConfig | ComponentMetadata | ComponentMeta) { return new Prototype(config); - }; + } private id: string; private meta: ComponentMeta; @@ -241,6 +241,7 @@ class Prototype { setPackageName(name: string) { this.meta.setNpm({ package: name, + componentName: this.getComponentName(), }); } @@ -256,7 +257,10 @@ class Prototype { } getView() { - return this.meta.getMetadata().experimental?.view || designer.currentDocument?.simulator?.getComponent(this.getComponentName()); + return ( + this.meta.getMetadata().experimental?.view || + designer.currentDocument?.simulator?.getComponent(this.getComponentName()) + ); } } diff --git a/packages/vision-polyfill/src/bundle/upgrade-metadata.ts b/packages/vision-polyfill/src/bundle/upgrade-metadata.ts index d0a74b4e5..bf742c542 100644 --- a/packages/vision-polyfill/src/bundle/upgrade-metadata.ts +++ b/packages/vision-polyfill/src/bundle/upgrade-metadata.ts @@ -143,11 +143,11 @@ export interface OldPrototypeConfig { canSelecting?: boolean; canContain?: (dragment: Node) => boolean; // => nestingRule - canDropTo?: ((container: Node) => boolean) | string | string[]; // => nestingRule - canDropto?: (container: Node) => boolean; // => nestingRule + canDropTo?: ((container: Node) => boolean) | boolean | string | string[]; // => nestingRule + canDropto?: ((container: Node) => boolean) | boolean | string | string[]; // => nestingRule - canDropIn?: ((dragment: Node) => boolean) | string | string[]; // => nestingRule - canDroping?: (dragment: Node) => boolean; // => nestingRule + canDropIn?: ((dragment: Node) => boolean) | boolean | string | string[]; // => nestingRule + canDroping?: ((dragment: Node) => boolean) | boolean | string | string[]; // => nestingRule didDropOut?: (dragment: any, container: any) => void; // => hooks didDropIn?: (dragment: any, container: any) => void; // => hooks @@ -387,9 +387,9 @@ export function upgradePropConfig(config: OldPropConfig) { } export function upgradeConfigure(items: OldPropConfig[]) { - const configure = []; + const configure: any[] = []; let ignoreSlotName: any = null; - return items.forEach((config) => { + items.forEach((config) => { if (config.slotName) { ignoreSlotName = config.slotName; } else if (ignoreSlotName) { @@ -401,6 +401,7 @@ export function upgradeConfigure(items: OldPropConfig[]) { } configure.push(upgradePropConfig(config)); }); + return configure; } export function upgradeActions(actions?: Array<ComponentType<any> | ReactElement> | (() => ReactElement)) { @@ -507,11 +508,21 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) { if (canContain) { nestingRule.descendantWhitelist = canContain; } - if (canDropTo || canDropto) { - nestingRule.parentWhitelist = canDropTo || canDropto; + if (canDropTo != null || canDropto != null) { + if (canDropTo === false || canDropto === false) { + nestingRule.parentWhitelist = () => false; + } + if (canDropTo !== true && canDropto !== true) { + nestingRule.parentWhitelist = canDropTo || canDropto; + } } - if (canDropIn || canDroping) { - nestingRule.childWhitelist = canDropIn || canDroping; + if (canDropIn != null || canDroping != null) { + if (canDropIn === false || canDroping === false) { + nestingRule.childWhitelist = () => false; + } + if (canDropIn !== true && canDroping !== true) { + nestingRule.childWhitelist = canDropIn || canDroping; + } } component.nestingRule = nestingRule; diff --git a/packages/vision-polyfill/src/demo/index.ts b/packages/vision-polyfill/src/demo/index.ts index bd6ae4f6e..508fbbeee 100644 --- a/packages/vision-polyfill/src/demo/index.ts +++ b/packages/vision-polyfill/src/demo/index.ts @@ -1,7 +1,9 @@ -import Engine from '../vision'; // VisualEngine -import { editor } from '../editor'; +// @ts-ignore +import Engine from '@ali/visualengine'; import loadUrls from './loader'; +const { editor } = Engine; + Engine.init(); load(); @@ -12,14 +14,7 @@ async function load() { loadSchema(); } -const externals = [ - 'react', - 'react-dom', - 'prop-types', - 'react-router', - 'react-router-dom', - '@ali/recore', -]; +const externals = ['react', 'react-dom', 'prop-types', 'react-router', 'react-router-dom', '@ali/recore']; async function loadAssets() { const assets = await editor.utils.get('./legao-assets.json'); // Trunk.setPackages(assets.packages); @@ -28,7 +23,7 @@ async function loadAssets() { assets.packages.forEach((item: any) => { if (item.package.indexOf('@ali/vc-') === 0 && item.urls) { item.urls = item.urls.filter((url: string) => { - return url.indexOf('view.mobile') < 0 + return url.indexOf('view.mobile') < 0; }); } else if (item.package && externals.indexOf(item.package) > -1) { item.urls = null; diff --git a/packages/vision-polyfill/src/editor.ts b/packages/vision-polyfill/src/editor.ts index ec703e532..3ced493a8 100644 --- a/packages/vision-polyfill/src/editor.ts +++ b/packages/vision-polyfill/src/editor.ts @@ -4,7 +4,7 @@ import { Designer } from '@ali/lowcode-designer'; import { registerSetters } from '@ali/lowcode-setters'; import OutlinePane from '@ali/lowcode-plugin-outline-pane'; import SettingsPane from '@ali/lowcode-plugin-settings-pane'; -import DesignerView from '@ali/lowcode-plugin-designer'; +import DesignerPlugin from '@ali/lowcode-plugin-designer'; import { Skeleton } from './skeleton/skeleton'; registerSetters(); @@ -21,7 +21,7 @@ editor.set(Designer, designer); skeleton.mainArea.add({ name: 'designer', type: 'Widget', - content: DesignerView, + content: DesignerPlugin, }); skeleton.rightArea.add({ name: 'settingsPane', diff --git a/packages/vision-polyfill/src/env.ts b/packages/vision-polyfill/src/env.ts new file mode 100644 index 000000000..bd5ab894a --- /dev/null +++ b/packages/vision-polyfill/src/env.ts @@ -0,0 +1,88 @@ +import { EventEmitter } from 'events'; +import { ALI_SCHEMA_VERSION } from './base/const'; + +interface ILiteralObject { + [key: string]: any; +} + +export class Env { + + public envs: ILiteralObject; + + private emitter: EventEmitter; + private featureMap: ILiteralObject; + + constructor() { + this.emitter = new EventEmitter(); + this.emitter.setMaxListeners(0); + this.envs = {}; + this.featureMap = {}; + } + + public get(name: string): any { + return this.getEnv(name); + } + + public getEnv(name: string): any { + return this.envs[name]; + } + + public set(name: string, value: any) { + return this.setEnv(name, value); + } + + public setEnv(name: string, value: any) { + const orig = this.envs[name]; + if (JSON.stringify(orig) === JSON.stringify(value)) { + return; + } + this.envs[name] = value; + this.emitter.emit('envchange', this.envs, name, value); + } + + public setEnvMap(envs: ILiteralObject): void { + this.envs = Object.assign(this.envs, envs); + this.emitter.emit('envchange', this.envs); + } + + public getLocale(): string { + return this.getEnv('locale') || 'zh_CN'; + } + + public setLocale(locale: string) { + this.setEnv('locale', locale); + } + + public setExpertMode(flag: string) { + this.setEnv('expertMode', !!flag); + } + + public isExpertMode() { + return !!this.getEnv('expertMode'); + } + + public getSupportFeatures() { + return Object.assign({}, this.featureMap); + } + + public setSupportFeatures(features: ILiteralObject) { + this.featureMap = Object.assign({}, this.featureMap, features); + } + + public supports(name = 'supports') { + return !!this.featureMap[name]; + } + + public onEnvChange(func: (envs: ILiteralObject, name: string, value: any) => any) { + this.emitter.on('envchange', func); + return () => { + this.emitter.removeListener('envchange', func); + }; + } + + public getAliSchemaVersion() { + return ALI_SCHEMA_VERSION; + } +} + +export default new Env(); diff --git a/packages/vision-polyfill/src/field.tsx b/packages/vision-polyfill/src/field.tsx new file mode 100644 index 000000000..8ae36fce7 --- /dev/null +++ b/packages/vision-polyfill/src/field.tsx @@ -0,0 +1,17 @@ +import { Component } from "react"; + +export class Placeholder extends Component { + +} + +const Field = { + SettingField: Placeholder, + Stage: Placeholder, + PopupField: Placeholder, + EntryField: Placeholder, + AccordionField: Placeholder, + BlockField: Placeholder, + InlineField: Placeholder +}; + +export default Field; diff --git a/packages/vision-polyfill/src/pages.ts b/packages/vision-polyfill/src/pages.ts new file mode 100644 index 000000000..198ad0db6 --- /dev/null +++ b/packages/vision-polyfill/src/pages.ts @@ -0,0 +1,52 @@ +import { designer } from './editor'; +import { RootSchema } from '@ali/lowcode-globals'; +import { DocumentModel } from '@ali/lowcode-designer'; + +const { project } = designer; + +export interface OldPageData { + id: string; + layout: RootSchema; + [dataAddon: string]: any; +} + +const pages = Object.assign(project, { + setPages(pages: OldPageData[]) { + project.load({ + version: '1.0.0', + componentsMap: [], + componentsTree: pages.map(page => page.layout), + }); + }, + addPage(data: OldPageData) { + return project.open(data.layout); + }, + getPage(fnOrIndex: ((page: DocumentModel) => boolean) | number) { + if (typeof fnOrIndex === 'number') { + return project.documents[fnOrIndex]; + } else if (typeof fnOrIndex === 'function') { + return project.documents.find(fnOrIndex); + } + return null; + }, + removePage(page: DocumentModel) { + page.remove(); + }, + getPages() { + return project.documents; + }, + setCurrentPage(page: DocumentModel) { + page.active(); + }, + getCurrentPage() { + return project.currentDocument; + }, + onPagesChange() { + // noop + }, + onCurrentPageChange(fn: (page: DocumentModel) => void) { + return project.onCurrentDocumentChange(fn); + } +}); + +export default pages; diff --git a/packages/vision-polyfill/src/prop.ts b/packages/vision-polyfill/src/prop.ts new file mode 100644 index 000000000..fb00d47ef --- /dev/null +++ b/packages/vision-polyfill/src/prop.ts @@ -0,0 +1,616 @@ +import { Component } from 'react'; +import { EventEmitter } from 'events'; +import { fromJS, Iterable, Map as IMMap } from 'immutable'; +import logger from '@ali/vu-logger'; +import { uniqueId, cloneDeep, isDataEqual, combineInitial, Transducer } from '@ali/ve-utils'; +import I18nUtil from '@ali/ve-i18n-util'; +import { getSetter } from '@ali/lowcode-globals'; +import { editor } from './editor'; +import { OldPropConfig, DISPLAY_TYPE } from './bundle/upgrade-metadata'; + +type IPropConfig = OldPropConfig; + +// 1: chain -1: start 0: discard +const CHAIN_START = -1; +const CHAIN_HAS_REACH = 0; + +export enum PROP_VALUE_CHANGED_TYPE { + /** + * normal set value + */ + SET_VALUE = 'SET_VALUE', + /** + * value changed caused by sub-prop value change + */ + SUB_VALUE_CHANGE = 'SUB_VALUE_CHANGE', +} + +/** + * Dynamic setter will use 've.plugin.setterProvider' to + * calculate setter type in runtime + */ +let dynamicSetterProvider: any; + +export interface IHotDataMap extends IMMap<string, any> { + value: any; + hotValue: any; +} + +export interface ISetValueOptions { + disableMutator?: boolean; + type?: PROP_VALUE_CHANGED_TYPE; +} + +export interface IVariableSettable { + useVariable?: boolean; + variableValue: string; + isUseVariable: () => boolean; + isSupportVariable: () => boolean; + setVariableValue: (value: string) => void; + setUseVariable: (flag?: boolean) => void; + getVariableValue: () => string; + onUseVariableChange: (func: (data: { isUseVariable: boolean }) => any) => void; +} + +export default class Prop implements IVariableSettable { + + /** + * Setters predefined as default options + * can by selected by user for every prop + * + * @static + * @memberof Prop + */ + public static INSET_SETTER = {}; + + public id: string; + public emitter: EventEmitter; + + public inited: boolean; + public i18nLink: any; + public loopLock: boolean; + + public props: any; + public parent: any; + + public config: IPropConfig; + public initial: any; + public initialData: any; + + public expanded: boolean; + public useVariable?: boolean; + + /** + * value to be saved in schema it is usually JSON serialized + * prototype.js can config Transducer.toNative to generate value + */ + public value: any; + /** + * value to be used in VisualDesigner more flexible + * prototype.js can config Transducer.toHot to generate hotValue + */ + public hotValue: any; + /** + * 启用变量之后,变量表达式字符串值 + */ + public variableValue: string; + public hotData: IMMap<string, IHotDataMap>; + public defaultValue: any; + public transducer: any; + public inGroup: boolean; + + constructor(parent: any, config: IPropConfig, data?: any) { + if (parent.isProps) { + this.props = parent; + this.parent = null; + } else { + this.props = parent.getProps(); + this.parent = parent; + } + + this.id = uniqueId(null as any, 'prop', 'engine-prop'); + + if (typeof config.setter === 'string') { + config.setter = getSetter(config.setter)?.component as any; + } + this.config = config; + this.emitter = new EventEmitter(); + this.emitter.setMaxListeners(100); + this.initialData = data; + this.useVariable = false; + + dynamicSetterProvider = editor.get('ve.plugin.setterProvider'); + + this.beforeInit(); + } + + public getId() { + return this.id; + } + + public isTab() { + return this.getDisplay() === 'tab'; + } + + public isGroup() { + return false; + } + + public beforeInit() { + if (IMMap.isMap(this.initialData)) { + this.value = this.initialData.get('value'); + if (this.value && typeof this.value.toJS === 'function') { + this.value = this.value.toJS(); + } + this.hotData = this.initialData; + } else { + this.value = this.initialData; + } + + this.resolveValue(); + + let defaultValue = null; + if (this.config.defaultValue !== undefined) { + defaultValue = this.config.defaultValue; + } else if (typeof this.config.initialValue !== 'function') { + defaultValue = this.config.initialValue; + } + this.defaultValue = defaultValue; + this.transducer = new Transducer(this, this.config); + this.initial = combineInitial(this, this.config); + } + + public resolveValue() { + if (this.value && this.value.type === 'variable') { + const { value, variable } = this.value; + this.value = value; + this.variableValue = variable; + this.useVariable = this.isSupportVariable(); + } else { + this.useVariable = false; + } + } + + public init(defaultValue?: any) { + if (this.inited) { return; } + + this.value = this.initial(this.value, + this.defaultValue != null ? this.defaultValue : defaultValue); + + if (this.hotData) { + const tempVal = this.hotData.get('value'); + // if we create a prop from runtime data, we don't need initial() or set with defaultValue process + // but if we got an empty value, we fill with the initial() process and default value + if (Iterable.isIterable(tempVal)) { + this.value = tempVal.toJS() || this.value; + } else { + this.value = tempVal || this.value; + } + this.resolveValue(); + } + + this.i18nLink = I18nUtil.attach(this, this.value, + ((val: any) => { this.setValue(val, false, true); }) as any); + + // call config.accessor + const value = this.getValue(); + + if (this.hotData) { + this.hotValue = this.hotData.get('hotValue'); + if (this.hotValue && Iterable.isIterable(this.hotValue)) { + this.hotValue = this.hotValue.toJS(); + } + } else { + try { + this.hotValue = this.transducer.toHot(value); + } catch (e) { + logger.log('ERROR_PROP_VALUE'); + logger.warn('属性初始化错误:', this); + } + + this.hotData = fromJS({ + hotValue: this.hotValue, + value: this.getMixValue(value), + }); + } + this.inited = true; + } + + public isInited() { + return this.inited; + } + + public getHotData() { + return this.hotData; + } + + public getProps() { + return this.props; + } + + public getNode(): any { + return this.getProps().getNode(); + } + + /** + * 获得属性名称 + * + * @returns {string} + */ + public getName(): string { + const ns = this.parent ? `${this.parent.getName()}.` : ''; + return ns + this.config.name; + } + + public getKey() { + return this.config.name; + } + + /** + * 获得属性标题 + * + * @returns {string} + */ + public getTitle() { + return this.config.title || this.getName(); + } + + public getTip() { + return this.config.tip || null; + } + + public getValue(disableCache?: boolean, options?: { + disableAccessor?: boolean; + }) { + const accessor = this.config.accessor; + if (accessor && (!options || !options.disableAccessor)) { + const value = accessor.call(this, this.value); + if (!disableCache) { + this.value = value; + } + return value; + } + return this.value; + } + + public getMixValue(value?: any) { + if (value == null) { + value = this.getValue(); + } + if (this.isUseVariable()) { + value = { + type: 'variable', + value, + variable: this.getVariableValue(), + }; + } + return value; + } + + public toData() { + return cloneDeep(this.getMixValue()); + } + + public getDefaultValue() { + return this.defaultValue; + } + + public getHotValue() { + return this.hotValue; + } + + public getConfig<K extends keyof IPropConfig>(configName?: K): IPropConfig[K] | IPropConfig { + if (configName) { + return this.config[configName]; + } + + return this.config; + } + + public sync() { + if (this.props.hasReach(this)) { + return; + } + + const sync = this.config.sync; + if (sync) { + const value = sync.call(this, this.getValue(true)); + if (value !== undefined) { + this.setValue(value); + } + } else { + // sync 的时候不再需要调用经过 accessor 处理之后的值了 + // 这里之所以需要 setValue 是为了过 getValue() 中的 accessor 修饰函数 + this.setValue(this.getValue(true), false, false, { + disableMutator: true, + }); + } + } + + public isUseVariable() { + return this.useVariable || false; + } + + public isSupportVariable() { + return this.config.supportVariable || false; + } + + public setVariableValue(value: string) { + if (!this.isUseVariable()) { return; } + + const state = this.props.chainReach(this); + if (state === CHAIN_HAS_REACH) { + return; + } + + this.variableValue = value; + + if (this.modify()) { + this.valueChange(); + this.props.syncPass(this); + } + + if (state === CHAIN_START) { + this.props.endChain(); + } + } + + public setUseVariable(flag: boolean = false) { + if (this.useVariable === flag) { return; } + + const state = this.props.chainReach(this); + if (state === CHAIN_HAS_REACH) { + return; + } + + this.useVariable = flag; + this.expanded = true; + + if (this.modify()) { + this.valueChange(); + this.props.syncPass(this); + } + + if (state === CHAIN_START) { + this.props.endChain(); + } + + this.emitter.emit('ve.prop.useVariableChange', { isUseVariable: flag }); + if (this.config.useVariableChange) { + this.config.useVariableChange.call(this, { isUseVariable: flag }); + } + } + + public getVariableValue() { + return this.variableValue; + } + + /** + * @param value + * @param isHotValue 是否为设计器热状态值 + * @param force 是否强制触发更新 + */ + public setValue(value: any, isHotValue?: boolean, force?: boolean, extraOptions?: ISetValueOptions) { + const state = this.props.chainReach(this); + if (state === CHAIN_HAS_REACH) { + return; + } + + const preValue = this.value; + const preHotValue = this.hotValue; + + if (isHotValue) { + this.hotValue = value; + this.value = this.transducer.toNative(this.hotValue); + } else { + if (!isDataEqual(value, this.value)) { + this.hotValue = this.transducer.toHot(value); + } + this.value = value; + } + + this.i18nLink = I18nUtil.attach(this, this.value, ((val: any) => this.setValue(val, false, true)) as any); + + const mutator = this.config.mutator; + + if (!extraOptions) { + extraOptions = {}; + } + + if (mutator && !extraOptions.disableMutator) { + mutator.call(this, this.value); + } + + if (this.modify(force)) { + this.valueChange(extraOptions); + this.props.syncPass(this); + } + + if (state === CHAIN_START) { + this.props.endChain(); + } + } + + public setHotValue(hotValue: any, options?: ISetValueOptions) { + try { + this.setValue(hotValue, true, false, options); + } catch (e) { + logger.log('ERROR_PROP_VALUE'); + logger.warn('属性值设置错误:', e, hotValue); + } + } + + /** + * 验证是否存在变更 + * @param force 是否强制返回已变更 + */ + public modify(force?: boolean) { + const hotData = this.hotData.merge(fromJS({ + hotValue: this.getHotValue(), + value: this.getMixValue(), + })); + + if (!force && hotData.equals(this.hotData)) { + return false; + } + + this.hotData = hotData; + + (this.parent || this.props).modify(this.getName()); + + return true; + } + + public setHotData(hotData: IMMap<string, IHotDataMap>, options?: ISetValueOptions) { + if (!IMMap.isMap(hotData)) { + return; + } + this.hotData = hotData; + let value = hotData.get('value'); + if (value && typeof value.toJS === 'function') { + value = value.toJS(); + } + let hotValue = hotData.get('hotValue'); + if (hotValue && typeof hotValue.toJS === 'function') { + hotValue = hotValue.toJS(); + } + + const preValue = value; + const preHotValue = hotValue; + + this.value = value; + this.hotValue = hotValue; + this.resolveValue(); + + if (!options || !options.disableMutator) { + const mutator = this.config.mutator; + if (mutator) { + mutator.call(this, value); + } + } + + this.valueChange(); + } + + public valueChange(options?: ISetValueOptions) { + if (this.loopLock) { return; } + + this.emitter.emit('valuechange', options); + if (this.parent) { + this.parent.valueChange(options); + } + } + + public getDisplay() { + return this.config.display || this.config.fieldStyle || 'block'; + } + + public isHidden() { + if (!this.isInited() || this.getDisplay() === DISPLAY_TYPE.NONE || this.isDisabled()) { + return true; + } + + let hidden = this.config.hidden; + if (typeof hidden === 'function') { + hidden = hidden.call(this, this.getValue()); + } + return hidden === true; + } + + public isDisabled() { + let disabled = this.config.disabled; + if (typeof disabled === 'function') { + disabled = disabled.call(this, this.getValue()); + } + return disabled === true; + } + + public isIgnore() { + if (this.isDisabled()) { return true; } + + let ignore = this.config.ignore; + if (typeof ignore === 'function') { + ignore = ignore.call(this, this.getValue()); + } + return ignore === true; + } + + public isExpand() { + if (this.expanded == null) { + this.expanded = !(this.config.collapsed || this.config.fieldCollapsed); + } + return this.expanded; + } + + public toggleExpand() { + if (this.expanded) { + this.expanded = false; + } else { + this.expanded = true; + } + this.emitter.emit('expandchange', this.expanded); + } + + public getSetter() { + if (dynamicSetterProvider) { + const setter = dynamicSetterProvider.call(this, this, this.getNode().getPrototype()); + if (setter) { + return setter; + } + } + const setterConfig = this.config.setter; + if (typeof setterConfig === 'function' && !(setterConfig.prototype instanceof Component)) { + return (setterConfig as any).call(this, this.getValue()); + } + if (Array.isArray(setterConfig)) { + let item; + for (item of setterConfig) { + if (item.condition?.call(this, this.getValue())) { + return item.setter; + } + } + return setterConfig[0].setter; + } + return setterConfig; + } + + public getSetterData(): any { + if (Array.isArray(this.config.setter)) { + let item; + for (item of this.config.setter) { + if (item.condition?.call(this, this.getValue())) { + return item; + } + } + return this.config.setter[0]; + } + return { }; + } + + public destroy() { + if (this.i18nLink) { + this.i18nLink.detach(); + } + } + + public onValueChange(func: () => any) { + this.emitter.on('valuechange', func); + return () => { + this.emitter.removeListener('valuechange', func); + }; + } + + public onExpandChange(func: () => any) { + this.emitter.on('expandchange', func); + return () => { + this.emitter.removeListener('expandchange', func); + }; + } + + public onUseVariableChange(func: (data: { isUseVariable: boolean }) => any) { + this.emitter.on('ve.prop.useVariableChange', func); + return () => { + this.emitter.removeListener('ve.prop.useVariableChange', func); + }; + } +} diff --git a/packages/vision-polyfill/src/vision.less b/packages/vision-polyfill/src/vision.less new file mode 100644 index 000000000..495668ce8 --- /dev/null +++ b/packages/vision-polyfill/src/vision.less @@ -0,0 +1,47 @@ +html.engine-cursor-move, html.engine-cursor-move * { + cursor: grabbing !important +} + +html.engine-cursor-copy, html.engine-cursor-copy * { + cursor: copy !important +} + +html.engine-cursor-ew-resize, html.engine-cursor-ew-resize * { + cursor: ew-resize !important +} + +body, #engine { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + box-sizing: border-box; + padding: 0; + margin: 0; + overflow: hidden; + text-rendering: optimizeLegibility; + -webkit-user-select: none; + -webkit-user-drag: none; + -webkit-text-size-adjust: none; + -webkit-touch-callout: none; + -webkit-font-smoothing: antialiased; +} + +html { + min-width: 1024px; +} + +::-webkit-scrollbar { + width: 5px; + height: 5px; +} + +::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.3); + border-radius: 5px; +} + +html.engine-blur #engine { + -webkit-filter: blur(4px); +} diff --git a/packages/vision-polyfill/src/vision.ts b/packages/vision-polyfill/src/vision.ts index 858696f7d..c7b58cd63 100644 --- a/packages/vision-polyfill/src/vision.ts +++ b/packages/vision-polyfill/src/vision.ts @@ -2,11 +2,12 @@ import * as utils from '@ali/ve-utils'; import Popup from '@ali/ve-popups'; import Icons from '@ali/ve-icons'; import { render } from 'react-dom'; +import I18nUtil from '@ali/ve-i18n-util'; import { createElement } from 'react'; import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS } from './const'; import Bus from './bus'; import Symbols from './symbols'; -import { skeleton } from './editor'; +import { skeleton, editor } from './editor'; import { VisionWorkbench } from './skeleton/workbench'; import Panes from './panes'; import Exchange from './exchange'; @@ -15,6 +16,11 @@ import VisualManager from './base/visualManager'; import Trunk from './bundle/trunk'; import Prototype from './bundle/prototype'; import Bundle from './bundle/bundle'; +import Pages from './pages'; +import Field from './field'; +import Prop from './prop'; +import Env from './env'; +import './vision.less'; function init(container?: Element) { if (!container) { @@ -31,7 +37,13 @@ function init(container?: Element) { ); } +/** + * VE.ui.xxx + * + * Core UI Components + */ const ui = { + Field, Icon: Icons, Icons, Popup, @@ -39,11 +51,14 @@ const ui = { const modules = { VisualManager, + I18nUtil, + Prop, }; const context = new VisualEngineContext(); const VisualEngine = { + editor, /** * VE.Popup */ @@ -52,6 +67,8 @@ const VisualEngine = { * VE Utils */ utils, + I18nUtil, + Env, /* pub/sub 集线器 */ Bus, /* 事件 */ @@ -74,11 +91,13 @@ const VisualEngine = { Trunk, Prototype, Bundle, + Pages, }; export default VisualEngine; (window as any).VisualEngine = VisualEngine; + /* console.log( `%cLowcodeEngine %cv${VERSION}`,