mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-04-19 20:08:05 +00:00
Merge branch 'feat/universal-api' into release/1.0.30
This commit is contained in:
commit
373f8c3b2e
21
packages/engine/.eslintrc.js
Normal file
21
packages/engine/.eslintrc.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: 'eslint-config-ali/typescript/react',
|
||||||
|
rules: {
|
||||||
|
'react/no-multi-comp': 1,
|
||||||
|
'no-unused-expressions': 0,
|
||||||
|
'implicit-arrow-linebreak': 1,
|
||||||
|
'no-nested-ternary': 1,
|
||||||
|
'no-mixed-operators': 1,
|
||||||
|
'@typescript-eslint/no-parameter-properties': 1,
|
||||||
|
'@typescript-eslint/ban-types': 1,
|
||||||
|
'no-shadow': 1,
|
||||||
|
'no-prototype-builtins': 1,
|
||||||
|
'@typescript-eslint/no-unused-vars': 1,
|
||||||
|
'no-multi-assign': 1,
|
||||||
|
'no-dupe-class-members': 1,
|
||||||
|
'react/no-deprecated': 1,
|
||||||
|
'no-useless-escape': 1,
|
||||||
|
'brace-style': 1,
|
||||||
|
'@typescript-eslint/member-ordering': 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
82
packages/engine/README.md
Normal file
82
packages/engine/README.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
子视图
|
||||||
|
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.<device>.xxx
|
||||||
|
通配 view.xxx
|
||||||
|
|
||||||
|
universal
|
||||||
|
|
||||||
|
规则 2
|
||||||
|
urls: "view.js,view2 <device selector>, view3 <device selector>",
|
||||||
|
urls: [
|
||||||
|
"view.js",
|
||||||
|
"view.js *",
|
||||||
|
"view1.js mobile|pc",
|
||||||
|
"view2.js <device selector>"
|
||||||
|
]
|
||||||
|
|
||||||
|
环境通用资源
|
||||||
|
|
||||||
|
"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 构建组件查询字典,
|
||||||
|
|
||||||
|
|
||||||
|
获取 view 相关的样式、脚本
|
||||||
|
获取 proto 相关的样式
|
||||||
|
在 simulator 中也加载一次
|
||||||
|
|
||||||
|
1. meta 信息构造
|
||||||
|
2. components 字典构造, proto.getView 或者 通过 npm 信息查询
|
||||||
|
3.
|
||||||
|
|
||||||
|
|
||||||
|
componentMeta 段描述的信息,如果包含 x-prototype-urls ,那么这个 meta 信息都可以丢掉
|
||||||
28
packages/engine/build.json
Normal file
28
packages/engine/build.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"sourceMap": true,
|
||||||
|
"plugins": [
|
||||||
|
[
|
||||||
|
"build-plugin-component",
|
||||||
|
{
|
||||||
|
"filename": "engine",
|
||||||
|
"library": "AliLowCodeEngine",
|
||||||
|
"libraryTarget": "umd",
|
||||||
|
"externals": {
|
||||||
|
"react": "var window.React",
|
||||||
|
"react-dom": "var window.ReactDOM",
|
||||||
|
"prop-types": "var window.PropTypes",
|
||||||
|
"@ali/visualengine": "var window.VisualEngine",
|
||||||
|
"@ali/visualengine-utils": "var window.VisualEngineUtils",
|
||||||
|
"rax": "var window.Rax",
|
||||||
|
"monaco-editor/esm/vs/editor/editor.api": "var window.monaco",
|
||||||
|
"monaco-editor/esm/vs/editor/editor.main.js": "var window.monaco"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"build-plugin-fusion",
|
||||||
|
["build-plugin-moment-locales", {
|
||||||
|
"locales": ["zh-cn"]
|
||||||
|
}],
|
||||||
|
"./build.plugin.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
23
packages/engine/build.plugin.js
Normal file
23
packages/engine/build.plugin.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = ({ onGetWebpackConfig }) => {
|
||||||
|
onGetWebpackConfig((config) => {
|
||||||
|
config.resolve
|
||||||
|
.plugin('tsconfigpaths')
|
||||||
|
.use(TsconfigPathsPlugin, [{
|
||||||
|
configFile: './tsconfig.json',
|
||||||
|
}]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
config
|
||||||
|
// 定义插件名称
|
||||||
|
.plugin('MonacoWebpackPlugin')
|
||||||
|
// 第一项为具体插件,第二项为插件参数
|
||||||
|
.use(new MonacoWebpackPlugin({
|
||||||
|
languages:["javascript","css","json"]
|
||||||
|
}), []);
|
||||||
|
*/
|
||||||
|
config.plugins.delete('hot');
|
||||||
|
config.devServer.hot(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
19
packages/engine/build.test.json
Normal file
19
packages/engine/build.test.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
[
|
||||||
|
"build-plugin-component",
|
||||||
|
{
|
||||||
|
"filename": "editor-preset-vision",
|
||||||
|
"library": "LowcodeEditor",
|
||||||
|
"libraryTarget": "umd",
|
||||||
|
"externals": {
|
||||||
|
"react": "var window.React",
|
||||||
|
"react-dom": "var window.ReactDOM",
|
||||||
|
"prop-types": "var window.PropTypes",
|
||||||
|
"rax": "var window.Rax"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@ali/lowcode-test-mate/plugin/index.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
27
packages/engine/jest.config.js
Normal file
27
packages/engine/jest.config.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const esModules = [
|
||||||
|
'@recore/obx-react',
|
||||||
|
// '@ali/lowcode-editor-core',
|
||||||
|
].join('|');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
// transform: {
|
||||||
|
// '^.+\\.[jt]sx?$': 'babel-jest',
|
||||||
|
// // '^.+\\.(ts|tsx)$': 'ts-jest',
|
||||||
|
// // '^.+\\.(js|jsx)$': 'babel-jest',
|
||||||
|
// },
|
||||||
|
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
|
||||||
|
transformIgnorePatterns: [
|
||||||
|
`/node_modules/(?!${esModules})/`,
|
||||||
|
],
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||||
|
collectCoverage: false,
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'src/**/*.{ts,tsx}',
|
||||||
|
'!src/**/*.d.ts',
|
||||||
|
'!src/base/**',
|
||||||
|
'!src/fields/**',
|
||||||
|
'!src/prop.ts',
|
||||||
|
'!**/node_modules/**',
|
||||||
|
'!**/vendor/**',
|
||||||
|
],
|
||||||
|
};
|
||||||
60
packages/engine/package.json
Normal file
60
packages/engine/package.json
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"name": "@ali/lowcode-engine",
|
||||||
|
"version": "1.0.29",
|
||||||
|
"description": "Universal API for AliLowCode engine",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"private": true,
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"es",
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"start": "build-scripts start",
|
||||||
|
"version:update": "node ./scripts/version.js",
|
||||||
|
"build": "tnpm run version:update && build-scripts build --skip-demo",
|
||||||
|
"cloud-build": "build-scripts build --skip-demo",
|
||||||
|
"test": "build-scripts test --config build.test.json"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@ali/lowcode-designer": "^1.0.29",
|
||||||
|
"@ali/lowcode-editor-core": "^1.0.29",
|
||||||
|
"@ali/lowcode-editor-skeleton": "^1.0.29",
|
||||||
|
"@ali/lowcode-plugin-designer": "^1.0.29",
|
||||||
|
"@ali/lowcode-plugin-outline-pane": "^1.0.29",
|
||||||
|
"@ali/lowcode-utils": "^1.0.29",
|
||||||
|
"@ali/ve-i18n-util": "^2.0.0",
|
||||||
|
"@ali/ve-icons": "^4.1.9",
|
||||||
|
"@ali/ve-less-variables": "2.0.3",
|
||||||
|
"@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",
|
||||||
|
"@alifd/next": "^1.19.12",
|
||||||
|
"@alife/theme-lowcode-dark": "^0.1.0",
|
||||||
|
"@alife/theme-lowcode-light": "^0.1.0",
|
||||||
|
"domready": "^1.0.8",
|
||||||
|
"immutable": "^3.8.1",
|
||||||
|
"react": "^16.8.1",
|
||||||
|
"react-dom": "^16.8.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@ali/lowcode-test-mate": "^1.0.1",
|
||||||
|
"@alib/build-scripts": "^0.1.18",
|
||||||
|
"@types/domready": "^1.0.0",
|
||||||
|
"@types/events": "^3.0.0",
|
||||||
|
"@types/react": "^16.8.3",
|
||||||
|
"@types/react-dom": "^16.8.2",
|
||||||
|
"build-plugin-fusion": "^0.1.0",
|
||||||
|
"build-plugin-moment-locales": "^0.1.0",
|
||||||
|
"build-plugin-react-app": "^1.1.2",
|
||||||
|
"fs-extra": "^9.0.1",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"tsconfig-paths-webpack-plugin": "^3.2.0"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://registry.npm.alibaba-inc.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
packages/engine/scripts/version.js
Normal file
22
packages/engine/scripts/version.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
const { join } = require('path');
|
||||||
|
const fse = require('fs-extra');
|
||||||
|
|
||||||
|
const gitBranchName = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' });
|
||||||
|
const reBranchVersion = /^(?:[a-z]+\/)(\d+\.\d+\.\d+)$/im;
|
||||||
|
|
||||||
|
const match = reBranchVersion.exec(gitBranchName);
|
||||||
|
if (!match) {
|
||||||
|
console.warn(`[checkversion] gitBranchName: ${gitBranchName}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const releaseVersion = match[1];
|
||||||
|
|
||||||
|
const indexFile = join(__dirname, '../src/index.ts');
|
||||||
|
|
||||||
|
const indexContent = fse.readFileSync(indexFile, 'utf-8');
|
||||||
|
|
||||||
|
fse.writeFileSync(indexFile, indexContent.replace('{VERSION}', releaseVersion));
|
||||||
60
packages/engine/src/editor.ts
Normal file
60
packages/engine/src/editor.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { isJSBlock, isJSExpression, isJSSlot } from '@ali/lowcode-types';
|
||||||
|
import { isPlainObject, hasOwnProperty, cloneDeep, isI18NObject, isUseI18NSetter, convertToI18NObject, isString } from '@ali/lowcode-utils';
|
||||||
|
import { globalContext, Editor } from '@ali/lowcode-editor-core';
|
||||||
|
import { Designer, LiveEditing, TransformStage, Node, getConvertedExtraKey, LowCodePluginManager } from '@ali/lowcode-designer';
|
||||||
|
import Outline, { OutlineBackupPane, getTreeMaster } from '@ali/lowcode-plugin-outline-pane';
|
||||||
|
|
||||||
|
import DesignerPlugin from '@ali/lowcode-plugin-designer';
|
||||||
|
import { Skeleton, SettingsPrimaryPane, registerDefaults } from '@ali/lowcode-editor-skeleton';
|
||||||
|
|
||||||
|
export const editor = new Editor();
|
||||||
|
globalContext.register(editor, Editor);
|
||||||
|
|
||||||
|
export const skeleton = new Skeleton(editor);
|
||||||
|
editor.set(Skeleton, skeleton);
|
||||||
|
editor.set('skeleton', skeleton);
|
||||||
|
registerDefaults();
|
||||||
|
|
||||||
|
export const designer = new Designer({ editor });
|
||||||
|
editor.set(Designer, designer);
|
||||||
|
editor.set('designer', designer);
|
||||||
|
|
||||||
|
export const plugins = (new LowCodePluginManager(editor)).toProxy();
|
||||||
|
editor.set('plugins', plugins);
|
||||||
|
|
||||||
|
|
||||||
|
skeleton.add({
|
||||||
|
area: 'mainArea',
|
||||||
|
name: 'designer',
|
||||||
|
type: 'Widget',
|
||||||
|
content: DesignerPlugin,
|
||||||
|
});
|
||||||
|
skeleton.add({
|
||||||
|
area: 'rightArea',
|
||||||
|
name: 'settingsPane',
|
||||||
|
type: 'Panel',
|
||||||
|
content: SettingsPrimaryPane,
|
||||||
|
props: {
|
||||||
|
ignoreRoot: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
skeleton.add({
|
||||||
|
area: 'leftArea',
|
||||||
|
name: 'outlinePane',
|
||||||
|
type: 'PanelDock',
|
||||||
|
content: Outline,
|
||||||
|
panelProps: {
|
||||||
|
area: 'leftFixedArea',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
skeleton.add({
|
||||||
|
area: 'rightArea',
|
||||||
|
name: 'backupOutline',
|
||||||
|
type: 'Panel',
|
||||||
|
props: {
|
||||||
|
condition: () => {
|
||||||
|
return designer.dragon.dragging && !getTreeMaster(designer).hasVisibleTreeBoard();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
content: OutlineBackupPane,
|
||||||
|
});
|
||||||
90
packages/engine/src/index.ts
Normal file
90
packages/engine/src/index.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { createElement } from 'react';
|
||||||
|
import { render } from 'react-dom';
|
||||||
|
import * as editorHelper from '@ali/lowcode-editor-core';
|
||||||
|
import * as designerHelper from '@ali/lowcode-designer';
|
||||||
|
// import { Node } from '@ali/lowcode-designer';
|
||||||
|
import { skeleton, designer, editor, plugins } from './editor';
|
||||||
|
import * as skeletonHelper from '@ali/lowcode-editor-skeleton';
|
||||||
|
|
||||||
|
const { project, currentSelection: selection } = designer;
|
||||||
|
const { hotkey, monitor, getSetter, registerSetter } = editorHelper;
|
||||||
|
const { Workbench } = skeletonHelper;
|
||||||
|
const setters = {
|
||||||
|
getSetter,
|
||||||
|
registerSetter,
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
editor,
|
||||||
|
editorHelper,
|
||||||
|
skeleton,
|
||||||
|
skeletonHelper,
|
||||||
|
designer,
|
||||||
|
designerHelper,
|
||||||
|
plugins,
|
||||||
|
setters,
|
||||||
|
project,
|
||||||
|
selection,
|
||||||
|
/**
|
||||||
|
* 注册一些全局的切面
|
||||||
|
*/
|
||||||
|
// hooks,
|
||||||
|
/**
|
||||||
|
* 全局的一些数据存储
|
||||||
|
*/
|
||||||
|
// store,
|
||||||
|
hotkey,
|
||||||
|
monitor,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: build-plugin-component 的 umd 开发态没有导出 AliLowCodeEngine,这里先简单绕过
|
||||||
|
(window as any).AliLowCodeEngine = {
|
||||||
|
editor,
|
||||||
|
editorHelper,
|
||||||
|
skeleton,
|
||||||
|
skeletonHelper,
|
||||||
|
designer,
|
||||||
|
designerHelper,
|
||||||
|
plugins,
|
||||||
|
setters,
|
||||||
|
project,
|
||||||
|
selection,
|
||||||
|
/**
|
||||||
|
* 注册一些全局的切面
|
||||||
|
*/
|
||||||
|
// hooks,
|
||||||
|
/**
|
||||||
|
* 全局的一些数据存储
|
||||||
|
*/
|
||||||
|
// store,
|
||||||
|
hotkey,
|
||||||
|
monitor,
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function init(container?: Element) {
|
||||||
|
let engineContainer = container;
|
||||||
|
if (!engineContainer) {
|
||||||
|
engineContainer = document.createElement('div');
|
||||||
|
document.body.appendChild(engineContainer);
|
||||||
|
}
|
||||||
|
engineContainer.id = 'engine';
|
||||||
|
|
||||||
|
await plugins.init();
|
||||||
|
render(
|
||||||
|
createElement(Workbench, {
|
||||||
|
skeleton,
|
||||||
|
className: 'engine-main',
|
||||||
|
topAreaItemClassName: 'engine-actionitem',
|
||||||
|
}),
|
||||||
|
engineContainer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const version = '{VERSION}';
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`%c AliLowCodeEngine %c v${version} `,
|
||||||
|
'padding: 2px 1px; border-radius: 3px 0 0 3px; color: #fff; background: #606060; font-weight: bold;',
|
||||||
|
'padding: 2px 1px; border-radius: 0 3px 3px 0; color: #fff; background: #42c02e; font-weight: bold;',
|
||||||
|
);
|
||||||
|
|
||||||
9
packages/engine/tsconfig.json
Normal file
9
packages/engine/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./src/"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"entry": {
|
"entry": {
|
||||||
"editor-preset-vision": "../editor-preset-vision/src/index.ts",
|
"editor-preset-vision": "../editor-preset-vision/src/index.ts",
|
||||||
|
"engine": "../engine/src/index.ts",
|
||||||
|
"vision-polyfill": "../vision-polyfill/src/index.ts",
|
||||||
"react-simulator-renderer": "../react-simulator-renderer/src/index.ts",
|
"react-simulator-renderer": "../react-simulator-renderer/src/index.ts",
|
||||||
"rax-simulator-renderer": "../rax-simulator-renderer/src/index.ts"
|
"rax-simulator-renderer": "../rax-simulator-renderer/src/index.ts"
|
||||||
},
|
},
|
||||||
|
|||||||
21
packages/vision-polyfill/.eslintrc.js
Normal file
21
packages/vision-polyfill/.eslintrc.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
module.exports = {
|
||||||
|
extends: 'eslint-config-ali/typescript/react',
|
||||||
|
rules: {
|
||||||
|
'react/no-multi-comp': 1,
|
||||||
|
'no-unused-expressions': 0,
|
||||||
|
'implicit-arrow-linebreak': 1,
|
||||||
|
'no-nested-ternary': 1,
|
||||||
|
'no-mixed-operators': 1,
|
||||||
|
'@typescript-eslint/no-parameter-properties': 1,
|
||||||
|
'@typescript-eslint/ban-types': 1,
|
||||||
|
'no-shadow': 1,
|
||||||
|
'no-prototype-builtins': 1,
|
||||||
|
'@typescript-eslint/no-unused-vars': 1,
|
||||||
|
'no-multi-assign': 1,
|
||||||
|
'no-dupe-class-members': 1,
|
||||||
|
'react/no-deprecated': 1,
|
||||||
|
'no-useless-escape': 1,
|
||||||
|
'brace-style': 1,
|
||||||
|
'@typescript-eslint/member-ordering': 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
1146
packages/vision-polyfill/CHANGELOG.md
Normal file
1146
packages/vision-polyfill/CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
82
packages/vision-polyfill/README.md
Normal file
82
packages/vision-polyfill/README.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
子视图
|
||||||
|
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.<device>.xxx
|
||||||
|
通配 view.xxx
|
||||||
|
|
||||||
|
universal
|
||||||
|
|
||||||
|
规则 2
|
||||||
|
urls: "view.js,view2 <device selector>, view3 <device selector>",
|
||||||
|
urls: [
|
||||||
|
"view.js",
|
||||||
|
"view.js *",
|
||||||
|
"view1.js mobile|pc",
|
||||||
|
"view2.js <device selector>"
|
||||||
|
]
|
||||||
|
|
||||||
|
环境通用资源
|
||||||
|
|
||||||
|
"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 构建组件查询字典,
|
||||||
|
|
||||||
|
|
||||||
|
获取 view 相关的样式、脚本
|
||||||
|
获取 proto 相关的样式
|
||||||
|
在 simulator 中也加载一次
|
||||||
|
|
||||||
|
1. meta 信息构造
|
||||||
|
2. components 字典构造, proto.getView 或者 通过 npm 信息查询
|
||||||
|
3.
|
||||||
|
|
||||||
|
|
||||||
|
componentMeta 段描述的信息,如果包含 x-prototype-urls ,那么这个 meta 信息都可以丢掉
|
||||||
29
packages/vision-polyfill/build.json
Normal file
29
packages/vision-polyfill/build.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"sourceMap": true,
|
||||||
|
"plugins": [
|
||||||
|
[
|
||||||
|
"build-plugin-component",
|
||||||
|
{
|
||||||
|
"filename": "vision-polyfill",
|
||||||
|
"library": "VisualEngine",
|
||||||
|
"libraryTarget": "umd",
|
||||||
|
"externals": {
|
||||||
|
"react": "var window.React",
|
||||||
|
"react-dom": "var window.ReactDOM",
|
||||||
|
"prop-types": "var window.PropTypes",
|
||||||
|
"@ali/visualengine": "var window.VisualEngine",
|
||||||
|
"@ali/lowcode-engine": "var window.AliLowCodeEngine",
|
||||||
|
"@ali/visualengine-utils": "var window.VisualEngineUtils",
|
||||||
|
"rax": "var window.Rax",
|
||||||
|
"monaco-editor/esm/vs/editor/editor.api": "var window.monaco",
|
||||||
|
"monaco-editor/esm/vs/editor/editor.main.js": "var window.monaco"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"build-plugin-fusion",
|
||||||
|
["build-plugin-moment-locales", {
|
||||||
|
"locales": ["zh-cn"]
|
||||||
|
}],
|
||||||
|
"./build.plugin.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
23
packages/vision-polyfill/build.plugin.js
Normal file
23
packages/vision-polyfill/build.plugin.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = ({ onGetWebpackConfig }) => {
|
||||||
|
onGetWebpackConfig((config) => {
|
||||||
|
config.resolve
|
||||||
|
.plugin('tsconfigpaths')
|
||||||
|
.use(TsconfigPathsPlugin, [{
|
||||||
|
configFile: './tsconfig.json',
|
||||||
|
}]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
config
|
||||||
|
// 定义插件名称
|
||||||
|
.plugin('MonacoWebpackPlugin')
|
||||||
|
// 第一项为具体插件,第二项为插件参数
|
||||||
|
.use(new MonacoWebpackPlugin({
|
||||||
|
languages:["javascript","css","json"]
|
||||||
|
}), []);
|
||||||
|
*/
|
||||||
|
config.plugins.delete('hot');
|
||||||
|
config.devServer.hot(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
19
packages/vision-polyfill/build.test.json
Normal file
19
packages/vision-polyfill/build.test.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
[
|
||||||
|
"build-plugin-component",
|
||||||
|
{
|
||||||
|
"filename": "editor-preset-vision",
|
||||||
|
"library": "LowcodeEditor",
|
||||||
|
"libraryTarget": "umd",
|
||||||
|
"externals": {
|
||||||
|
"react": "var window.React",
|
||||||
|
"react-dom": "var window.ReactDOM",
|
||||||
|
"prop-types": "var window.PropTypes",
|
||||||
|
"rax": "var window.Rax"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@ali/lowcode-test-mate/plugin/index.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
27
packages/vision-polyfill/jest.config.js
Normal file
27
packages/vision-polyfill/jest.config.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
const esModules = [
|
||||||
|
'@recore/obx-react',
|
||||||
|
// '@ali/lowcode-editor-core',
|
||||||
|
].join('|');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
// transform: {
|
||||||
|
// '^.+\\.[jt]sx?$': 'babel-jest',
|
||||||
|
// // '^.+\\.(ts|tsx)$': 'ts-jest',
|
||||||
|
// // '^.+\\.(js|jsx)$': 'babel-jest',
|
||||||
|
// },
|
||||||
|
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
|
||||||
|
transformIgnorePatterns: [
|
||||||
|
`/node_modules/(?!${esModules})/`,
|
||||||
|
],
|
||||||
|
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
|
||||||
|
collectCoverage: false,
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'src/**/*.{ts,tsx}',
|
||||||
|
'!src/**/*.d.ts',
|
||||||
|
'!src/base/**',
|
||||||
|
'!src/fields/**',
|
||||||
|
'!src/prop.ts',
|
||||||
|
'!**/node_modules/**',
|
||||||
|
'!**/vendor/**',
|
||||||
|
],
|
||||||
|
};
|
||||||
61
packages/vision-polyfill/package.json
Normal file
61
packages/vision-polyfill/package.json
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"name": "@ali/lowcode-editor-preset-vision",
|
||||||
|
"version": "1.0.29",
|
||||||
|
"description": "Vision Polyfill for Ali lowCode engine",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"private": true,
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"es",
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"start": "build-scripts start",
|
||||||
|
"version:update": "node ./scripts/version.js",
|
||||||
|
"build": "tnpm run version:update && build-scripts build --skip-demo",
|
||||||
|
"cloud-build": "build-scripts build --skip-demo",
|
||||||
|
"test": "build-scripts test --config build.test.json"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@ali/lowcode-designer": "^1.0.29",
|
||||||
|
"@ali/lowcode-editor-core": "^1.0.29",
|
||||||
|
"@ali/lowcode-editor-setters": "^1.0.22",
|
||||||
|
"@ali/lowcode-editor-skeleton": "^1.0.29",
|
||||||
|
"@ali/lowcode-plugin-designer": "^1.0.29",
|
||||||
|
"@ali/lowcode-plugin-outline-pane": "^1.0.29",
|
||||||
|
"@ali/lowcode-utils": "^1.0.29",
|
||||||
|
"@ali/ve-i18n-util": "^2.0.0",
|
||||||
|
"@ali/ve-icons": "^4.1.9",
|
||||||
|
"@ali/ve-less-variables": "2.0.3",
|
||||||
|
"@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",
|
||||||
|
"@alifd/next": "^1.19.12",
|
||||||
|
"@alife/theme-lowcode-dark": "^0.1.0",
|
||||||
|
"@alife/theme-lowcode-light": "^0.1.0",
|
||||||
|
"domready": "^1.0.8",
|
||||||
|
"immutable": "^3.8.1",
|
||||||
|
"react": "^16.8.1",
|
||||||
|
"react-dom": "^16.8.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@ali/lowcode-test-mate": "^1.0.1",
|
||||||
|
"@alib/build-scripts": "^0.1.18",
|
||||||
|
"@types/domready": "^1.0.0",
|
||||||
|
"@types/events": "^3.0.0",
|
||||||
|
"@types/react": "^16.8.3",
|
||||||
|
"@types/react-dom": "^16.8.2",
|
||||||
|
"build-plugin-fusion": "^0.1.0",
|
||||||
|
"build-plugin-moment-locales": "^0.1.0",
|
||||||
|
"build-plugin-react-app": "^1.1.2",
|
||||||
|
"fs-extra": "^9.0.1",
|
||||||
|
"prop-types": "^15.7.2",
|
||||||
|
"tsconfig-paths-webpack-plugin": "^3.2.0"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://registry.npm.alibaba-inc.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
22
packages/vision-polyfill/scripts/version.js
Normal file
22
packages/vision-polyfill/scripts/version.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
const { join } = require('path');
|
||||||
|
const fse = require('fs-extra');
|
||||||
|
|
||||||
|
const gitBranchName = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' });
|
||||||
|
const reBranchVersion = /^(?:[a-z]+\/)(\d+\.\d+\.\d+)$/im;
|
||||||
|
|
||||||
|
const match = reBranchVersion.exec(gitBranchName);
|
||||||
|
if (!match) {
|
||||||
|
console.warn(`[checkversion] gitBranchName: ${gitBranchName}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const releaseVersion = match[1];
|
||||||
|
|
||||||
|
const indexFile = join(__dirname, '../src/index.ts');
|
||||||
|
|
||||||
|
const indexContent = fse.readFileSync(indexFile, 'utf-8');
|
||||||
|
|
||||||
|
fse.writeFileSync(indexFile, indexContent.replace('{VERSION}', releaseVersion));
|
||||||
137
packages/vision-polyfill/src/base/base.ts
Normal file
137
packages/vision-polyfill/src/base/base.ts
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import bus from '../bus';
|
||||||
|
import SchemaManager from './schemaManager';
|
||||||
|
import VisualDesigner from './visualDesigner';
|
||||||
|
import VisualManager from './visualManager';
|
||||||
|
|
||||||
|
import { findIndex, get, unionBy, uniqueId } from 'lodash';
|
||||||
|
|
||||||
|
export type removeEventListener = () => void;
|
||||||
|
|
||||||
|
export interface IEventNameMap {
|
||||||
|
[eventName: string]: string | symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISchemaController {
|
||||||
|
getSchemaManager(): SchemaManager;
|
||||||
|
getSchemaManagerById(id?: string): SchemaManager;
|
||||||
|
getSchemaManagerByName(name?: string): SchemaManager[];
|
||||||
|
getSchemaManagerList(): SchemaManager[];
|
||||||
|
connectSchemaManager(manager: SchemaManager): this;
|
||||||
|
connectSchemaManagerList(managerList: SchemaManager[]): this;
|
||||||
|
notifyAllSchemaManagers(eventName: string | symbol, eventData: any): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IManagerController {
|
||||||
|
getManager(): VisualManager;
|
||||||
|
getManagerById(id?: string): VisualManager;
|
||||||
|
getManagerByName(name?: string): VisualManager[];
|
||||||
|
getManagerList(name?: string): VisualManager[];
|
||||||
|
connectManager(manager: VisualManager): this;
|
||||||
|
connectManagerList(managerList: VisualManager[]): this;
|
||||||
|
notifyAllManagers(eventName: string | symbol, eventData: any): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDesignerController {
|
||||||
|
getDesigner(): VisualDesigner;
|
||||||
|
getDesignerById(id?: string): VisualDesigner;
|
||||||
|
getDesignerByName(name?: string): VisualDesigner[];
|
||||||
|
getDesignerList(): VisualDesigner[];
|
||||||
|
connectDesigner(designer: VisualDesigner): this;
|
||||||
|
connectDesignerList(designerList: VisualDesigner[]): this;
|
||||||
|
notifyAllDesigners(eventName: string | symbol, eventData: any): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface INameable {
|
||||||
|
getName(): string;
|
||||||
|
getId(): string;
|
||||||
|
setName(name?: string): this;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IObservable {
|
||||||
|
getEventMap(): IEventNameMap;
|
||||||
|
on(eventName: string | symbol, callback: () => any): removeEventListener;
|
||||||
|
emit(eventName: string | symbol, eventData?: any[]): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IManagerConfigs {
|
||||||
|
name?: string;
|
||||||
|
disableEvents?: boolean;
|
||||||
|
emitter?: IEmitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IEmitter {
|
||||||
|
on(eventName: string | symbol, callback: () => any): removeEventListener;
|
||||||
|
emit(eventName: string | symbol, eventData?: any): boolean;
|
||||||
|
removeListener(eventName: string | symbol, callback: () => any): any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function connectGeneralManager(manager: any, managerList: any[]) {
|
||||||
|
const index = findIndex(managerList, (m) => m.getId() === manager.getId());
|
||||||
|
if (index > -1) {
|
||||||
|
managerList.push(manager);
|
||||||
|
} else {
|
||||||
|
managerList.splice(index, 1, manager);
|
||||||
|
}
|
||||||
|
return managerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function connectGeneralManagerList(managerList: any[], sourceManagerList: any[]): any {
|
||||||
|
return unionBy(sourceManagerList, managerList, (manager) => manager.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BaseManager implements INameable, IObservable {
|
||||||
|
static EVENTS: IEventNameMap = {};
|
||||||
|
|
||||||
|
static NAME = 'BaseManager';
|
||||||
|
|
||||||
|
private name: string;
|
||||||
|
|
||||||
|
private id: string;
|
||||||
|
|
||||||
|
private emitter: any;
|
||||||
|
|
||||||
|
constructor(managerConfigs: IManagerConfigs = {}) {
|
||||||
|
this.name = managerConfigs.name || get(this, 'constructor', 'NAME');
|
||||||
|
this.id = uniqueId(this.name);
|
||||||
|
if (!managerConfigs.disableEvents) {
|
||||||
|
if (managerConfigs.emitter) {
|
||||||
|
// 使用自定义的满足 EventEmitter 接口要求的自定义事件对象
|
||||||
|
this.emitter = managerConfigs.emitter;
|
||||||
|
} else {
|
||||||
|
// Bus 为单例模式
|
||||||
|
this.emitter = bus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getId(): string {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name: string): this {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getName(): string {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEventMap() {
|
||||||
|
/**
|
||||||
|
* Hack for get current constructor
|
||||||
|
* because if we write this.constructor.EVENTS
|
||||||
|
* ts compiler will show compiled error
|
||||||
|
*/
|
||||||
|
return get(this, 'constructor', BaseManager.EVENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
on(eventName: string | symbol, callback: () => any): removeEventListener {
|
||||||
|
this.emitter.on(eventName, callback);
|
||||||
|
return () => this.emitter.removeListener(eventName, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(eventName: string | symbol, ...eventData: any[]): boolean {
|
||||||
|
return this.emitter.emit.call(this.emitter, eventName, ...eventData);
|
||||||
|
}
|
||||||
|
}
|
||||||
44
packages/vision-polyfill/src/base/const.ts
Normal file
44
packages/vision-polyfill/src/base/const.ts
Normal file
@ -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',
|
||||||
|
};
|
||||||
104
packages/vision-polyfill/src/base/schemaManager.ts
Normal file
104
packages/vision-polyfill/src/base/schemaManager.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import { cloneDeep, find } from 'lodash';
|
||||||
|
|
||||||
|
import {
|
||||||
|
BaseManager,
|
||||||
|
connectGeneralManager,
|
||||||
|
connectGeneralManagerList,
|
||||||
|
IManagerController,
|
||||||
|
ISchemaController,
|
||||||
|
} from './base';
|
||||||
|
import VisualManager from './visualManager';
|
||||||
|
|
||||||
|
export default class SchemaManager extends BaseManager implements IManagerController, ISchemaController {
|
||||||
|
private schemaData: object = {};
|
||||||
|
|
||||||
|
private visualManagerList: VisualManager[] = [];
|
||||||
|
|
||||||
|
private schemaManagerList: SchemaManager[] = [];
|
||||||
|
|
||||||
|
getManager(): VisualManager {
|
||||||
|
return this.visualManagerList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerByName(name?: string): VisualManager[] {
|
||||||
|
return this.visualManagerList.filter((m) => m.getName() === name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerById(id?: string): VisualManager {
|
||||||
|
return find(this.visualManagerList, (m) => m.getId() === id) as VisualManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerList(): VisualManager[] {
|
||||||
|
return this.visualManagerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaManager(): SchemaManager {
|
||||||
|
return this.schemaManagerList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaManagerById(id?: string): SchemaManager {
|
||||||
|
return find(this.schemaManagerList, (m) => m.getId() === id) as SchemaManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaManagerByName(name?: string): SchemaManager[] {
|
||||||
|
return this.schemaManagerList.filter((m) => m.getName() === name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSchemaManagerList() {
|
||||||
|
return this.schemaManagerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectManager(manager: any) {
|
||||||
|
connectGeneralManager.call(this, manager, this.visualManagerList as any);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectSchemaManager(manager: SchemaManager): this {
|
||||||
|
connectGeneralManager.call(this, manager, this.schemaManagerList);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectManagerList(managerList: VisualManager[]): this {
|
||||||
|
this.visualManagerList = connectGeneralManagerList.call(this, managerList as any, this.visualManagerList as any);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectSchemaManagerList(managerList: SchemaManager[]): this {
|
||||||
|
this.schemaManagerList = connectGeneralManagerList.call(this, managerList, this.schemaManagerList);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyAllManagers(eventName: string | symbol, ...eventData: any[]): boolean {
|
||||||
|
return this.visualManagerList.map((m) => m.emit(eventName, eventData)).every((r) => r);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyAllSchemaManagers(eventName: string | symbol, ...eventData: any[]): boolean {
|
||||||
|
return this.schemaManagerList.map((m) => m.emit(eventName, eventData)).every((r) => r);
|
||||||
|
}
|
||||||
|
|
||||||
|
exportSchema(): string {
|
||||||
|
try {
|
||||||
|
return JSON.stringify(this.schemaData);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exportSchemaObject(): object {
|
||||||
|
return cloneDeep(this.schemaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
importSchema(schemaString: string): this {
|
||||||
|
try {
|
||||||
|
this.schemaData = JSON.parse(schemaString);
|
||||||
|
return this;
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
importSchemaObject(schema: object): this {
|
||||||
|
this.schemaData = schema;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
116
packages/vision-polyfill/src/base/visualDesigner.ts
Normal file
116
packages/vision-polyfill/src/base/visualDesigner.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import { assign, find, get } from 'lodash';
|
||||||
|
import { Component } from 'react';
|
||||||
|
|
||||||
|
import bus from '../bus';
|
||||||
|
import {
|
||||||
|
BaseManager,
|
||||||
|
connectGeneralManager,
|
||||||
|
connectGeneralManagerList,
|
||||||
|
IEmitter,
|
||||||
|
IEventNameMap,
|
||||||
|
IManagerController,
|
||||||
|
INameable,
|
||||||
|
IObservable,
|
||||||
|
} from './base';
|
||||||
|
import VisualManager from './visualManager';
|
||||||
|
|
||||||
|
interface IDesignerProps {
|
||||||
|
name?: string;
|
||||||
|
visualManagers?: VisualManager[];
|
||||||
|
emitter?: IEmitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class VisualDesigner extends Component implements IManagerController, IObservable, INameable {
|
||||||
|
static NAME = 'VisualDesigner';
|
||||||
|
|
||||||
|
static EVENTS: IEventNameMap = {};
|
||||||
|
|
||||||
|
props: IDesignerProps = {};
|
||||||
|
|
||||||
|
defaultProps: IDesignerProps = {
|
||||||
|
name: 'defaultDesigner',
|
||||||
|
visualManagers: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
private visualManagerList: VisualManager[] = [];
|
||||||
|
|
||||||
|
private name = '';
|
||||||
|
|
||||||
|
private id = '';
|
||||||
|
|
||||||
|
private emitter: IEmitter;
|
||||||
|
|
||||||
|
constructor(props: IDesignerProps) {
|
||||||
|
super(props);
|
||||||
|
this.setName(props.name || get(this, 'constructor', 'NAME'));
|
||||||
|
this.connectManagerList(this.props.visualManagers as any);
|
||||||
|
|
||||||
|
if (props.emitter) {
|
||||||
|
// 使用自定义的满足 EventEmitter 接口要求的自定义事件对象
|
||||||
|
this.emitter = props.emitter;
|
||||||
|
} else {
|
||||||
|
this.emitter = bus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getId(): string {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
setName(name: string): this {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
getManager(): VisualManager {
|
||||||
|
return this.visualManagerList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerByName(name?: string): VisualManager[] {
|
||||||
|
return this.visualManagerList.filter((m) => m.getName() === name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerById(id: string): VisualManager {
|
||||||
|
return find(this.visualManagerList, (m) => m.getId() === id) as VisualManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerList(): VisualManager[] {
|
||||||
|
return this.visualManagerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectManager(manager: VisualManager) {
|
||||||
|
connectGeneralManager.call(this, manager, this.visualManagerList);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectManagerList(managerList: VisualManager[]): this {
|
||||||
|
this.visualManagerList = connectGeneralManagerList.call(this, managerList, this.visualManagerList);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEventMap() {
|
||||||
|
/**
|
||||||
|
* Hack for get current constructor
|
||||||
|
* because if we write this.constructor.EVENTS
|
||||||
|
* ts compiler will show compiled error
|
||||||
|
*/
|
||||||
|
return get(this, 'constructor', BaseManager.EVENTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyAllManagers(eventName: string | symbol, ...eventData: any[]): boolean {
|
||||||
|
return this.visualManagerList.map((m) => m.emit(eventName, eventData)).every((r) => r);
|
||||||
|
}
|
||||||
|
|
||||||
|
on(eventName: string | symbol, callback: () => any) {
|
||||||
|
this.emitter.on(eventName, callback);
|
||||||
|
return () => this.emitter.removeListener(eventName, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(eventName: string | symbol, ...eventData: any[]): boolean {
|
||||||
|
return this.emitter.emit.call(this.emitter, eventName, ...eventData);
|
||||||
|
}
|
||||||
|
}
|
||||||
80
packages/vision-polyfill/src/base/visualManager.ts
Normal file
80
packages/vision-polyfill/src/base/visualManager.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import { find } from 'lodash';
|
||||||
|
|
||||||
|
import {
|
||||||
|
BaseManager,
|
||||||
|
connectGeneralManager,
|
||||||
|
connectGeneralManagerList,
|
||||||
|
IDesignerController,
|
||||||
|
IManagerController,
|
||||||
|
} from './base';
|
||||||
|
import VisualDesigner from './visualDesigner';
|
||||||
|
|
||||||
|
export default class VisualManager extends BaseManager implements IManagerController, IDesignerController {
|
||||||
|
private visualManagerList: VisualManager[] = [];
|
||||||
|
|
||||||
|
private visualDesignerList: VisualDesigner[] = [];
|
||||||
|
|
||||||
|
getManager(): VisualManager {
|
||||||
|
return this.visualManagerList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerByName(name?: string): VisualManager[] {
|
||||||
|
return this.visualManagerList.filter((m) => m.getName() === name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerById(id?: string): VisualManager {
|
||||||
|
return find(this.visualManagerList, (m) => m.getId() === id) as VisualManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerList(): VisualManager[] {
|
||||||
|
return this.visualManagerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDesigner(): VisualDesigner {
|
||||||
|
return this.visualDesignerList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
getDesignerByName(name?: string): VisualDesigner[] {
|
||||||
|
return this.visualDesignerList.filter((m) => m.getName() === name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDesignerById(id?: string): VisualDesigner {
|
||||||
|
return find(this.visualDesignerList, (m) => m.getId() === id) as VisualDesigner;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDesignerList() {
|
||||||
|
return this.visualDesignerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectManager(manager: VisualManager) {
|
||||||
|
connectGeneralManager.call(this, manager, this.visualManagerList);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectDesigner(manager: VisualDesigner): this {
|
||||||
|
connectGeneralManager.call(this, manager, this.visualDesignerList);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectManagerList(managerList: VisualManager[]): this {
|
||||||
|
this.visualManagerList = connectGeneralManagerList.call(this, managerList, this.visualManagerList);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectDesignerList(managerList: VisualDesigner[]): this {
|
||||||
|
this.visualDesignerList = connectGeneralManagerList.call(this, managerList, this.visualDesignerList);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyAllManagers(eventName: string | symbol, ...eventData: any[]): boolean {
|
||||||
|
return this.getManagerList()
|
||||||
|
.map((m) => m.emit(eventName, eventData))
|
||||||
|
.every((r) => r);
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyAllDesigners(eventName: string | symbol, ...eventData: any[]): boolean {
|
||||||
|
return this.getDesignerList()
|
||||||
|
.map((m) => m.emit(eventName, eventData))
|
||||||
|
.every((r) => r);
|
||||||
|
}
|
||||||
|
}
|
||||||
48
packages/vision-polyfill/src/base/visualRender.ts
Normal file
48
packages/vision-polyfill/src/base/visualRender.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { find } from 'lodash';
|
||||||
|
|
||||||
|
import { BaseManager, connectGeneralManager, connectGeneralManagerList, IManagerController } from './base';
|
||||||
|
import VisualManager from './visualManager';
|
||||||
|
|
||||||
|
export default class VisualRender extends BaseManager implements IManagerController {
|
||||||
|
private visualManagerList: VisualManager[] = [];
|
||||||
|
|
||||||
|
getManager(): VisualManager {
|
||||||
|
return this.visualManagerList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerByName(name?: string): VisualManager[] {
|
||||||
|
return this.visualManagerList.filter((m) => m.getName() === name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerById(id?: string): VisualManager {
|
||||||
|
return find(this.visualManagerList, (m) => m.getId() === id) as VisualManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
getManagerList(): VisualManager[] {
|
||||||
|
return this.visualManagerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectManager(manager: VisualManager) {
|
||||||
|
connectGeneralManager.call(this, manager, this.visualManagerList);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectManagerList(managerList: VisualManager[]): this {
|
||||||
|
this.visualManagerList = connectGeneralManagerList.call(this, managerList, this.visualManagerList);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyAllManagers(eventName: string | symbol, ...eventData: any[]): boolean {
|
||||||
|
return this.visualManagerList.map((m) => m.emit(eventName, eventData)).every((r) => r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render function
|
||||||
|
* @override
|
||||||
|
*
|
||||||
|
* @memberof VisualRender
|
||||||
|
*/
|
||||||
|
render(): any {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
208
packages/vision-polyfill/src/bundle/bundle.ts
Normal file
208
packages/vision-polyfill/src/bundle/bundle.ts
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
import lg from '@ali/vu-logger';
|
||||||
|
import { ComponentClass, ComponentType } from 'react';
|
||||||
|
import Prototype, { isPrototype } from './prototype';
|
||||||
|
import { designer } from '@ali/lowcode-engine';
|
||||||
|
import { upgradeMetadata } from './upgrade-metadata';
|
||||||
|
import trunk from './trunk';
|
||||||
|
|
||||||
|
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 interface ComponentProtoBundle {
|
||||||
|
// @ali/vc-xxx
|
||||||
|
name: string;
|
||||||
|
componentName?: string;
|
||||||
|
category?: string;
|
||||||
|
module: Prototype | Prototype[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComponentViewBundle {
|
||||||
|
// @ali/vc-xxx
|
||||||
|
name: string;
|
||||||
|
// alias to property name
|
||||||
|
componentName?: string;
|
||||||
|
category?: string;
|
||||||
|
module: ComponentType<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Bundle {
|
||||||
|
static createPrototype = Prototype.create;
|
||||||
|
|
||||||
|
static addGlobalPropsReducer = Prototype.addGlobalPropsReducer;
|
||||||
|
|
||||||
|
static addGlobalPropsConfigure = Prototype.addGlobalPropsConfigure;
|
||||||
|
|
||||||
|
static addGlobalExtraActions = Prototype.addGlobalExtraActions;
|
||||||
|
|
||||||
|
static removeGlobalPropsConfigure = Prototype.removeGlobalPropsConfigure;
|
||||||
|
|
||||||
|
static overridePropsConfigure = Prototype.overridePropsConfigure;
|
||||||
|
|
||||||
|
static create(protos: ComponentProtoBundle[], views?: ComponentViewBundle[]) {
|
||||||
|
return new Bundle(protos, views);
|
||||||
|
}
|
||||||
|
|
||||||
|
private viewsMap: { [componentName: string]: ComponentType } = {};
|
||||||
|
|
||||||
|
private registry: { [componentName: string]: Prototype } = {};
|
||||||
|
|
||||||
|
private prototypeList: Prototype[] = [];
|
||||||
|
|
||||||
|
constructor(protos?: ComponentProtoBundle[], views?: ComponentViewBundle[]) {
|
||||||
|
// 注册 prototypeView 视图
|
||||||
|
if (views && Array.isArray(views)) {
|
||||||
|
this.recursivelyRegisterViews(views);
|
||||||
|
}
|
||||||
|
protos?.forEach((item) => {
|
||||||
|
const prototype = item.module;
|
||||||
|
if (prototype instanceof Prototype) {
|
||||||
|
this.revisePrototype(item, prototype);
|
||||||
|
const componentName = item.componentName || prototype.getComponentName()!;
|
||||||
|
const matchedView = this.viewsMap[componentName] || null;
|
||||||
|
if (matchedView) {
|
||||||
|
prototype.setView(matchedView);
|
||||||
|
}
|
||||||
|
this.registerPrototype(prototype);
|
||||||
|
} else if (Array.isArray(prototype)) {
|
||||||
|
this.recursivelyRegisterPrototypes(prototype, item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// invoke prototype mocker while the prototype does not exist
|
||||||
|
Object.keys(this.viewsMap).forEach((viewName) => {
|
||||||
|
if (!this.prototypeList.find((proto) => proto.getComponentName() === viewName)) {
|
||||||
|
const mockedPrototype = trunk.mockComponentPrototype(this.viewsMap[viewName]);
|
||||||
|
if (mockedPrototype) {
|
||||||
|
mockedPrototype.setView(this.viewsMap[viewName]);
|
||||||
|
this.registerPrototype(mockedPrototype);
|
||||||
|
if (!mockedPrototype.getPackageName()) {
|
||||||
|
mockedPrototype.setPackageName((this.viewsMap[viewName] as any)._packageName_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getFromMeta(componentName: string): Prototype {
|
||||||
|
if (this.registry[componentName]) {
|
||||||
|
return this.registry[componentName];
|
||||||
|
}
|
||||||
|
const meta = designer.getComponentMeta(componentName);
|
||||||
|
const prototype = Prototype.create(meta);
|
||||||
|
this.prototypeList.push(prototype);
|
||||||
|
this.registry[componentName] = prototype;
|
||||||
|
return prototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeComponentBundle(componentName: string) {
|
||||||
|
const cIndex = this.prototypeList.findIndex((proto) => proto.getComponentName() === componentName);
|
||||||
|
delete this.registry[componentName];
|
||||||
|
this.prototypeList.splice(cIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getList() {
|
||||||
|
return this.prototypeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(componentName: string) {
|
||||||
|
return this.registry[componentName];
|
||||||
|
}
|
||||||
|
|
||||||
|
replacePrototype(componentName: string, cp: Prototype) {
|
||||||
|
const view: any = this.get(componentName).getView();
|
||||||
|
this.removeComponentBundle(componentName);
|
||||||
|
this.registry[cp.getComponentName()!] = cp;
|
||||||
|
this.prototypeList.push(cp);
|
||||||
|
cp.setView(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO dirty fix
|
||||||
|
*/
|
||||||
|
addComponentBundle(bundles: any) {
|
||||||
|
/**
|
||||||
|
* Normal Component bundle: [ Prototype, PrototypeView ]
|
||||||
|
* Component without Prototype.js: [ View ]
|
||||||
|
*/
|
||||||
|
if (bundles.length >= 2) {
|
||||||
|
const prototype = bundles[0];
|
||||||
|
const metadata = upgradeMetadata({ ...prototype.options, packageName: prototype.packageName });
|
||||||
|
prototype.meta = designer.createComponentMeta(metadata);
|
||||||
|
const prototypeView = bundles[1];
|
||||||
|
prototype.setView(prototypeView);
|
||||||
|
this.registerPrototype(prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
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 = getCamelName(viewName || item.name);
|
||||||
|
}
|
||||||
|
(viewDetail as any)._packageName_ = viewName || item.name;
|
||||||
|
this.viewsMap[viewDetail.displayName] = viewDetail;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private recursivelyRegisterPrototypes(list: any[], cp: ComponentProtoBundle) {
|
||||||
|
const propList: ComponentProtoBundle[] = list;
|
||||||
|
propList.forEach((proto: any, index: number) => {
|
||||||
|
if (Array.isArray(proto)) {
|
||||||
|
this.recursivelyRegisterPrototypes(proto, cp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isPrototype(proto)) {
|
||||||
|
const componentName = proto.getComponentName()!;
|
||||||
|
if (this.viewsMap[componentName]) {
|
||||||
|
proto.setView(this.viewsMap[componentName]);
|
||||||
|
}
|
||||||
|
if (cp.name && !proto.getPackageName()) {
|
||||||
|
proto.setPackageName(cp.name);
|
||||||
|
}
|
||||||
|
this.registerPrototype(proto);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private revisePrototype(item: ComponentProtoBundle, prototype: Prototype) {
|
||||||
|
if (item.category) {
|
||||||
|
prototype.setCategory(item.category);
|
||||||
|
}
|
||||||
|
if (item.name && !prototype.getPackageName()) {
|
||||||
|
prototype.setPackageName(item.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private registerPrototype(prototype: Prototype) {
|
||||||
|
const componentName = prototype.getComponentName()!;
|
||||||
|
if (this.registry[componentName]) {
|
||||||
|
lg.warn('WARN: override prototype', prototype, componentName);
|
||||||
|
const idx = this.prototypeList.findIndex((proto) => componentName === proto.getComponentName());
|
||||||
|
this.prototypeList[idx] = prototype;
|
||||||
|
} else {
|
||||||
|
this.prototypeList.push(prototype);
|
||||||
|
}
|
||||||
|
this.registry[componentName] = prototype;
|
||||||
|
}
|
||||||
|
}
|
||||||
371
packages/vision-polyfill/src/bundle/prototype.ts
Normal file
371
packages/vision-polyfill/src/bundle/prototype.ts
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
import { ComponentType, ReactElement, Component, FunctionComponent } from 'react';
|
||||||
|
import { ComponentMetadata, FieldConfig, InitialItem, FilterItem, AutorunItem } from '@ali/lowcode-types';
|
||||||
|
import {
|
||||||
|
ComponentMeta,
|
||||||
|
} from '@ali/lowcode-designer';
|
||||||
|
import {
|
||||||
|
OldPropConfig,
|
||||||
|
OldPrototypeConfig,
|
||||||
|
upgradeMetadata,
|
||||||
|
upgradeActions,
|
||||||
|
upgradePropConfig,
|
||||||
|
upgradeConfigure,
|
||||||
|
} from './upgrade-metadata';
|
||||||
|
import { accessLibrary } from '@ali/lowcode-utils';
|
||||||
|
import { designer, designerHelper, editorHelper } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
const {
|
||||||
|
addBuiltinComponentAction,
|
||||||
|
isComponentMeta,
|
||||||
|
registerMetadataTransducer,
|
||||||
|
TransformStage,
|
||||||
|
} = designerHelper;
|
||||||
|
const { intl } = editorHelper;
|
||||||
|
|
||||||
|
const GlobalPropsConfigure: Array<{
|
||||||
|
position: string;
|
||||||
|
initials?: InitialItem[];
|
||||||
|
filters?: FilterItem[];
|
||||||
|
autoruns?: AutorunItem[];
|
||||||
|
config: FieldConfig;
|
||||||
|
}> = [];
|
||||||
|
const Overrides: {
|
||||||
|
[componentName: string]: {
|
||||||
|
initials?: InitialItem[];
|
||||||
|
filters?: FilterItem[];
|
||||||
|
autoruns?: AutorunItem[];
|
||||||
|
override: any;
|
||||||
|
};
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
function addGlobalPropsConfigure(config: OldGlobalPropConfig) {
|
||||||
|
const initials: InitialItem[] = [];
|
||||||
|
const filters: FilterItem[] = [];
|
||||||
|
const autoruns: AutorunItem[] = [];
|
||||||
|
GlobalPropsConfigure.push({
|
||||||
|
position: config.position || 'bottom',
|
||||||
|
initials,
|
||||||
|
filters,
|
||||||
|
autoruns,
|
||||||
|
config: upgradePropConfig(config, {
|
||||||
|
addInitial: (item) => {
|
||||||
|
initials.push(item);
|
||||||
|
},
|
||||||
|
addFilter: (item) => {
|
||||||
|
filters.push(item);
|
||||||
|
},
|
||||||
|
addAutorun: (item) => {
|
||||||
|
autoruns.push(item);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function removeGlobalPropsConfigure(name: string) {
|
||||||
|
let l = GlobalPropsConfigure.length;
|
||||||
|
while (l-- > 0) {
|
||||||
|
if (GlobalPropsConfigure[l].config.name === name) {
|
||||||
|
GlobalPropsConfigure.splice(l, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function overridePropsConfigure(componentName: string, config: { [name: string]: OldPropConfig } | OldPropConfig[]) {
|
||||||
|
const initials: InitialItem[] = [];
|
||||||
|
const filters: FilterItem[] = [];
|
||||||
|
const autoruns: AutorunItem[] = [];
|
||||||
|
const addInitial = (item: InitialItem) => {
|
||||||
|
initials.push(item);
|
||||||
|
};
|
||||||
|
const addFilter = (item: FilterItem) => {
|
||||||
|
filters.push(item);
|
||||||
|
};
|
||||||
|
const addAutorun = (item: AutorunItem) => {
|
||||||
|
autoruns.push(item);
|
||||||
|
};
|
||||||
|
let override: any;
|
||||||
|
if (Array.isArray(config)) {
|
||||||
|
override = upgradeConfigure(config, { addInitial, addFilter, addAutorun });
|
||||||
|
} else {
|
||||||
|
override = {};
|
||||||
|
Object.keys(config).forEach((key) => {
|
||||||
|
override[key] = upgradePropConfig(config[key], { addInitial, addFilter, addAutorun });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Overrides[componentName] = {
|
||||||
|
initials,
|
||||||
|
filters,
|
||||||
|
autoruns,
|
||||||
|
override,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
registerMetadataTransducer(
|
||||||
|
(metadata) => {
|
||||||
|
const {
|
||||||
|
configure: { combined, props },
|
||||||
|
componentName,
|
||||||
|
} = metadata;
|
||||||
|
let top: FieldConfig[];
|
||||||
|
let bottom: FieldConfig[];
|
||||||
|
if (combined) {
|
||||||
|
top = combined?.[0]?.items || combined;
|
||||||
|
bottom = combined?.[combined.length - 1]?.items || combined;
|
||||||
|
} else if (props) {
|
||||||
|
top = props;
|
||||||
|
bottom = props;
|
||||||
|
} else {
|
||||||
|
metadata.configure.props = top = bottom = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalPropsConfigure.forEach((item) => {
|
||||||
|
const position = item.position || 'bottom';
|
||||||
|
|
||||||
|
if (position === 'top') {
|
||||||
|
top.unshift(item.config);
|
||||||
|
} else if (position === 'bottom') {
|
||||||
|
bottom.push(item.config);
|
||||||
|
}
|
||||||
|
// TODO: replace autoruns,initials,filters
|
||||||
|
});
|
||||||
|
|
||||||
|
const override = Overrides[componentName]?.override;
|
||||||
|
if (override) {
|
||||||
|
if (Array.isArray(override)) {
|
||||||
|
// 替换 #props,其他暂时忽略
|
||||||
|
const idx = metadata.configure.combined?.findIndex(item => item.name === '#props');
|
||||||
|
if (idx > -1) {
|
||||||
|
metadata.configure.combined[idx].items = override;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let l = top.length;
|
||||||
|
let item;
|
||||||
|
while (l-- > 0) {
|
||||||
|
item = top[l];
|
||||||
|
if (item.name in override) {
|
||||||
|
if (override[item.name]) {
|
||||||
|
top.splice(l, 1, override[item.name]);
|
||||||
|
} else {
|
||||||
|
top.splice(l, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: replace autoruns,initials,filters
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
},
|
||||||
|
100,
|
||||||
|
'vision-polyfill',
|
||||||
|
);
|
||||||
|
|
||||||
|
function addGlobalExtraActions(action: () => ReactElement) {
|
||||||
|
upgradeActions(action)?.forEach(addBuiltinComponentAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// const GlobalPropsReducers: any[] = [];
|
||||||
|
function addGlobalPropsReducer(reducer: () => any) {
|
||||||
|
// GlobalPropsReducers.push(reducer);
|
||||||
|
designer.addPropsReducer(reducer, TransformStage.Render);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OldGlobalPropConfig extends OldPropConfig {
|
||||||
|
position?: 'top' | 'bottom';
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageMaps: any = {};
|
||||||
|
|
||||||
|
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 isNewSpec(options: any): options is ComponentMetadata {
|
||||||
|
return (
|
||||||
|
options &&
|
||||||
|
(options.npm || options.props || (options.configure && (options.configure.props || options.configure.component)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Prototype {
|
||||||
|
static addGlobalPropsReducer = addGlobalPropsReducer;
|
||||||
|
|
||||||
|
static addGlobalPropsConfigure = addGlobalPropsConfigure;
|
||||||
|
|
||||||
|
static addGlobalExtraActions = addGlobalExtraActions;
|
||||||
|
|
||||||
|
static removeGlobalPropsConfigure = removeGlobalPropsConfigure;
|
||||||
|
|
||||||
|
static overridePropsConfigure = overridePropsConfigure;
|
||||||
|
|
||||||
|
static create(config: OldPrototypeConfig | ComponentMetadata | ComponentMeta, extraConfigs: any = null, lookup = false) {
|
||||||
|
return new Prototype(config, extraConfigs, lookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly isPrototype = true;
|
||||||
|
|
||||||
|
readonly meta: ComponentMeta;
|
||||||
|
|
||||||
|
readonly options: OldPrototypeConfig | ComponentMetadata;
|
||||||
|
|
||||||
|
get componentName() {
|
||||||
|
return this.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
get packageName() {
|
||||||
|
return this.meta.npm?.package;
|
||||||
|
}
|
||||||
|
|
||||||
|
set packageName(pkgName) {
|
||||||
|
if (this.meta.npm) {
|
||||||
|
this.meta.npm.package = pkgName;
|
||||||
|
} else {
|
||||||
|
this.meta.npm = { package: pkgName };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兼容原 vision 用法
|
||||||
|
view: ComponentType | undefined;
|
||||||
|
|
||||||
|
constructor(input: OldPrototypeConfig | ComponentMetadata | ComponentMeta, extraConfigs: any = null, lookup = false) {
|
||||||
|
if (lookup) {
|
||||||
|
this.meta = designer.getComponentMeta(input.componentName);
|
||||||
|
this.options = this.meta.getMetadata();
|
||||||
|
return this.meta.prototype || this;
|
||||||
|
} else {
|
||||||
|
if (isComponentMeta(input)) {
|
||||||
|
this.meta = input;
|
||||||
|
this.options = input.getMetadata();
|
||||||
|
} else {
|
||||||
|
this.options = input;
|
||||||
|
const metadata = isNewSpec(input) ? input : upgradeMetadata(input);
|
||||||
|
this.meta = designer.createComponentMeta(metadata);
|
||||||
|
}
|
||||||
|
(this.meta as any).prototype = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getId() {
|
||||||
|
return this.getComponentName();
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfig(configName?: keyof (OldPrototypeConfig | ComponentMetadata)) {
|
||||||
|
if (configName) {
|
||||||
|
return this.options[configName];
|
||||||
|
}
|
||||||
|
return this.options;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPackageName() {
|
||||||
|
return this.packageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
getContextInfo(name: string): any {
|
||||||
|
return this.meta.getMetadata().experimental?.context?.[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
getTitle() {
|
||||||
|
return intl(this.meta.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponentName() {
|
||||||
|
return this.meta.componentName;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDocUrl() {
|
||||||
|
return this.meta.getMetadata().docUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPropConfigs() {
|
||||||
|
return this.options;
|
||||||
|
}
|
||||||
|
|
||||||
|
private category?: string;
|
||||||
|
|
||||||
|
getCategory() {
|
||||||
|
if (this.options.category != null) {
|
||||||
|
return this.options.category;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.meta.getMetadata().tags?.[0] || '*';
|
||||||
|
}
|
||||||
|
|
||||||
|
setCategory(category: string) {
|
||||||
|
this.options.category = category;
|
||||||
|
}
|
||||||
|
|
||||||
|
getIcon() {
|
||||||
|
return this.meta.icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfigure() {
|
||||||
|
return this.meta.configure;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRectSelector() {
|
||||||
|
return this.meta.rootSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
isContainer() {
|
||||||
|
return this.meta.isContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
isModal() {
|
||||||
|
return this.meta.isModal;
|
||||||
|
}
|
||||||
|
|
||||||
|
isAutoGenerated() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPackageName(name: string) {
|
||||||
|
this.meta.setNpm({
|
||||||
|
package: name,
|
||||||
|
componentName: this.getComponentName(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setView(view: ComponentType<any>) {
|
||||||
|
this.view = view;
|
||||||
|
const metadata = this.meta.getMetadata();
|
||||||
|
if (!metadata.experimental) {
|
||||||
|
metadata.experimental = {
|
||||||
|
view,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
metadata.experimental.view = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getView() {
|
||||||
|
return (
|
||||||
|
this.view ||
|
||||||
|
this.meta.getMetadata().experimental?.view ||
|
||||||
|
designer.currentDocument?.simulator?.getComponent(this.getComponentName())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPrototype(obj: any): obj is Prototype {
|
||||||
|
return obj && obj.isPrototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Prototype;
|
||||||
144
packages/vision-polyfill/src/bundle/trunk.ts
Normal file
144
packages/vision-polyfill/src/bundle/trunk.ts
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
import { ReactElement, ComponentType } from 'react';
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import { setters } from '@ali/lowcode-engine';
|
||||||
|
import { RegisteredSetter } from '@ali/lowcode-editor-core';
|
||||||
|
import lg from '@ali/vu-logger';
|
||||||
|
import { CustomView } from '@ali/lowcode-types';
|
||||||
|
import Bundle from './bundle';
|
||||||
|
import Prototype from './prototype';
|
||||||
|
|
||||||
|
const { registerSetter, getSetter } = setters;
|
||||||
|
|
||||||
|
export class Trunk {
|
||||||
|
private trunk: any[] = [];
|
||||||
|
|
||||||
|
private emitter: EventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
private metaBundle = new Bundle();
|
||||||
|
|
||||||
|
private componentPrototypeMocker: any;
|
||||||
|
|
||||||
|
isReady() {
|
||||||
|
return this.getList().length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
addBundle(bundle: Bundle) {
|
||||||
|
this.trunk.push(bundle);
|
||||||
|
this.emitter.emit('trunkchange');
|
||||||
|
}
|
||||||
|
|
||||||
|
getBundle(): Bundle {
|
||||||
|
console.warn('Trunk.getBundle is deprecated');
|
||||||
|
return this.trunk[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
getList(): any[] {
|
||||||
|
const list = this.trunk.filter(o => o).reduceRight((prev, cur) => prev.concat(cur.getList()), []);
|
||||||
|
const result: Prototype[] = [];
|
||||||
|
list.forEach((item: Prototype) => {
|
||||||
|
if (!result.find(r => r.options.componentName === item.options.componentName)) {
|
||||||
|
result.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrototype(name: string) {
|
||||||
|
let i = this.trunk.length;
|
||||||
|
let bundle;
|
||||||
|
let prototype;
|
||||||
|
while (i-- > 0) {
|
||||||
|
bundle = this.trunk[i];
|
||||||
|
prototype = bundle.get(name);
|
||||||
|
if (prototype) {
|
||||||
|
return (prototype.meta as any).prototype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.metaBundle.getFromMeta(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrototypeById(id: string) {
|
||||||
|
return this.getPrototype(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
listByCategory() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrototypeView(componentName: string) {
|
||||||
|
return this.getPrototype(componentName)?.getView();
|
||||||
|
}
|
||||||
|
|
||||||
|
onTrunkChange(func: () => any) {
|
||||||
|
this.emitter.on('trunkchange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('trunkchange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
registerSetter(type: string, setter: CustomView | RegisteredSetter) {
|
||||||
|
registerSetter(type, setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeLoadBundle() {
|
||||||
|
console.warn('Trunk.beforeLoadBundle is deprecated');
|
||||||
|
}
|
||||||
|
|
||||||
|
afterLoadBundle() {
|
||||||
|
console.warn('Trunk.afterLoadBundle is deprecated');
|
||||||
|
}
|
||||||
|
|
||||||
|
registerComponentPrototypeMocker(mocker: any) {
|
||||||
|
this.componentPrototypeMocker = mocker;
|
||||||
|
}
|
||||||
|
|
||||||
|
mockComponentPrototype(bundle: any) {
|
||||||
|
if (!this.componentPrototypeMocker) {
|
||||||
|
lg.error('ERROR: no component prototypeMocker is set');
|
||||||
|
}
|
||||||
|
return this.componentPrototypeMocker
|
||||||
|
&& this.componentPrototypeMocker.mockPrototype(bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPackages() {
|
||||||
|
console.warn('Trunk.setPackages is deprecated');
|
||||||
|
}
|
||||||
|
|
||||||
|
getSetter(type: string): any {
|
||||||
|
const setter = getSetter(type);
|
||||||
|
if (setter?.component) {
|
||||||
|
return setter.component;
|
||||||
|
}
|
||||||
|
return setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRecents(limit: number) {
|
||||||
|
return this.getList().filter((prototype) => prototype.getCategory()).slice(0, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Trunk();
|
||||||
823
packages/vision-polyfill/src/bundle/upgrade-metadata.ts
Normal file
823
packages/vision-polyfill/src/bundle/upgrade-metadata.ts
Normal file
@ -0,0 +1,823 @@
|
|||||||
|
import { ComponentType, ReactElement, isValidElement, ComponentClass } from 'react';
|
||||||
|
import { isPlainObject, uniqueId } from '@ali/lowcode-utils';
|
||||||
|
import { isI18nData, SettingTarget, InitialItem, FilterItem, isJSSlot, ProjectSchema, AutorunItem, isJSBlock } from '@ali/lowcode-types';
|
||||||
|
import { editorHelper, designerHelper } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
const { SettingField } = designerHelper;
|
||||||
|
const { untracked } = editorHelper;
|
||||||
|
|
||||||
|
type Field = SettingTarget;
|
||||||
|
|
||||||
|
export enum DISPLAY_TYPE {
|
||||||
|
NONE = 'none', // => condition'plain'
|
||||||
|
PLAIN = 'plain',
|
||||||
|
INLINE = 'inline',
|
||||||
|
BLOCK = 'block',
|
||||||
|
ACCORDION = 'accordion',
|
||||||
|
TAB = 'tab', // => 'accordion'
|
||||||
|
ENTRY = 'entry',
|
||||||
|
}
|
||||||
|
|
||||||
|
// from vision 5.4
|
||||||
|
export interface OldPropConfig {
|
||||||
|
/**
|
||||||
|
* composite share the namespace
|
||||||
|
* group just be tie up together
|
||||||
|
*/
|
||||||
|
type?: 'composite' | 'group'; // => composite as objectSetter
|
||||||
|
/**
|
||||||
|
* when type is composite or group
|
||||||
|
*/
|
||||||
|
items?: OldPropConfig[]; // => items
|
||||||
|
/**
|
||||||
|
* property name: the field key in props of schema
|
||||||
|
*/
|
||||||
|
name: string; // =>
|
||||||
|
title?: string; // =>
|
||||||
|
tip?: {
|
||||||
|
content?: string;
|
||||||
|
url?: string;
|
||||||
|
};
|
||||||
|
defaultValue?: any; // => extraProps.defaultValue
|
||||||
|
initialValue?: any | ((value: any, defaultValue: any) => any); // => initials.initialValue
|
||||||
|
initial?: (value: any, defaultValue: any) => any; // => initials.initialValue
|
||||||
|
|
||||||
|
display?: DISPLAY_TYPE; // => fieldExtraProps
|
||||||
|
fieldStyle?: DISPLAY_TYPE; // => fieldExtraProps
|
||||||
|
setter?: ComponentClass | ISetterConfig[] | string | SetterGetter; // =>
|
||||||
|
supportVariable?: boolean; // => use MixedSetter
|
||||||
|
/**
|
||||||
|
* the prop should be collapsed while display value is accordion
|
||||||
|
*/
|
||||||
|
collapse?: boolean; // => extraProps.defaultCollapsed
|
||||||
|
/**
|
||||||
|
* alias to collapse
|
||||||
|
*/
|
||||||
|
collapsed?: boolean; // => extraProps.defaultCollapsed
|
||||||
|
fieldCollapsed?: boolean; // => extraProps.defaultCollapsed
|
||||||
|
/**
|
||||||
|
* if a prop is declared as disabled, it will not be saved into
|
||||||
|
* schema
|
||||||
|
*/
|
||||||
|
disabled?: boolean | ReturnBooleanFunction; // => hide & virtual ? thinkof global transform
|
||||||
|
/**
|
||||||
|
* will not export data to schema
|
||||||
|
*/
|
||||||
|
ignore?: boolean | ReturnBooleanFunction; // => ?virtualProp ? thinkof global transform
|
||||||
|
hidden?: boolean | ReturnBooleanFunction; // => condition
|
||||||
|
|
||||||
|
/**
|
||||||
|
* when use getValue(), accessor shall be called as initializer
|
||||||
|
*/
|
||||||
|
accessor?(this: Field, value: any): any; // => getValue
|
||||||
|
/**
|
||||||
|
* when current prop value mutate, the mutator function shall be called
|
||||||
|
*/
|
||||||
|
mutator?( // => setValue
|
||||||
|
this: Field,
|
||||||
|
value: any,
|
||||||
|
hotValue: any,
|
||||||
|
): /*
|
||||||
|
preValue: any, // => x
|
||||||
|
preHotValue: any, // => x
|
||||||
|
*/
|
||||||
|
void;
|
||||||
|
/**
|
||||||
|
* other values' change will trigger sync function here
|
||||||
|
*/
|
||||||
|
sync?(this: Field, value: any): void; // => autorun
|
||||||
|
/**
|
||||||
|
* user click var to change current field to
|
||||||
|
* variable setting field
|
||||||
|
*/
|
||||||
|
useVariableChange?(this: Field, data: { isUseVariable: boolean }): any; // => as MixinSetter param
|
||||||
|
|
||||||
|
slotName?: string;
|
||||||
|
slotTitle?: string;
|
||||||
|
initialChildren?: any; // schema
|
||||||
|
allowTextInput: boolean;
|
||||||
|
liveTextEditing?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
// from vision 5.4
|
||||||
|
export interface OldPrototypeConfig {
|
||||||
|
packageName: string; // => npm.package
|
||||||
|
/**
|
||||||
|
* category display in the component pane
|
||||||
|
* component will be hidden while the value is: null
|
||||||
|
*/
|
||||||
|
category: string; // => tags
|
||||||
|
componentName: string; // =>
|
||||||
|
docUrl?: string; // =>
|
||||||
|
defaultProps?: any; // => ?
|
||||||
|
/**
|
||||||
|
* extra actions on the outline of current selected node
|
||||||
|
* by default we have: remove / clone
|
||||||
|
*/
|
||||||
|
extraActions?: Array<ComponentType<any> | ReactElement> | (() => ReactElement); // => configure.component.actions
|
||||||
|
title?: string; // =>
|
||||||
|
icon?: ComponentType<any> | ReactElement; // =>
|
||||||
|
view: ComponentType; // => ?
|
||||||
|
initialChildren?: (props: any) => any[]; // => snippets
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Props configurations of node
|
||||||
|
*/
|
||||||
|
configure: OldPropConfig[]; // => configure.props
|
||||||
|
snippets?: any[]; // => snippets
|
||||||
|
transducers?: any; // => ?
|
||||||
|
/**
|
||||||
|
* Selector expression rectangle of a node, it is usually a querySelector string
|
||||||
|
* @example '.classname > div'
|
||||||
|
*/
|
||||||
|
rectSelector?: string; // => configure.component.rectSelector
|
||||||
|
context?: {
|
||||||
|
// => ?
|
||||||
|
[contextInfoName: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
isContainer?: boolean; // => configure.component.isContainer
|
||||||
|
isAbsoluteLayoutContainer?: boolean; // => meta.experimental.isAbsoluteLayoutContainer 是否是绝对定位容器
|
||||||
|
isModal?: boolean; // => configure.component.isModal
|
||||||
|
isFloating?: boolean; // => configure.component.isFloating
|
||||||
|
descriptor?: string; // => configure.component.descriptor
|
||||||
|
|
||||||
|
// alias to canDragging
|
||||||
|
canDraging?: boolean; // => onDrag
|
||||||
|
canDragging?: boolean; // => ?
|
||||||
|
|
||||||
|
canOperating?: boolean; // => disabledActions
|
||||||
|
canUseCondition?: boolean;
|
||||||
|
canLoop?: boolean;
|
||||||
|
canContain?: (dragment: Node) => boolean; // => nestingRule
|
||||||
|
|
||||||
|
canDropTo?: ((container: Node) => boolean) | boolean | string | string[]; // => nestingRule
|
||||||
|
canDropto?: ((container: Node) => boolean) | boolean | string | string[]; // => 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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* when sub-node of the current node changed
|
||||||
|
* including: sub-node insert / remove
|
||||||
|
*/
|
||||||
|
subtreeModified?(this: Node): any; // => ? hooks
|
||||||
|
|
||||||
|
// => ?
|
||||||
|
canResizing?: ((dragment: any, 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;
|
||||||
|
devMode?: string;
|
||||||
|
schema?: ProjectSchema;
|
||||||
|
isTopFixed?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ISetterConfig {
|
||||||
|
setter?: ComponentClass;
|
||||||
|
// use value to decide whether this setter is available
|
||||||
|
condition?: (value: any) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetterGetter = (this: Field, value: any) => ComponentClass;
|
||||||
|
|
||||||
|
type ReturnBooleanFunction = (this: Field, value: any) => boolean;
|
||||||
|
|
||||||
|
export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollector) {
|
||||||
|
const {
|
||||||
|
type,
|
||||||
|
name,
|
||||||
|
title,
|
||||||
|
tip,
|
||||||
|
slotName,
|
||||||
|
slotTitle,
|
||||||
|
initialChildren,
|
||||||
|
allowTextInput,
|
||||||
|
initialValue,
|
||||||
|
defaultValue,
|
||||||
|
display,
|
||||||
|
fieldStyle,
|
||||||
|
collapse,
|
||||||
|
collapsed,
|
||||||
|
fieldCollapsed,
|
||||||
|
hidden,
|
||||||
|
disabled,
|
||||||
|
items,
|
||||||
|
ignore,
|
||||||
|
initial,
|
||||||
|
sync,
|
||||||
|
accessor,
|
||||||
|
mutator,
|
||||||
|
setter,
|
||||||
|
useVariableChange,
|
||||||
|
supportVariable,
|
||||||
|
liveTextEditing,
|
||||||
|
} = config;
|
||||||
|
|
||||||
|
const extraProps: any = {
|
||||||
|
display: DISPLAY_TYPE.BLOCK,
|
||||||
|
};
|
||||||
|
const newConfig: any = {
|
||||||
|
type: type === 'group' ? 'group' : 'field',
|
||||||
|
name: type === 'group' && !name ? uniqueId('group') : name,
|
||||||
|
title,
|
||||||
|
extraProps,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (tip) {
|
||||||
|
if (typeof title !== 'object' || isI18nData(title) || isValidElement(title)) {
|
||||||
|
newConfig.title = {
|
||||||
|
label: title,
|
||||||
|
tip: tip.content,
|
||||||
|
docUrl: tip.url,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
newConfig.title = {
|
||||||
|
...(title as any),
|
||||||
|
tip: tip.content,
|
||||||
|
docUrl: tip.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display || fieldStyle) {
|
||||||
|
extraProps.display = display || fieldStyle;
|
||||||
|
if (extraProps.display === DISPLAY_TYPE.TAB) {
|
||||||
|
extraProps.display = DISPLAY_TYPE.ACCORDION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collapse || collapsed || fieldCollapsed || extraProps.display === DISPLAY_TYPE.ENTRY) {
|
||||||
|
extraProps.defaultCollapsed = true;
|
||||||
|
}
|
||||||
|
function isDisabled(field: Field) {
|
||||||
|
if (typeof disabled === 'function') {
|
||||||
|
return disabled.call(field, field.getValue()) === true;
|
||||||
|
}
|
||||||
|
return disabled === true;
|
||||||
|
}
|
||||||
|
function isHidden(field: Field) {
|
||||||
|
if (typeof hidden === 'function') {
|
||||||
|
return hidden.call(field, field.getValue()) === true;
|
||||||
|
}
|
||||||
|
return hidden === true;
|
||||||
|
}
|
||||||
|
if (extraProps.display === DISPLAY_TYPE.NONE) {
|
||||||
|
extraProps.display = undefined;
|
||||||
|
extraProps.condition = () => false;
|
||||||
|
} else if (hidden != null || disabled != null) {
|
||||||
|
extraProps.condition = (field: Field) => !(isHidden(field) || isDisabled(field));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'group') {
|
||||||
|
newConfig.items = items ? upgradeConfigure(items, collector) : [];
|
||||||
|
return newConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaultValue !== undefined) {
|
||||||
|
extraProps.defaultValue = defaultValue;
|
||||||
|
} else if (typeof initialValue !== 'function') {
|
||||||
|
extraProps.defaultValue = initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let initialFn = (slotName ? null : initial) || initialValue;
|
||||||
|
if (slotName && initialValue === true) {
|
||||||
|
initialFn = (value: any, defaultValue: any) => {
|
||||||
|
if (isJSBlock(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 'JSSlot',
|
||||||
|
title: slotTitle || title,
|
||||||
|
name: slotName,
|
||||||
|
value: initialChildren,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!slotName) {
|
||||||
|
if (accessor) {
|
||||||
|
extraProps.getValue = (field: Field, fieldValue: any) => {
|
||||||
|
return accessor.call(field, fieldValue);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sync || accessor) {
|
||||||
|
collector.addAutorun({
|
||||||
|
name,
|
||||||
|
autorun: (field: Field) => {
|
||||||
|
let fieldValue = untracked(() => field.getValue());
|
||||||
|
if (accessor) {
|
||||||
|
fieldValue = accessor.call(field, fieldValue);
|
||||||
|
}
|
||||||
|
if (sync) {
|
||||||
|
fieldValue = sync.call(field, fieldValue);
|
||||||
|
if (fieldValue !== undefined) {
|
||||||
|
field.setValue(fieldValue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
field.setValue(fieldValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mutator) {
|
||||||
|
extraProps.setValue = (field: Field, value: any) => {
|
||||||
|
// TODO: 兼容代码,不触发查询组件的 Mutator
|
||||||
|
if (field instanceof SettingField && field.componentMeta?.componentName === 'Filter') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mutator.call(field, value, value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const setterInitial = getInitialFromSetter(setter);
|
||||||
|
|
||||||
|
if (type !== 'composite') {
|
||||||
|
collector.addInitial({
|
||||||
|
// FIXME! name could be "xxx.xxx"
|
||||||
|
name: slotName || name,
|
||||||
|
initial: (field: Field, currentValue: any) => {
|
||||||
|
// FIXME! read from prototype.defaultProps
|
||||||
|
const defaults = extraProps.defaultValue;
|
||||||
|
|
||||||
|
if (typeof initialFn !== 'function') {
|
||||||
|
initialFn = defaultInitial;
|
||||||
|
}
|
||||||
|
|
||||||
|
const v = initialFn.call(field, currentValue, defaults);
|
||||||
|
|
||||||
|
if (setterInitial) {
|
||||||
|
return setterInitial.call(field, v, defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ignore != null || disabled != null) {
|
||||||
|
collector.addFilter({
|
||||||
|
// FIXME! name should be "xxx.xxx"
|
||||||
|
name: slotName || name,
|
||||||
|
filter: (field: Field, currentValue: any) => {
|
||||||
|
let disabledValue: boolean;
|
||||||
|
if (typeof disabled === 'function') {
|
||||||
|
disabledValue = disabled.call(field, currentValue) === true;
|
||||||
|
} else {
|
||||||
|
disabledValue = disabled === true;
|
||||||
|
}
|
||||||
|
if (disabledValue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (typeof ignore === 'function') {
|
||||||
|
return ignore.call(field, currentValue) !== true;
|
||||||
|
}
|
||||||
|
return ignore !== true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slotName) {
|
||||||
|
newConfig.name = slotName;
|
||||||
|
if (!newConfig.title && slotTitle) {
|
||||||
|
newConfig.title = slotTitle;
|
||||||
|
}
|
||||||
|
const setters: any[] = [
|
||||||
|
{
|
||||||
|
componentName: 'SlotSetter',
|
||||||
|
initialValue: (field: any, value: any) => {
|
||||||
|
if (isJSSlot(value)) {
|
||||||
|
return {
|
||||||
|
title: slotTitle || title,
|
||||||
|
...value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 'JSSlot',
|
||||||
|
title: slotTitle || title,
|
||||||
|
name: slotName,
|
||||||
|
value: value == null ? initialChildren : value,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (allowTextInput) {
|
||||||
|
setters.unshift('I18nSetter');
|
||||||
|
}
|
||||||
|
if (supportVariable) {
|
||||||
|
setters.push('VariableSetter');
|
||||||
|
}
|
||||||
|
newConfig.setter = setters.length > 1 ? setters : setters[0];
|
||||||
|
|
||||||
|
return newConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
let primarySetter: any;
|
||||||
|
if (type === 'composite') {
|
||||||
|
const initials: InitialItem[] = [];
|
||||||
|
const objItems = items
|
||||||
|
? upgradeConfigure(items,
|
||||||
|
{
|
||||||
|
addInitial: (item) => {
|
||||||
|
initials.push(item);
|
||||||
|
},
|
||||||
|
addFilter: (item) => {
|
||||||
|
collector.addFilter({
|
||||||
|
name: `${name}.${item.name}`,
|
||||||
|
filter: item.filter,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addAutorun: (item) => {
|
||||||
|
collector.addAutorun({
|
||||||
|
name: `${name}.${item.name}`,
|
||||||
|
autorun: item.autorun,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
newConfig.items = objItems;
|
||||||
|
|
||||||
|
const initial = (target: SettingTarget, value?: any) => {
|
||||||
|
// TODO:
|
||||||
|
const defaults = extraProps.defaultValue;
|
||||||
|
const data: any = {};
|
||||||
|
initials.forEach((item) => {
|
||||||
|
// FIXME! Target may be a wrong
|
||||||
|
data[item.name] = item.initial(target, isPlainObject(value) ? value[item.name] : null);
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
collector.addInitial({
|
||||||
|
name,
|
||||||
|
initial,
|
||||||
|
});
|
||||||
|
primarySetter = {
|
||||||
|
componentName: 'ObjectSetter',
|
||||||
|
props: {
|
||||||
|
config: {
|
||||||
|
items: objItems,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
initialValue: (field: Field) => {
|
||||||
|
return initial(field, field.getValue());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (setter) {
|
||||||
|
if (Array.isArray(setter)) {
|
||||||
|
// FIXME! read initial from setter
|
||||||
|
primarySetter = setter.map(({ setter, condition }) => {
|
||||||
|
return {
|
||||||
|
componentName: setter,
|
||||||
|
condition: condition
|
||||||
|
? (field: Field) => {
|
||||||
|
return condition.call(field, field.getValue());
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
primarySetter = setter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!primarySetter) {
|
||||||
|
primarySetter = 'I18nSetter';
|
||||||
|
}
|
||||||
|
if (supportVariable) {
|
||||||
|
const setters = Array.isArray(primarySetter)
|
||||||
|
? primarySetter.concat('VariableSetter')
|
||||||
|
: [primarySetter, 'VariableSetter'];
|
||||||
|
primarySetter = {
|
||||||
|
componentName: 'MixedSetter',
|
||||||
|
props: {
|
||||||
|
setters,
|
||||||
|
onSetterChange: (field: Field, name: string) => {
|
||||||
|
if (useVariableChange) {
|
||||||
|
useVariableChange.call(field, { isUseVariable: name === 'VariableSetter' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
newConfig.setter = primarySetter;
|
||||||
|
|
||||||
|
if (liveTextEditing) {
|
||||||
|
extraProps.liveTextEditing = liveTextEditing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddInitial = (initialItem: InitialItem) => void;
|
||||||
|
type AddFilter = (filterItem: FilterItem) => void;
|
||||||
|
type AddAutorun = (autorunItem: AutorunItem) => void;
|
||||||
|
|
||||||
|
type ConfigCollector = {
|
||||||
|
addInitial: AddInitial;
|
||||||
|
addFilter: AddFilter;
|
||||||
|
addAutorun: AddAutorun;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getInitialFromSetter(setter: any) {
|
||||||
|
return setter && (
|
||||||
|
setter.initial || setter.Initial
|
||||||
|
|| (setter.type && (setter.type.initial || setter.type.Initial))
|
||||||
|
) || null; // eslint-disable-line
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultInitial(value: any, defaultValue: any) {
|
||||||
|
return value == null ? defaultValue : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function upgradeConfigure(items: OldPropConfig[], collector: ConfigCollector) {
|
||||||
|
const configure: any[] = [];
|
||||||
|
let ignoreSlotName: any = null;
|
||||||
|
items.forEach((config) => {
|
||||||
|
if (config.slotName) {
|
||||||
|
ignoreSlotName = config.slotName;
|
||||||
|
} else if (ignoreSlotName) {
|
||||||
|
if (config.name === ignoreSlotName) {
|
||||||
|
ignoreSlotName = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ignoreSlotName = null;
|
||||||
|
}
|
||||||
|
configure.push(upgradePropConfig(config, collector));
|
||||||
|
});
|
||||||
|
return configure;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function upgradeActions(actions?: Array<ComponentType<any> | ReactElement> | (() => ReactElement)) {
|
||||||
|
if (!actions) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!Array.isArray(actions)) {
|
||||||
|
actions = [actions];
|
||||||
|
}
|
||||||
|
return actions.map((content) => {
|
||||||
|
const type: any = isValidElement(content) ? content.type : content;
|
||||||
|
if (typeof content === 'function') {
|
||||||
|
const fn = content as () => ReactElement;
|
||||||
|
content = (({ node }: any) => {
|
||||||
|
return fn.call(node);
|
||||||
|
}) as any;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name: type.displayName || type.name || 'anonymous',
|
||||||
|
content,
|
||||||
|
important: true,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 升级
|
||||||
|
*/
|
||||||
|
export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||||
|
const {
|
||||||
|
componentName,
|
||||||
|
docUrl,
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
packageName,
|
||||||
|
category,
|
||||||
|
extraActions,
|
||||||
|
defaultProps,
|
||||||
|
initialChildren,
|
||||||
|
snippets,
|
||||||
|
view,
|
||||||
|
configure,
|
||||||
|
transducers,
|
||||||
|
isContainer,
|
||||||
|
isAbsoluteLayoutContainer,
|
||||||
|
rectSelector,
|
||||||
|
isModal,
|
||||||
|
isFloating,
|
||||||
|
descriptor,
|
||||||
|
context,
|
||||||
|
canOperating,
|
||||||
|
canContain,
|
||||||
|
canDropTo,
|
||||||
|
canDropto,
|
||||||
|
canDropIn,
|
||||||
|
canDroping,
|
||||||
|
canUseCondition,
|
||||||
|
canLoop,
|
||||||
|
|
||||||
|
// hooks
|
||||||
|
canDraging,
|
||||||
|
canDragging, // handleDragging
|
||||||
|
// events
|
||||||
|
didDropOut, // onNodeRemove
|
||||||
|
didDropIn, // onNodeAdd
|
||||||
|
subtreeModified, // onSubtreeModified
|
||||||
|
|
||||||
|
canResizing, // resizing
|
||||||
|
onResizeStart, // onResizeStart
|
||||||
|
onResize, // onResize
|
||||||
|
onResizeEnd, // onResizeEnd
|
||||||
|
devMode,
|
||||||
|
schema,
|
||||||
|
isTopFixed,
|
||||||
|
} = oldConfig;
|
||||||
|
|
||||||
|
const meta: any = {
|
||||||
|
componentName,
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
docUrl,
|
||||||
|
devMode: devMode || 'procode',
|
||||||
|
schema: schema?.componentsTree[0],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (category) {
|
||||||
|
meta.tags = [category];
|
||||||
|
}
|
||||||
|
if (packageName) {
|
||||||
|
meta.npm = {
|
||||||
|
componentName,
|
||||||
|
package: packageName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const component: any = {
|
||||||
|
isContainer,
|
||||||
|
rootSelector: rectSelector,
|
||||||
|
isModal,
|
||||||
|
isFloating,
|
||||||
|
descriptor,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (canOperating === false) {
|
||||||
|
component.disableBehaviors = '*';
|
||||||
|
}
|
||||||
|
if (extraActions) {
|
||||||
|
component.actions = upgradeActions(extraActions);
|
||||||
|
}
|
||||||
|
const nestingRule: any = {};
|
||||||
|
if (canContain) {
|
||||||
|
nestingRule.descendantWhitelist = canContain;
|
||||||
|
}
|
||||||
|
if (canDropTo != null || canDropto != null) {
|
||||||
|
if (canDropTo === false || canDropto === false) {
|
||||||
|
nestingRule.parentWhitelist = () => false;
|
||||||
|
}
|
||||||
|
if (canDropTo !== true && canDropto !== true) {
|
||||||
|
nestingRule.parentWhitelist = canDropTo || canDropto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 未考虑清楚的,放在实验性段落
|
||||||
|
const experimental: any = {
|
||||||
|
isAbsoluteLayoutContainer,
|
||||||
|
};
|
||||||
|
if (context) {
|
||||||
|
// for prototype.getContextInfo
|
||||||
|
experimental.context = context;
|
||||||
|
}
|
||||||
|
if (snippets) {
|
||||||
|
experimental.snippets = snippets.map((data) => {
|
||||||
|
const { schema = {} } = data;
|
||||||
|
if (!schema.children && initialChildren && typeof initialChildren !== 'function') {
|
||||||
|
schema.children = initialChildren;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
schema,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// FIXME! defaultProps for initial input
|
||||||
|
// initialChildren maybe a function
|
||||||
|
else if (defaultProps || initialChildren) {
|
||||||
|
const snippet = {
|
||||||
|
screenshot: icon,
|
||||||
|
label: title,
|
||||||
|
schema: {
|
||||||
|
componentName,
|
||||||
|
props: defaultProps,
|
||||||
|
children: initialChildren,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (experimental.snippets) {
|
||||||
|
experimental.snippets.push(snippet);
|
||||||
|
} else {
|
||||||
|
experimental.snippets = [snippet];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (initialChildren) {
|
||||||
|
experimental.initialChildren =
|
||||||
|
typeof initialChildren === 'function'
|
||||||
|
? (node: any) => {
|
||||||
|
return initialChildren.call(node, node.settingEntry);
|
||||||
|
}
|
||||||
|
: initialChildren;
|
||||||
|
}
|
||||||
|
if (view) {
|
||||||
|
experimental.view = view;
|
||||||
|
}
|
||||||
|
if (isTopFixed) {
|
||||||
|
experimental.isTopFixed = isTopFixed;
|
||||||
|
}
|
||||||
|
if (transducers) {
|
||||||
|
// Array<{ toStatic, toNative }>
|
||||||
|
// ? only twice
|
||||||
|
experimental.transducers = transducers;
|
||||||
|
}
|
||||||
|
if (canResizing) {
|
||||||
|
// TODO: enhance
|
||||||
|
experimental.getResizingHandlers = (currentNode: any) => {
|
||||||
|
const directs = ['n', 'w', 's', 'e'];
|
||||||
|
if (canResizing === true) {
|
||||||
|
return directs;
|
||||||
|
}
|
||||||
|
return directs.filter((d) => canResizing(currentNode, d));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const callbacks: any = {};
|
||||||
|
if (canDragging != null || canDraging != null) {
|
||||||
|
let v = true;
|
||||||
|
if (canDragging === false || canDraging === false) {
|
||||||
|
v = false;
|
||||||
|
}
|
||||||
|
callbacks.onMoveHook = () => v;
|
||||||
|
}
|
||||||
|
if (didDropIn) {
|
||||||
|
callbacks.onNodeAdd = didDropIn;
|
||||||
|
}
|
||||||
|
if (didDropOut) {
|
||||||
|
callbacks.onNodeRemove = didDropOut;
|
||||||
|
}
|
||||||
|
if (subtreeModified) {
|
||||||
|
callbacks.onSubtreeModified = subtreeModified;
|
||||||
|
}
|
||||||
|
if (onResize) {
|
||||||
|
callbacks.onResize = (e: any, currentNode: any) => {
|
||||||
|
// todo: what is trigger?
|
||||||
|
const { trigger, deltaX, deltaY } = e;
|
||||||
|
onResize(e, trigger, currentNode, deltaX, deltaY);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (onResizeStart) {
|
||||||
|
callbacks.onResizeStart = (e: any, currentNode: any) => {
|
||||||
|
// todo: what is trigger?
|
||||||
|
const { trigger } = e;
|
||||||
|
onResizeStart(e, trigger, currentNode);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (onResizeEnd) {
|
||||||
|
callbacks.onResizeEnd = (e: any, currentNode: any) => {
|
||||||
|
// todo: what is trigger?
|
||||||
|
const { trigger } = e;
|
||||||
|
onResizeEnd(e, trigger, currentNode);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
experimental.callbacks = callbacks;
|
||||||
|
|
||||||
|
const initials: InitialItem[] = [];
|
||||||
|
const filters: FilterItem[] = [];
|
||||||
|
const autoruns: AutorunItem[] = [];
|
||||||
|
const props = upgradeConfigure(configure || [],
|
||||||
|
{
|
||||||
|
addInitial: (item) => {
|
||||||
|
initials.push(item);
|
||||||
|
},
|
||||||
|
addFilter: (item) => {
|
||||||
|
filters.push(item);
|
||||||
|
},
|
||||||
|
addAutorun: (item) => {
|
||||||
|
autoruns.push(item);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
experimental.initials = initials;
|
||||||
|
experimental.filters = filters;
|
||||||
|
experimental.autoruns = autoruns;
|
||||||
|
|
||||||
|
const supports: any = {};
|
||||||
|
if (canUseCondition != null) {
|
||||||
|
supports.condition = canUseCondition;
|
||||||
|
}
|
||||||
|
if (canLoop != null) {
|
||||||
|
supports.loop = canLoop;
|
||||||
|
}
|
||||||
|
meta.configure = { props, component, supports };
|
||||||
|
meta.experimental = experimental;
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
83
packages/vision-polyfill/src/bus.ts
Normal file
83
packages/vision-polyfill/src/bus.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import logger from '@ali/vu-logger';
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import { editor } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bus class as an EventEmitter
|
||||||
|
*/
|
||||||
|
export 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bus = new Bus();
|
||||||
|
|
||||||
|
editor?.on('hotkey.callback.call', (data) => {
|
||||||
|
bus.emit('ve.hotkey.callback.call', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
editor?.on('history.back', (data) => {
|
||||||
|
bus.emit('ve.history.back', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
editor?.on('history.forward', (data) => {
|
||||||
|
bus.emit('ve.history.forward', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
editor?.on('node.prop.change', (data) => {
|
||||||
|
bus.emit('node.prop.change', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default bus;
|
||||||
82
packages/vision-polyfill/src/components/index.less
Normal file
82
packages/vision-polyfill/src/components/index.less
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
@import '~@ali/ve-less-variables/index.less';
|
||||||
|
|
||||||
|
// 样式直接沿用之前的样式,优化了下命名
|
||||||
|
.instance-node-selector {
|
||||||
|
position: relative;
|
||||||
|
margin-right: 2px;
|
||||||
|
color: var(--color-icon-white, @title-bgcolor);
|
||||||
|
border-radius: @global-border-radius;
|
||||||
|
margin-right: 2px;
|
||||||
|
pointer-events: auto;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-right: 5px;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
max-width: inherit;
|
||||||
|
path {
|
||||||
|
fill: var(--color-icon-white, @title-bgcolor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-current {
|
||||||
|
background: var(--color-brand, @brand-color-1);
|
||||||
|
padding: 0 6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--color-icon-white, @title-bgcolor);
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
padding-right: 6px;
|
||||||
|
color: var(--color-icon-white, @title-bgcolor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-list {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
&-node {
|
||||||
|
margin: 2px 0;
|
||||||
|
&-content {
|
||||||
|
padding-left: 6px;
|
||||||
|
background: #78869a;
|
||||||
|
display: inline-flex;
|
||||||
|
border-radius: 3px;
|
||||||
|
align-items: center;
|
||||||
|
height: 20px;
|
||||||
|
color: var(--color-icon-white, @title-bgcolor);
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
&-title {
|
||||||
|
padding-right: 6px;
|
||||||
|
// margin-left: 5px;
|
||||||
|
color: var(--color-icon-white, @title-bgcolor);
|
||||||
|
cursor: pointer;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.instance-node-selector-current {
|
||||||
|
color: ar(--color-text-reverse, @white-alpha-2);
|
||||||
|
}
|
||||||
|
.instance-node-selector-popup {
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
|
transition: 0.2s all ease-in;
|
||||||
|
}
|
||||||
|
}
|
||||||
113
packages/vision-polyfill/src/components/index.tsx
Normal file
113
packages/vision-polyfill/src/components/index.tsx
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { Overlay } from '@alifd/next';
|
||||||
|
import React from 'react';
|
||||||
|
import { Node, ParentalNode } from '@ali/lowcode-designer';
|
||||||
|
import { editorHelper } from '@ali/lowcode-engine';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
|
const { Popup } = Overlay;
|
||||||
|
const { Title } = editorHelper;
|
||||||
|
|
||||||
|
export interface IProps {
|
||||||
|
node: Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IState {
|
||||||
|
parentNodes: Node[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnionNode = Node | ParentalNode | null;
|
||||||
|
|
||||||
|
export class InstanceNodeSelector extends React.Component<IProps, IState> {
|
||||||
|
state: IState = {
|
||||||
|
parentNodes: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const parentNodes = this.getParentNodes(this.props.node);
|
||||||
|
this.setState({
|
||||||
|
parentNodes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取节点的父级节点(最多获取5层)
|
||||||
|
getParentNodes = (node: Node) => {
|
||||||
|
const parentNodes = [];
|
||||||
|
let currentNode: UnionNode = node;
|
||||||
|
|
||||||
|
while (currentNode && parentNodes.length < 5) {
|
||||||
|
currentNode = currentNode.getParent();
|
||||||
|
if (currentNode) {
|
||||||
|
parentNodes.push(currentNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parentNodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
onSelect = (node: Node) => () => {
|
||||||
|
if (node && typeof node.select === 'function') {
|
||||||
|
node.select();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMouseOver = (node: Node) => (_: any, flag = true) => {
|
||||||
|
if (node && typeof node.hover === 'function') {
|
||||||
|
node.hover(flag);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMouseOut = (node: Node) => (_: any, flag = false) => {
|
||||||
|
if (node && typeof node.hover === 'function') {
|
||||||
|
node.hover(flag);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
renderNodes = (node: Node) => {
|
||||||
|
const nodes = this.state.parentNodes || [];
|
||||||
|
const children = nodes.map((node, key) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={key}
|
||||||
|
onClick={this.onSelect(node)}
|
||||||
|
onMouseEnter={this.onMouseOver(node)}
|
||||||
|
onMouseLeave={this.onMouseOut(node)}
|
||||||
|
className="instance-node-selector-node"
|
||||||
|
>
|
||||||
|
<div className="instance-node-selector-node-content">
|
||||||
|
<Title
|
||||||
|
className="instance-node-selector-node-title"
|
||||||
|
title={{
|
||||||
|
label: node.title,
|
||||||
|
icon: node.icon,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return children;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { node } = this.props;
|
||||||
|
return (
|
||||||
|
<div className="instance-node-selector">
|
||||||
|
<Popup
|
||||||
|
trigger={
|
||||||
|
<div className="instance-node-selector-current">
|
||||||
|
<Title
|
||||||
|
className="instance-node-selector-node-title"
|
||||||
|
title={{
|
||||||
|
label: node.title,
|
||||||
|
icon: node.icon,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
triggerType="hover"
|
||||||
|
>
|
||||||
|
<div className="instance-node-selector">{this.renderNodes(node)}</div>
|
||||||
|
</Popup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
113
packages/vision-polyfill/src/context.ts
Normal file
113
packages/vision-polyfill/src/context.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { assign } from 'lodash';
|
||||||
|
|
||||||
|
import { Component, ReactElement } from 'react';
|
||||||
|
import VisualManager from './base/visualManager';
|
||||||
|
import Prototype from './bundle/prototype';
|
||||||
|
import { VE_HOOKS } from './base/const';
|
||||||
|
import { setters } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
// TODO: Env 本地引入后需要兼容方法 getDesignerLocale
|
||||||
|
// import Env from './env';
|
||||||
|
|
||||||
|
const { registerSetter } = setters;
|
||||||
|
// prop is Prop object in Designer
|
||||||
|
export type SetterProvider = (prop: any, componentPrototype: Prototype) => Component | ReactElement<any>;
|
||||||
|
|
||||||
|
export class VisualEngineContext {
|
||||||
|
private managerMap: { [name: string]: VisualManager } = {};
|
||||||
|
|
||||||
|
private moduleMap: { [name: string]: any } = {};
|
||||||
|
|
||||||
|
private pluginsMap: { [name: string]: any } = {};
|
||||||
|
|
||||||
|
use(pluginName: string, plugin: any) {
|
||||||
|
this.pluginsMap[pluginName || 'unknown'] = plugin;
|
||||||
|
if (pluginName === VE_HOOKS.VE_SETTING_FIELD_VARIABLE_SETTER) {
|
||||||
|
registerSetter('VariableSetter', {
|
||||||
|
component: plugin,
|
||||||
|
title: { type: 'i18n', 'zh-CN': '变量绑定', 'en-US': 'Variable Binding' },
|
||||||
|
// TODO: add logic below
|
||||||
|
// initialValue?: any | ((field: any) => any);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlugin(name: string) {
|
||||||
|
if (!name) {
|
||||||
|
name = 'default';
|
||||||
|
}
|
||||||
|
if (this.pluginsMap[name]) {
|
||||||
|
return this.pluginsMap[name];
|
||||||
|
} else if (this.moduleMap[name]) {
|
||||||
|
return this.moduleMap[name];
|
||||||
|
}
|
||||||
|
return this.getManager(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
registerManager(managerMap?: { [name: string]: VisualManager }): this;
|
||||||
|
|
||||||
|
registerManager(name: string, manager: VisualManager): this;
|
||||||
|
|
||||||
|
registerManager(name?: any, manager?: VisualManager): this {
|
||||||
|
if (name && typeof name === 'object') {
|
||||||
|
this.managerMap = assign(this.managerMap, name);
|
||||||
|
} else {
|
||||||
|
this.managerMap[name] = manager as VisualManager;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
registerModule(moduleMap: { [name: string]: any }): this;
|
||||||
|
|
||||||
|
registerModule(name: string, module: any): this;
|
||||||
|
|
||||||
|
registerModule(name?: any, module?: any): this {
|
||||||
|
if (typeof name === 'object') {
|
||||||
|
this.moduleMap = Object.assign({}, this.moduleMap, name);
|
||||||
|
} else {
|
||||||
|
this.moduleMap[name] = module;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getManager(name: string): VisualManager {
|
||||||
|
return this.managerMap[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
getModule(name: string): any {
|
||||||
|
return this.moduleMap[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDesignerLocale(): string {
|
||||||
|
// return Env.getLocale();
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builtin APIs
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* support dynamic setter replacement
|
||||||
|
*/
|
||||||
|
registerDynamicSetterProvider(setterProvider: SetterProvider) {
|
||||||
|
if (!setterProvider) {
|
||||||
|
console.error('ERROR: ', 'please set provider function.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.use('ve.plugin.setterProvider', setterProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* support add treePane on the setting pane
|
||||||
|
* @param treePane see @ali/ve-tree-pane
|
||||||
|
* @param treeCore see @ali/ve-tree-pane
|
||||||
|
*/
|
||||||
|
registerTreePane(TreePane: Component, TreeCore: Component) {
|
||||||
|
if (TreePane && TreeCore) {
|
||||||
|
this.registerModule('TreePane', TreePane);
|
||||||
|
this.registerModule('TreeCore', TreeCore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new VisualEngineContext();
|
||||||
58
packages/vision-polyfill/src/deep-value-parser.ts
Normal file
58
packages/vision-polyfill/src/deep-value-parser.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import Env from './env';
|
||||||
|
import { isJSSlot, isI18nData, isJSExpression } from '@ali/lowcode-types';
|
||||||
|
import { isPlainObject } from '@ali/lowcode-utils';
|
||||||
|
import i18nUtil from './i18n-util';
|
||||||
|
import { editor } from '@ali/lowcode-engine';
|
||||||
|
import { isVariable } from './utils';
|
||||||
|
|
||||||
|
// FIXME: 表达式使用 mock 值,未来live 模式直接使用原始值
|
||||||
|
// TODO: designType
|
||||||
|
export function deepValueParser(obj?: any): any {
|
||||||
|
if (isJSExpression(obj)) {
|
||||||
|
if (editor.get('designMode') === 'live') {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
obj = obj.mock;
|
||||||
|
}
|
||||||
|
// 兼容 ListSetter 中的变量结构
|
||||||
|
if (isVariable(obj)) {
|
||||||
|
if (editor.get('designMode') === 'live') {
|
||||||
|
return {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: obj.variable,
|
||||||
|
mock: obj.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
obj = obj.value;
|
||||||
|
}
|
||||||
|
if (!obj) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
return obj.map((item) => deepValueParser(item));
|
||||||
|
}
|
||||||
|
if (isPlainObject(obj)) {
|
||||||
|
if (isI18nData(obj)) {
|
||||||
|
// FIXME! use editor.get
|
||||||
|
let locale = Env.getLocale();
|
||||||
|
if (obj.key) {
|
||||||
|
// FIXME: 此处需要升级I18nUtil,改成响应式
|
||||||
|
return i18nUtil.get(obj.key, locale);
|
||||||
|
}
|
||||||
|
if (locale !== 'zh_CN' && locale !== 'zh_TW' && !obj[locale]) {
|
||||||
|
locale = 'en_US';
|
||||||
|
}
|
||||||
|
return obj[obj.use || locale] || obj.zh_CN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isJSSlot(obj)) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
const out: any = {};
|
||||||
|
Object.keys(obj).forEach((key) => {
|
||||||
|
out[key] = deepValueParser(obj[key]);
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
59
packages/vision-polyfill/src/drag-engine.ts
Normal file
59
packages/vision-polyfill/src/drag-engine.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { designer, designerHelper } from '@ali/lowcode-engine';
|
||||||
|
import { isPrototype } from './bundle/prototype';
|
||||||
|
|
||||||
|
const { DragObjectType, isNode, isDragNodeDataObject } = designerHelper;
|
||||||
|
const { dragon } = designer;
|
||||||
|
const DragEngine = {
|
||||||
|
from(shell: Element, boost: (e: MouseEvent) => any): any {
|
||||||
|
return dragon.from(shell, (e) => {
|
||||||
|
const r = boost(e);
|
||||||
|
if (!r) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (isPrototype(r)) {
|
||||||
|
return {
|
||||||
|
type: DragObjectType.NodeData,
|
||||||
|
data: {
|
||||||
|
componentName: r.getComponentName(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else if (isNode(r)) {
|
||||||
|
return {
|
||||||
|
type: DragObjectType.Node,
|
||||||
|
nodes: [r],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
type: DragObjectType.NodeData,
|
||||||
|
data: r,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onDragstart(func: (e: any, dragment: any) => any) {
|
||||||
|
return dragon.onDragstart((evt) => {
|
||||||
|
func(evt.originalEvent, evt.dragObject.nodes[0]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onDrag(func: (e: any, dragment: any, location: any) => any) {
|
||||||
|
return dragon.onDrag((evt) => {
|
||||||
|
const loc = designer.currentDocument?.dropLocation;
|
||||||
|
func(evt.originalEvent, evt.dragObject.nodes[0], loc);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onDragend(func: (dragment: any, location: any, copy: any) => any) {
|
||||||
|
return dragon.onDragend(({ dragObject, copy }) => {
|
||||||
|
const loc = designer.currentDocument?.dropLocation;
|
||||||
|
if (isDragNodeDataObject(dragObject)) {
|
||||||
|
func(dragObject.data, loc, copy);
|
||||||
|
} else {
|
||||||
|
func(dragObject.nodes[0], loc, copy);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
inDragging() {
|
||||||
|
return dragon.dragging;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DragEngine;
|
||||||
95
packages/vision-polyfill/src/env.ts
Normal file
95
packages/vision-polyfill/src/env.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import { ALI_SCHEMA_VERSION } from './base/const';
|
||||||
|
import { editorHelper } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
const { obx } = editorHelper;
|
||||||
|
|
||||||
|
interface ILiteralObject {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Env {
|
||||||
|
@obx.val envs: ILiteralObject = {};
|
||||||
|
|
||||||
|
private emitter: EventEmitter;
|
||||||
|
|
||||||
|
private featureMap: ILiteralObject;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.emitter = new EventEmitter();
|
||||||
|
this.emitter.setMaxListeners(0);
|
||||||
|
this.featureMap = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
get(name: string): any {
|
||||||
|
return this.getEnv(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getEnv(name: string): any {
|
||||||
|
return this.envs[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
set(name: string, value: any) {
|
||||||
|
return this.setEnv(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
setEnvMap(envs: ILiteralObject): void {
|
||||||
|
this.envs = Object.assign(this.envs, envs);
|
||||||
|
this.emitter.emit('envchange', this.envs);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLocale(): string {
|
||||||
|
return this.getEnv('locale') || 'zh_CN';
|
||||||
|
}
|
||||||
|
|
||||||
|
setLocale(locale: string) {
|
||||||
|
this.setEnv('locale', locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
setExpertMode(flag: string) {
|
||||||
|
this.setEnv('expertMode', !!flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
isExpertMode() {
|
||||||
|
return !!this.getEnv('expertMode');
|
||||||
|
}
|
||||||
|
|
||||||
|
getSupportFeatures() {
|
||||||
|
return Object.assign({}, this.featureMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
setSupportFeatures(features: ILiteralObject) {
|
||||||
|
this.featureMap = Object.assign({}, this.featureMap, features);
|
||||||
|
}
|
||||||
|
|
||||||
|
supports(name = 'supports') {
|
||||||
|
return !!this.featureMap[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnvChange(func: (envs: ILiteralObject, name: string, value: any) => any) {
|
||||||
|
this.emitter.on('envchange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('envchange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.envs = {};
|
||||||
|
this.featureMap = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
getAliSchemaVersion() {
|
||||||
|
return ALI_SCHEMA_VERSION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Env();
|
||||||
24
packages/vision-polyfill/src/exchange.ts
Normal file
24
packages/vision-polyfill/src/exchange.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Node } from '@ali/lowcode-designer';
|
||||||
|
import { designer } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
select: (node: Node) => {
|
||||||
|
if (!node) {
|
||||||
|
return designer.currentSelection?.clear();
|
||||||
|
}
|
||||||
|
designer.currentSelection?.select(node.id);
|
||||||
|
},
|
||||||
|
getSelected: () => {
|
||||||
|
const nodes = designer.currentSelection?.getNodes();
|
||||||
|
return nodes?.[0];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* TODO dirty fix
|
||||||
|
*/
|
||||||
|
onIntoView(func: (node: any, insertion: any) => any) {
|
||||||
|
// this.emitter.on('intoview', func);
|
||||||
|
return () => {
|
||||||
|
// this.emitter.removeListener('intoview', func);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
151
packages/vision-polyfill/src/fields/field.tsx
Normal file
151
packages/vision-polyfill/src/fields/field.tsx
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import classnames from 'classnames';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Component } from 'react';
|
||||||
|
import InlineTip from './inlinetip';
|
||||||
|
import { isPlainObject } from '@ali/lowcode-utils';
|
||||||
|
|
||||||
|
interface IHelpTip {
|
||||||
|
url?: string;
|
||||||
|
content?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitWord(title: string): JSX.Element[] {
|
||||||
|
return (title || '').split('').map((w, i) => <b key={`word${i}`} className="engine-word">{w}</b>);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFieldTitle(title: string, tip: IHelpTip, compact?: boolean, propName?: string): JSX.Element {
|
||||||
|
const className = classnames('engine-field-title', { 've-compact': compact });
|
||||||
|
let titleContent = null;
|
||||||
|
|
||||||
|
if (!compact && typeof title === 'string') {
|
||||||
|
titleContent = splitWord(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tipUrl = null;
|
||||||
|
let tipContent = null;
|
||||||
|
|
||||||
|
tipContent = (
|
||||||
|
<div>
|
||||||
|
<div>属性:{propName}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isPlainObject(tip)) {
|
||||||
|
tipUrl = tip.url;
|
||||||
|
tipContent = (
|
||||||
|
<div>
|
||||||
|
<div>属性:{propName}</div>
|
||||||
|
<div>说明:{tip.content}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (tip) {
|
||||||
|
tipContent = (
|
||||||
|
<div>
|
||||||
|
<div>属性:{propName}</div>
|
||||||
|
<div>说明:{tip}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
className={className}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
href={tipUrl!}
|
||||||
|
>
|
||||||
|
{titleContent || (typeof title === 'object' ? '' : title)}
|
||||||
|
<InlineTip position="top">{tipContent}</InlineTip>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IVEFieldProps {
|
||||||
|
prop: any;
|
||||||
|
children: JSX.Element | string;
|
||||||
|
title?: string;
|
||||||
|
tip?: any;
|
||||||
|
propName?: string;
|
||||||
|
className?: string;
|
||||||
|
compact?: boolean;
|
||||||
|
stageName?: string;
|
||||||
|
/**
|
||||||
|
* render the top-header by jsx
|
||||||
|
*/
|
||||||
|
headDIY?: boolean;
|
||||||
|
|
||||||
|
isSupportVariable?: boolean;
|
||||||
|
isSupportMultiSetter?: boolean;
|
||||||
|
isUseVariable?: boolean;
|
||||||
|
|
||||||
|
isGroup?: boolean;
|
||||||
|
isExpand?: boolean;
|
||||||
|
|
||||||
|
toggleExpand?: () => any;
|
||||||
|
onExpandChange?: (fn: () => any) => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IVEFieldState {
|
||||||
|
hasError?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class VEField extends Component<IVEFieldProps, IVEFieldState> {
|
||||||
|
public static displayName = 'VEField';
|
||||||
|
|
||||||
|
public readonly props: IVEFieldProps;
|
||||||
|
|
||||||
|
public classNames: string[] = [];
|
||||||
|
|
||||||
|
public state: IVEFieldState = {
|
||||||
|
hasError: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
public componentDidCatch(error: Error, info: React.ErrorInfo) {
|
||||||
|
console.error(error);
|
||||||
|
console.warn(info.componentStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderHead(): JSX.Element | JSX.Element[] | null {
|
||||||
|
const { title, tip, compact, propName } = this.props;
|
||||||
|
return getFieldTitle(title!, tip, compact, propName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderBody(): JSX.Element | string {
|
||||||
|
return this.props.children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderFoot(): any {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
const { stageName, headDIY } = this.props;
|
||||||
|
const classNameList = classnames(...this.classNames, this.props.className);
|
||||||
|
const fieldProps: any = {};
|
||||||
|
|
||||||
|
if (stageName) {
|
||||||
|
// 为 stage 切换奠定基础
|
||||||
|
fieldProps['data-stage-target'] = this.props.stageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.hasError) {
|
||||||
|
return (
|
||||||
|
<div>Field render error, please open console to find out.</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headContent = headDIY ? this.renderHead()
|
||||||
|
: <div className="engine-field-head">{this.renderHead()}</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNameList} {...fieldProps}>
|
||||||
|
{headContent}
|
||||||
|
<div className="engine-field-body">
|
||||||
|
{this.renderBody()}
|
||||||
|
</div>
|
||||||
|
<div className="engine-field-foot">
|
||||||
|
{this.renderFoot()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
272
packages/vision-polyfill/src/fields/fields.less
Normal file
272
packages/vision-polyfill/src/fields/fields.less
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
@import '~@ali/ve-less-variables/index.less';
|
||||||
|
|
||||||
|
.engine-setting-field {
|
||||||
|
white-space: nowrap;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:after, &:before {
|
||||||
|
content: " ";
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-field-title {
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: @font-family;
|
||||||
|
line-height: 1em;
|
||||||
|
user-select: none;
|
||||||
|
color: var(--color-text, @dark-alpha-3);
|
||||||
|
width: fit-content;
|
||||||
|
white-space: initial;
|
||||||
|
word-break: break-word;
|
||||||
|
&::first-letter {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-word {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: normal;
|
||||||
|
&:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
&:last-of-type {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
&:only-of-type {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.engine-field-title {
|
||||||
|
border-bottom: 1px dashed var(--color-line-normal, @normal-alpha-7);
|
||||||
|
text-decoration: none;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
&:hover {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-field-variable-wrapper {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-field-variable {
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.6;
|
||||||
|
&.engine-active {
|
||||||
|
opacity: 1;
|
||||||
|
color: var(--color-brand, @brand-color-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-field-head {
|
||||||
|
padding-left: 10px;
|
||||||
|
height: 32px;
|
||||||
|
background: var(--color-block-background-shallow, @normal-alpha-8);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 500;
|
||||||
|
border-top: 1px solid var(--color-line-normal, @normal-alpha-7);
|
||||||
|
border-bottom: 1px solid var(--color-line-normal, @normal-alpha-7);
|
||||||
|
color: var(--color-title, @dark-alpha-2);
|
||||||
|
>.engine-icontip {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-field-body {
|
||||||
|
min-height: 20px;
|
||||||
|
margin: 6px 0;
|
||||||
|
|
||||||
|
&:after, &:before {
|
||||||
|
content: " ";
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-field-head {
|
||||||
|
height: 28px;
|
||||||
|
border: none;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.engine-plain-field {
|
||||||
|
>.engine-field-variable {
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
>.engine-field-variable {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.engine-entry-field {
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 32px;
|
||||||
|
padding-left: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
border-top: 1px solid var(--color-line-normal, @normal-alpha-7);
|
||||||
|
border-bottom: 1px solid var(--color-line-normal, @normal-alpha-7);
|
||||||
|
background: var(--color-block-background-shallow, @normal-alpha-8);
|
||||||
|
margin-bottom: 6px;
|
||||||
|
|
||||||
|
>.engine-field-title {
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.engine-icontip {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.engine-field-arrow {
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%) rotate(-90deg);
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
>.engine-field-arrow {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.engine-popup-field {
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 32px;
|
||||||
|
padding-left: 10px;
|
||||||
|
background: var(--color-block-background-shallow, @normal-alpha-8);
|
||||||
|
margin-bottom: 1px;
|
||||||
|
|
||||||
|
>.engine-field-title {
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.engine-icontip {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.engine-field-icon {
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
>.engine-field-icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.engine-block-field {
|
||||||
|
>.engine-field-head{
|
||||||
|
> .engine-field-title {
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
>.engine-field-variable {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>.engine-field-body {
|
||||||
|
margin: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.engine-inline-field {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 10px;
|
||||||
|
>.engine-field-head {
|
||||||
|
display: inline-flex;
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
>.engine-field-title {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 50px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>.engine-field-body {
|
||||||
|
width: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
>.engine-field-variable {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
>.engine-field-variable {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.engine-accordion-field {
|
||||||
|
>.engine-field-head {
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
>.engine-field-title {
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
>.engine-field-arrow {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
position: absolute;
|
||||||
|
right: 7px;
|
||||||
|
top: 7px;
|
||||||
|
transition: transform 0.1s ease;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
>.engine-field-variable {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.engine-collapsed {
|
||||||
|
>.engine-field-head {
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
>.engine-field-head > .engine-field-arrow {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
>.engine-field-body {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>.engine-field-body {
|
||||||
|
margin: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-block-field,.engine-accordion-field,.engine-entry-field {
|
||||||
|
.engine-input-control {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-field-tip-icon {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
379
packages/vision-polyfill/src/fields/fields.tsx
Normal file
379
packages/vision-polyfill/src/fields/fields.tsx
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
import Icons from '@ali/ve-icons';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { Component } from 'react';
|
||||||
|
import { testType } from '@ali/ve-utils';
|
||||||
|
import VEField, { IVEFieldProps } from './field';
|
||||||
|
import { SettingField } from './settingField';
|
||||||
|
import VariableSwitcher from './variableSwitcher';
|
||||||
|
import popups from '@ali/ve-popups';
|
||||||
|
|
||||||
|
import './fields.less';
|
||||||
|
|
||||||
|
interface IHelpTip {
|
||||||
|
url?: string;
|
||||||
|
content?: string | JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderTip(tip: IHelpTip, prop?: { propName?: string }) {
|
||||||
|
const propName = prop && prop.propName;
|
||||||
|
if (!tip) {
|
||||||
|
return (
|
||||||
|
<Icons.Tip position="top" key="icon" className="engine-field-tip-icon">
|
||||||
|
<div>
|
||||||
|
<div>{propName}</div>
|
||||||
|
</div>
|
||||||
|
</Icons.Tip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (testType(tip) === 'object') {
|
||||||
|
return (
|
||||||
|
<Icons.Tip position="top" url={tip.url} key="icon-tip" className="engine-field-tip-icon">
|
||||||
|
<div>
|
||||||
|
<div>属性:{propName}</div>
|
||||||
|
<div>说明:{tip.content}</div>
|
||||||
|
</div>
|
||||||
|
</Icons.Tip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Icons.Tip position="top" key="icon" className="engine-field-tip-icon">
|
||||||
|
<div>
|
||||||
|
<div>属性:{propName}</div>
|
||||||
|
<div>说明:{tip}</div>
|
||||||
|
</div>
|
||||||
|
</Icons.Tip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PlainField extends VEField {
|
||||||
|
public static defaultProps = {
|
||||||
|
headDIY: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static displayName = 'PlainField';
|
||||||
|
|
||||||
|
public renderHead(): null {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InlineField extends VEField {
|
||||||
|
public static displayName = 'InlineField';
|
||||||
|
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
this.classNames = ['engine-setting-field', 'engine-inline-field'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderFoot() {
|
||||||
|
return (
|
||||||
|
<div className="engine-field-variable-wrapper">
|
||||||
|
<VariableSwitcher {...this.props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BlockField extends VEField {
|
||||||
|
public static displayName = 'BlockField';
|
||||||
|
|
||||||
|
constructor(props: IVEFieldProps) {
|
||||||
|
super(props);
|
||||||
|
this.classNames = ['engine-setting-field', 'engine-block-field', props.isGroup ? 'engine-group-field' : ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderHead() {
|
||||||
|
const { title, tip, propName } = this.props;
|
||||||
|
return [
|
||||||
|
<span className="engine-field-title" key={title}>
|
||||||
|
{title}
|
||||||
|
</span>,
|
||||||
|
renderTip(tip, { propName }),
|
||||||
|
<VariableSwitcher {...this.props} />,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AccordionField extends VEField {
|
||||||
|
public readonly props: IVEFieldProps;
|
||||||
|
|
||||||
|
private willDetach?: () => any;
|
||||||
|
|
||||||
|
constructor(props: IVEFieldProps) {
|
||||||
|
super(props);
|
||||||
|
this._generateClassNames(props);
|
||||||
|
if (this.props.onExpandChange) {
|
||||||
|
this.willDetach = this.props.onExpandChange(() => this.forceUpdate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillReceiveProps(nextProps: IVEFieldProps) {
|
||||||
|
this.classNames = this._generateClassNames(nextProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
if (this.willDetach) {
|
||||||
|
this.willDetach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderHead() {
|
||||||
|
const { title, tip, toggleExpand, propName } = this.props;
|
||||||
|
return (
|
||||||
|
<div className="engine-field-head" onClick={() => toggleExpand && toggleExpand()}>
|
||||||
|
<Icons name="arrow" className="engine-field-arrow" size="12px" />
|
||||||
|
<span className="engine-field-title">{title}</span>
|
||||||
|
{renderTip(tip, { propName })}
|
||||||
|
{<VariableSwitcher {...this.props} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _generateClassNames(props: IVEFieldProps) {
|
||||||
|
this.classNames = [
|
||||||
|
'engine-setting-field',
|
||||||
|
'engine-accordion-field',
|
||||||
|
props.isGroup ? 'engine-group-field' : '',
|
||||||
|
!props.isExpand ? 'engine-collapsed' : '',
|
||||||
|
];
|
||||||
|
return this.classNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EntryField extends VEField {
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
this.classNames = ['engine-setting-field', 'engine-entry-field'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { propName, stageName, tip, title } = this.props;
|
||||||
|
const classNameList = classNames(...this.classNames, this.props.className);
|
||||||
|
const fieldProps: any = {};
|
||||||
|
|
||||||
|
if (stageName) {
|
||||||
|
// 为 stage 切换奠定基础
|
||||||
|
fieldProps['data-stage-target'] = this.props.stageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const innerElements = [
|
||||||
|
<span className="engine-field-title" key="field-title">
|
||||||
|
{title}
|
||||||
|
</span>,
|
||||||
|
renderTip(tip, { propName }),
|
||||||
|
<Icons name="arrow" className="engine-field-arrow" size="12px" key="engine-field-arrow-icon" />,
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNameList} {...fieldProps}>
|
||||||
|
{innerElements}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PopupField extends VEField {
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
this.classNames = ['engine-setting-field', 'engine-popup-field'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderBody() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { propName, stageName, tip, title } = this.props;
|
||||||
|
const classNameList = classNames(...this.classNames, this.props.className);
|
||||||
|
const fieldProps: any = {};
|
||||||
|
|
||||||
|
if (stageName) {
|
||||||
|
// 为 stage 切换奠定基础
|
||||||
|
fieldProps['data-stage-target'] = this.props.stageName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNameList}
|
||||||
|
onClick={(e) => popups.popup({
|
||||||
|
cancelOnBlur: true,
|
||||||
|
content: this.props.children,
|
||||||
|
position: 'left bottom',
|
||||||
|
showClose: true,
|
||||||
|
sizeFixed: true,
|
||||||
|
target: e.currentTarget,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className="engine-field-title">{title}</span>
|
||||||
|
{renderTip(tip, { propName })}
|
||||||
|
<VariableSwitcher {...this.props} />
|
||||||
|
<Icons name="popup" className="engine-field-icon" size="medium" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CaptionField extends VEField {
|
||||||
|
constructor(props: IVEFieldProps) {
|
||||||
|
super(props);
|
||||||
|
this.classNames = ['engine-setting-field', 'engine-caption-field'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderHead() {
|
||||||
|
const { title, tip, propName } = this.props;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<span className="engine-field-title">{title}</span>
|
||||||
|
{renderTip(tip, { propName })}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Stage extends Component {
|
||||||
|
public readonly props: {
|
||||||
|
key: any;
|
||||||
|
stage: any;
|
||||||
|
current?: boolean;
|
||||||
|
direction?: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
public stage: any;
|
||||||
|
|
||||||
|
public additionClassName: string;
|
||||||
|
|
||||||
|
public shell: Element | null = null;
|
||||||
|
|
||||||
|
private willDetach: () => any;
|
||||||
|
|
||||||
|
public componentWillMount() {
|
||||||
|
this.stage = this.props.stage;
|
||||||
|
if (this.stage.onCurrentTabChange) {
|
||||||
|
this.willDetach = this.stage.onCurrentTabChange(() => this.forceUpdate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
this.doSkate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillReceiveProps(props: any) {
|
||||||
|
if (props.stage !== this.stage) {
|
||||||
|
this.stage = props.stage;
|
||||||
|
if (this.willDetach) {
|
||||||
|
this.willDetach();
|
||||||
|
}
|
||||||
|
if (this.stage.onCurrentTabChange) {
|
||||||
|
this.willDetach = this.stage.onCurrentTabChange(() => this.forceUpdate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidUpdate() {
|
||||||
|
this.doSkate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
if (this.willDetach) {
|
||||||
|
this.willDetach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public doSkate() {
|
||||||
|
if (this.additionClassName) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const elem = this.shell;
|
||||||
|
if (elem && elem.classList) {
|
||||||
|
if (this.props.current) {
|
||||||
|
elem.classList.remove(this.additionClassName);
|
||||||
|
} else {
|
||||||
|
elem.classList.add(this.additionClassName);
|
||||||
|
}
|
||||||
|
this.additionClassName = '';
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { stage } = this;
|
||||||
|
let content = null;
|
||||||
|
let tabs = null;
|
||||||
|
|
||||||
|
let className = 'engine-settings-stage';
|
||||||
|
|
||||||
|
if (stage.getTabs) {
|
||||||
|
const selected = stage.getNode();
|
||||||
|
// stat for cache
|
||||||
|
stage.stat();
|
||||||
|
const currentTab = stage.getCurrentTab();
|
||||||
|
|
||||||
|
if (stage.hasTabs()) {
|
||||||
|
className += ' engine-has-tabs';
|
||||||
|
tabs = (
|
||||||
|
<div className="engine-settings-tabs">
|
||||||
|
{stage.getTabs().map((tab: any) => (
|
||||||
|
<div
|
||||||
|
key={tab.getId()}
|
||||||
|
className={`engine-settings-tab${tab === currentTab ? ' engine-active' : ''}`}
|
||||||
|
onClick={() => stage.setCurrentTab(tab)}
|
||||||
|
>
|
||||||
|
{tab.getTitle()}
|
||||||
|
{renderTip(tab.getTip())}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentTab) {
|
||||||
|
if (currentTab.getVisibleItems) {
|
||||||
|
content = currentTab
|
||||||
|
.getVisibleItems()
|
||||||
|
.map((item: any) => <SettingField key={item.getId()} selected={selected} prop={item} />);
|
||||||
|
} else if (currentTab.getSetter) {
|
||||||
|
content = (
|
||||||
|
<SettingField key={currentTab.getId()} selected={selected} prop={currentTab} forceDisplay="plain" />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
content = stage.getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.current) {
|
||||||
|
if (this.props.direction) {
|
||||||
|
this.additionClassName = `engine-stagein-${this.props.direction}`;
|
||||||
|
className += ` ${this.additionClassName}`;
|
||||||
|
}
|
||||||
|
} else if (this.props.direction) {
|
||||||
|
this.additionClassName = `engine-stageout-${this.props.direction}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let stageBacker = null;
|
||||||
|
if (stage.hasBack()) {
|
||||||
|
className += ' engine-has-backer';
|
||||||
|
stageBacker = (
|
||||||
|
<div className="engine-settings-stagebacker" data-stage-target="stageback">
|
||||||
|
<Icons name="arrow" className="engine-field-arrow" size="12px" />
|
||||||
|
<span className="engine-field-title">{stage.getTitle()}</span>
|
||||||
|
{renderTip(stage.getTip())}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={(ref) => {
|
||||||
|
this.shell = ref;
|
||||||
|
}}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
{stageBacker}
|
||||||
|
{tabs}
|
||||||
|
<div className="engine-stage-content">{content}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
packages/vision-polyfill/src/fields/index.ts
Normal file
2
packages/vision-polyfill/src/fields/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './settingField';
|
||||||
|
export * from './fields';
|
||||||
30
packages/vision-polyfill/src/fields/inlinetip.tsx
Normal file
30
packages/vision-polyfill/src/fields/inlinetip.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Component } from 'react';
|
||||||
|
|
||||||
|
export interface InlineTipProps {
|
||||||
|
position: string;
|
||||||
|
theme?: 'green' | 'black';
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class InlineTip extends Component<InlineTipProps> {
|
||||||
|
public static displayName = 'InlineTip';
|
||||||
|
|
||||||
|
public static defaultProps = {
|
||||||
|
position: 'auto',
|
||||||
|
theme: 'black',
|
||||||
|
};
|
||||||
|
|
||||||
|
public render(): React.ReactNode {
|
||||||
|
const { position, theme, children } = this.props;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
data-role="tip"
|
||||||
|
data-position={position}
|
||||||
|
data-theme={theme}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
189
packages/vision-polyfill/src/fields/settingField.tsx
Normal file
189
packages/vision-polyfill/src/fields/settingField.tsx
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
import VariableSetter from './variableSetter';
|
||||||
|
import context from '../context';
|
||||||
|
import { VE_HOOKS } from '../base/const';
|
||||||
|
import {
|
||||||
|
AccordionField,
|
||||||
|
BlockField,
|
||||||
|
EntryField,
|
||||||
|
InlineField,
|
||||||
|
PlainField,
|
||||||
|
PopupField,
|
||||||
|
} from './fields';
|
||||||
|
|
||||||
|
import { ComponentClass, Component, isValidElement, createElement } from 'react';
|
||||||
|
import { editorHelper, setters } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
const { getSetter } = setters;
|
||||||
|
const { createSetterContent } = editorHelper;
|
||||||
|
|
||||||
|
function isReactClass(obj: any): obj is ComponentClass<any> {
|
||||||
|
return (
|
||||||
|
obj &&
|
||||||
|
obj.prototype &&
|
||||||
|
(obj.prototype.isReactComponent || obj.prototype instanceof Component)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IExtraProps {
|
||||||
|
stageName?: string;
|
||||||
|
isGroup?: boolean;
|
||||||
|
isExpand?: boolean;
|
||||||
|
propName?: string;
|
||||||
|
toggleExpand?: () => any;
|
||||||
|
onExpandChange?: () => any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FIELD_TYPE_MAP: any = {
|
||||||
|
accordion: AccordionField,
|
||||||
|
block: BlockField,
|
||||||
|
entry: EntryField,
|
||||||
|
inline: InlineField,
|
||||||
|
plain: PlainField,
|
||||||
|
popup: PopupField,
|
||||||
|
tab: AccordionField,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class SettingField extends Component {
|
||||||
|
public readonly props: {
|
||||||
|
prop: any;
|
||||||
|
selected?: boolean;
|
||||||
|
forceDisplay?: string;
|
||||||
|
className?: string;
|
||||||
|
children?: JSX.Element | string;
|
||||||
|
compact?: boolean;
|
||||||
|
key?: string;
|
||||||
|
addonProps?: object;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VariableSetter placeholder
|
||||||
|
*/
|
||||||
|
public variableSetter: any;
|
||||||
|
|
||||||
|
constructor(props: any) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.variableSetter = getSetter('VariableSetter')?.component || VariableSetter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { prop, selected, addonProps } = this.props;
|
||||||
|
const display = this.props.forceDisplay || prop.getDisplay();
|
||||||
|
|
||||||
|
if (display === 'none') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标准的属性,即每一个 Field 在 VE 下都拥有的属性
|
||||||
|
const standardProps = {
|
||||||
|
className: this.props.className,
|
||||||
|
compact: this.props.compact,
|
||||||
|
|
||||||
|
isSupportMultiSetter: this.supportMultiSetter(),
|
||||||
|
isSupportVariable: prop.isSupportVariable(),
|
||||||
|
isUseVariable: prop.isUseVariable(),
|
||||||
|
prop,
|
||||||
|
setUseVariable: () => prop.setUseVariable(!prop.isUseVariable()),
|
||||||
|
tip: prop.getTip(),
|
||||||
|
title: prop.getTitle(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 部分 Field 所需要的额外 fieldProps
|
||||||
|
const extraProps = {};
|
||||||
|
const ctx = context;
|
||||||
|
const plugin = ctx.getPlugin(VE_HOOKS.VE_SETTING_FIELD_PROVIDER);
|
||||||
|
let Field;
|
||||||
|
if (typeof plugin === 'function') {
|
||||||
|
Field = plugin(display, FIELD_TYPE_MAP, prop);
|
||||||
|
}
|
||||||
|
if (!Field) {
|
||||||
|
Field = FIELD_TYPE_MAP[display] || PlainField;
|
||||||
|
}
|
||||||
|
this._prepareProps(display, extraProps);
|
||||||
|
|
||||||
|
if (display === 'entry') {
|
||||||
|
return <Field {...{ ...standardProps, ...extraProps }} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
let setter;
|
||||||
|
const props: any = {
|
||||||
|
prop,
|
||||||
|
selected,
|
||||||
|
};
|
||||||
|
const fieldProps = { ...standardProps, ...extraProps };
|
||||||
|
|
||||||
|
if (prop.isUseVariable() && !this.variableSetter.isPopup) {
|
||||||
|
props.placeholder = '请输入表达式: ${var}';
|
||||||
|
props.key = `${prop.getId()}-variable`;
|
||||||
|
setter = createElement(this.variableSetter, props);
|
||||||
|
return <Field {...fieldProps}>{setter}</Field>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for composited prop
|
||||||
|
if (prop.getVisibleItems) {
|
||||||
|
setter = prop
|
||||||
|
.getVisibleItems()
|
||||||
|
.map((item: any) => (
|
||||||
|
<SettingField {...{ key: item.getId(), prop: item, selected }} />
|
||||||
|
));
|
||||||
|
return <Field {...fieldProps}>{setter}</Field>;
|
||||||
|
}
|
||||||
|
|
||||||
|
setter = prop.getSetter();
|
||||||
|
if (
|
||||||
|
typeof setter === 'object' &&
|
||||||
|
'componentName' in setter &&
|
||||||
|
!(isValidElement(setter) || isReactClass(setter))
|
||||||
|
) {
|
||||||
|
const { componentName: setterType, props: setterProps } = setter as any;
|
||||||
|
setter = createSetterContent(setterType, {
|
||||||
|
...addonProps,
|
||||||
|
...setterProps,
|
||||||
|
...props,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setter = createSetterContent(setter, {
|
||||||
|
...addonProps,
|
||||||
|
...props,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Field {...fieldProps}>{setter}</Field>;
|
||||||
|
}
|
||||||
|
|
||||||
|
private supportMultiSetter() {
|
||||||
|
const { prop } = this.props;
|
||||||
|
const setter = prop && prop.getConfig && prop.getConfig('setter');
|
||||||
|
return prop.isSupportVariable() || Array.isArray(setter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _prepareProps(displayType: string, extraProps: IExtraProps): void {
|
||||||
|
const { prop } = this.props;
|
||||||
|
extraProps.propName = prop.isGroup()
|
||||||
|
? '组合属性,无属性名称'
|
||||||
|
: prop.getName();
|
||||||
|
switch (displayType) {
|
||||||
|
case 'title':
|
||||||
|
break;
|
||||||
|
case 'block':
|
||||||
|
Object.assign(extraProps, { isGroup: prop.isGroup() });
|
||||||
|
break;
|
||||||
|
case 'accordion':
|
||||||
|
Object.assign(extraProps, {
|
||||||
|
headDIY: true,
|
||||||
|
isExpand: prop.isExpand(),
|
||||||
|
isGroup: prop.isGroup(),
|
||||||
|
onExpandChange: () => prop.onExpandChange(() => this.forceUpdate()),
|
||||||
|
toggleExpand: () => {
|
||||||
|
prop.toggleExpand();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'entry':
|
||||||
|
Object.assign(extraProps, { stageName: prop.getName() });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
packages/vision-polyfill/src/fields/variableSetter.less
Normal file
43
packages/vision-polyfill/src/fields/variableSetter.less
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
@import '~@ali/ve-less-variables/index.less';
|
||||||
|
|
||||||
|
.engine-input-control {
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 12px;
|
||||||
|
font-family: Consolas, "Courier New", Courier, FreeMono, monospace;
|
||||||
|
color: var(--color-text, @dark-alpha-3);
|
||||||
|
background: var(--color-field-background, @white-alpha-1);
|
||||||
|
border: 1px solid var(--color-field-border, @normal-alpha-5);
|
||||||
|
flex: 1;
|
||||||
|
border-radius: @global-border-radius;
|
||||||
|
max-height: 200px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--color-field-border-hover, @normal-alpha-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.engine-focused {
|
||||||
|
border-color: var(--color-field-border-active, @normal-alpha-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
>.engine-input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 6px;
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
color: var(--color-text, @dark-alpha-3);
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
background: transparent;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&::-webkit-input-placeholder {
|
||||||
|
color: var(--color-field-placeholder, @normal-alpha-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
packages/vision-polyfill/src/fields/variableSetter.tsx
Normal file
86
packages/vision-polyfill/src/fields/variableSetter.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import './variableSetter.less';
|
||||||
|
import { Component } from 'react';
|
||||||
|
|
||||||
|
class Input extends Component {
|
||||||
|
public props: {
|
||||||
|
value: string;
|
||||||
|
placeholder: string;
|
||||||
|
onChange: (val: any) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
public state: { focused: boolean };
|
||||||
|
|
||||||
|
constructor(props: object) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
focused: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
this.adjustTextAreaHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
private domRef: HTMLTextAreaElement | null = null;
|
||||||
|
|
||||||
|
public adjustTextAreaHeight() {
|
||||||
|
if (!this.domRef) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.domRef.style.height = '1px';
|
||||||
|
const calculatedHeight = this.domRef.scrollHeight;
|
||||||
|
this.domRef.style.height = calculatedHeight >= 200 ? '200px' : `${calculatedHeight }px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { value, placeholder, onChange } = this.props;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`engine-variable-setter-input engine-input-control${this.state.focused ? ' engine-focused' : ''}`}
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
ref={(r) => {
|
||||||
|
this.domRef = r;
|
||||||
|
}}
|
||||||
|
className="engine-input"
|
||||||
|
value={value || ''}
|
||||||
|
placeholder={placeholder || ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
onChange(e.target.value || '');
|
||||||
|
}}
|
||||||
|
onBlur={() => this.setState({ focused: false })}
|
||||||
|
onFocus={() => this.setState({ focused: true })}
|
||||||
|
onKeyUp={this.adjustTextAreaHeight.bind(this)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class VariableSetter extends Component<{
|
||||||
|
prop: any;
|
||||||
|
placeholder: string;
|
||||||
|
}> {
|
||||||
|
public willDetach: () => any;
|
||||||
|
|
||||||
|
public componentWillMount() {
|
||||||
|
this.willDetach = this.props.prop.onValueChange(() => this.forceUpdate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
if (this.willDetach) {
|
||||||
|
this.willDetach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { prop } = this.props;
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
value={prop.getVariableValue()}
|
||||||
|
placeholder={this.props.placeholder}
|
||||||
|
onChange={(val: string) => prop.setVariableValue(val)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
packages/vision-polyfill/src/fields/variableSwitcher.less
Normal file
20
packages/vision-polyfill/src/fields/variableSwitcher.less
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
@import '~@ali/ve-less-variables/index.less';
|
||||||
|
|
||||||
|
.engine-field-variable-switcher {
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.6;
|
||||||
|
margin-left: 2px;
|
||||||
|
|
||||||
|
&.engine-active {
|
||||||
|
opacity: 1;
|
||||||
|
background: var(--color-brand, @brand-color-1);
|
||||||
|
color: #fff !important;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin-left: 4px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
height: 22px !important;
|
||||||
|
width: 22px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
packages/vision-polyfill/src/fields/variableSwitcher.tsx
Normal file
61
packages/vision-polyfill/src/fields/variableSwitcher.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import VariableSetter from './variableSetter';
|
||||||
|
import Icons from '@ali/ve-icons';
|
||||||
|
import { IVEFieldProps } from './field';
|
||||||
|
import './variableSwitcher.less';
|
||||||
|
import { Component } from 'react';
|
||||||
|
import { setters } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
const { getSetter } = setters;
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
visible: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class VariableSwitcher extends Component<IVEFieldProps, IState> {
|
||||||
|
private ref: HTMLElement | null = null;
|
||||||
|
|
||||||
|
private VariableSetter: any;
|
||||||
|
|
||||||
|
constructor(props: IVEFieldProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.VariableSetter = getSetter('VariableSetter')?.component || VariableSetter;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
visible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { isUseVariable, prop } = this.props;
|
||||||
|
const { visible } = this.state;
|
||||||
|
const isSupportVariable = prop.isSupportVariable();
|
||||||
|
const tip = !isUseVariable ? '绑定变量' : prop.getVariableValue();
|
||||||
|
if (!isSupportVariable) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Icons.Tip
|
||||||
|
name="var"
|
||||||
|
size="24px"
|
||||||
|
position="bottom center"
|
||||||
|
className={`engine-field-variable-switcher ${isUseVariable ? 'engine-active' : ''}`}
|
||||||
|
data-tip={tip}
|
||||||
|
onClick={(e: Event) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (this.VariableSetter.isPopup) {
|
||||||
|
this.VariableSetter.show({
|
||||||
|
prop,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
prop.setUseVariable(!isUseVariable);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
绑定变量
|
||||||
|
</Icons.Tip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
152
packages/vision-polyfill/src/flags.ts
Normal file
152
packages/vision-polyfill/src/flags.ts
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import domReady from 'domready';
|
||||||
|
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
|
const Shells = ['iphone6'];
|
||||||
|
|
||||||
|
export class Flags {
|
||||||
|
public emitter: EventEmitter;
|
||||||
|
|
||||||
|
public flags: string[];
|
||||||
|
|
||||||
|
public ready: boolean;
|
||||||
|
|
||||||
|
public lastFlags: string[];
|
||||||
|
|
||||||
|
public lastShell: string;
|
||||||
|
|
||||||
|
private lastSimulatorDevice: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.emitter = new EventEmitter();
|
||||||
|
this.flags = [];
|
||||||
|
|
||||||
|
domReady(() => {
|
||||||
|
this.ready = true;
|
||||||
|
this.applyFlags();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public setDragMode(flag: boolean) {
|
||||||
|
if (flag) {
|
||||||
|
this.add('drag-mode');
|
||||||
|
} else {
|
||||||
|
this.remove('drag-mode');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setPreviewMode(flag: boolean) {
|
||||||
|
if (flag) {
|
||||||
|
this.add('preview-mode');
|
||||||
|
this.remove('design-mode');
|
||||||
|
} else {
|
||||||
|
this.add('design-mode');
|
||||||
|
this.remove('preview-mode');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setWithShell(shell: string) {
|
||||||
|
if (shell === this.lastShell) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.lastShell) {
|
||||||
|
this.remove(`with-${this.lastShell}shell`);
|
||||||
|
}
|
||||||
|
if (shell) {
|
||||||
|
if (Shells.indexOf(shell) < 0) {
|
||||||
|
shell = Shells[0];
|
||||||
|
}
|
||||||
|
this.add(`with-${shell}shell`);
|
||||||
|
this.lastShell = shell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSimulator(device: string) {
|
||||||
|
if (this.lastSimulatorDevice) {
|
||||||
|
this.remove(`simulator-${this.lastSimulatorDevice}`);
|
||||||
|
}
|
||||||
|
if (device !== '' && device !== 'pc') {
|
||||||
|
this.add(`simulator-${device}`);
|
||||||
|
}
|
||||||
|
this.lastSimulatorDevice = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setHideSlate(flag: boolean) {
|
||||||
|
if (this.has('slate-fixed')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
this.add('hide-slate');
|
||||||
|
} else {
|
||||||
|
this.remove('hide-slate');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSlateFixedMode(flag: boolean) {
|
||||||
|
if (flag) {
|
||||||
|
this.remove('hide-slate');
|
||||||
|
this.add('slate-fixed');
|
||||||
|
} else {
|
||||||
|
this.remove('slate-fixed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSlateFullMode(flag: boolean) {
|
||||||
|
if (flag) {
|
||||||
|
this.add('slate-full-screen');
|
||||||
|
} else {
|
||||||
|
this.remove('slate-full-screen');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFlags() {
|
||||||
|
return this.flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public applyFlags(modifiedFlag?: string) {
|
||||||
|
if (!this.ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = document.documentElement;
|
||||||
|
if (this.lastFlags) {
|
||||||
|
this.lastFlags.filter((flag: string) => this.flags.indexOf(flag) < 0).forEach((flag) => {
|
||||||
|
doc.classList.remove(`engine-${flag}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.flags.forEach((flag) => {
|
||||||
|
doc.classList.add(`engine-${flag}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.lastFlags = this.flags.slice(0);
|
||||||
|
this.emitter.emit('flagschange', this.flags, modifiedFlag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public has(flag: string) {
|
||||||
|
return this.flags.indexOf(flag) > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public add(flag: string) {
|
||||||
|
if (!this.has(flag)) {
|
||||||
|
this.flags.push(flag);
|
||||||
|
this.applyFlags(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public remove(flag: string) {
|
||||||
|
const i = this.flags.indexOf(flag);
|
||||||
|
if (i > -1) {
|
||||||
|
this.flags.splice(i, 1);
|
||||||
|
this.applyFlags(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public onFlagsChange(func: () => any) {
|
||||||
|
this.emitter.on('flagschange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('flagschange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Flags();
|
||||||
79
packages/vision-polyfill/src/i18n-util/index.d.ts
vendored
Normal file
79
packages/vision-polyfill/src/i18n-util/index.d.ts
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
declare enum LANGUAGES {
|
||||||
|
zh_CN = 'zh_CN',
|
||||||
|
en_US = 'en_US'
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface I18nRecord {
|
||||||
|
type?: 'i18n';
|
||||||
|
[key: string]: string;
|
||||||
|
/**
|
||||||
|
* i18n unique key
|
||||||
|
*/
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface I18nRecordData {
|
||||||
|
gmtCreate: Date;
|
||||||
|
gmtModified: Date;
|
||||||
|
i18nKey: string;
|
||||||
|
i18nText: I18nRecord;
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface II18nUtilConfigs {
|
||||||
|
items?: {};
|
||||||
|
/**
|
||||||
|
* 是否禁用初始化加载
|
||||||
|
*/
|
||||||
|
disableInstantLoad?: boolean;
|
||||||
|
/**
|
||||||
|
* 初始化的时候是否全量加载
|
||||||
|
*/
|
||||||
|
disableFullLoad?: boolean;
|
||||||
|
loader?: (configs: ILoaderConfigs) => Promise<I18nRecordData[]>;
|
||||||
|
remover?: (key: string, dic: I18nRecord) => Promise<void>;
|
||||||
|
saver?: (key: string, dic: I18nRecord) => Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ILoaderConfigs {
|
||||||
|
/**
|
||||||
|
* search keywords
|
||||||
|
*/
|
||||||
|
keyword?: string;
|
||||||
|
/**
|
||||||
|
* should load all i18n items
|
||||||
|
*/
|
||||||
|
isFull?: boolean;
|
||||||
|
/**
|
||||||
|
* search i18n item based on uniqueKey
|
||||||
|
*/
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface II18nUtil {
|
||||||
|
init(config: II18nUtilConfigs): void;
|
||||||
|
isInitialized(): boolean;
|
||||||
|
isReady(): boolean;
|
||||||
|
attach(prop: object, value: I18nRecord, updator: () => any);
|
||||||
|
search(keyword: string, silent?: boolean);
|
||||||
|
load(configs: ILoaderConfigs): Promise<I18nRecord[]>;
|
||||||
|
/**
|
||||||
|
* Get local i18n Record
|
||||||
|
* @param key
|
||||||
|
* @param lang
|
||||||
|
*/
|
||||||
|
get(key: string, lang: string): string | I18nRecord;
|
||||||
|
getFromRemote(key: string): Promise<I18nRecord>;
|
||||||
|
getItem(key: string, forceData?: boolean): any;
|
||||||
|
getItems(): I18nRecord[];
|
||||||
|
update(key: string, doc: I18nRecord, lang: LANGUAGES);
|
||||||
|
create(doc: I18nRecord, lang: LANGUAGES): string;
|
||||||
|
remove(key: string): Promise<void>;
|
||||||
|
|
||||||
|
onReady(func: () => any);
|
||||||
|
onRowsChange(func: () => any);
|
||||||
|
onChange(func: (dic: I18nRecord) => any);
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const i18nUtil: II18nUtil;
|
||||||
|
export default i18nUtil;
|
||||||
312
packages/vision-polyfill/src/i18n-util/index.js
Normal file
312
packages/vision-polyfill/src/i18n-util/index.js
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
|
import { editorHelper } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
const { obx } = editorHelper;
|
||||||
|
|
||||||
|
let keybase = Date.now();
|
||||||
|
function keygen(maps) {
|
||||||
|
let key;
|
||||||
|
do {
|
||||||
|
key = `i18n-${(keybase).toString(36)}`;
|
||||||
|
keybase += 1;
|
||||||
|
} while (key in maps);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DocItem {
|
||||||
|
constructor(parent, doc, unInitial) {
|
||||||
|
this.parent = parent;
|
||||||
|
const { use, ...strings } = doc;
|
||||||
|
this.doc = obx.val({
|
||||||
|
type: 'i18n',
|
||||||
|
...strings,
|
||||||
|
});
|
||||||
|
this.emitter = new EventEmitter();
|
||||||
|
this.inited = unInitial !== true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getKey() {
|
||||||
|
return this.doc.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDoc(lang) {
|
||||||
|
if (lang) {
|
||||||
|
return this.doc[lang];
|
||||||
|
}
|
||||||
|
return this.doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDoc(doc, lang, initial) {
|
||||||
|
if (lang) {
|
||||||
|
this.doc[lang] = doc;
|
||||||
|
} else {
|
||||||
|
const { use, strings } = doc || {};
|
||||||
|
Object.assign(this.doc, strings);
|
||||||
|
}
|
||||||
|
this.emitter.emit('change', this.doc);
|
||||||
|
|
||||||
|
if (initial) {
|
||||||
|
this.inited = true;
|
||||||
|
} else if (this.inited) {
|
||||||
|
this.parent._saveChange(this.doc.key, this.doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
if (!this.inited) return Promise.reject('not initialized');
|
||||||
|
|
||||||
|
const { key, ...doc } = this.doc; // eslint-disable-line
|
||||||
|
this.emitter.emit('change', doc);
|
||||||
|
return this.parent.remove(this.getKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(func) {
|
||||||
|
this.emitter.on('change', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('change', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class I18nUtil {
|
||||||
|
constructor() {
|
||||||
|
this.emitter = new EventEmitter();
|
||||||
|
// original data source from remote
|
||||||
|
this.i18nData = {};
|
||||||
|
// current i18n records on the left pane
|
||||||
|
this.items = [];
|
||||||
|
this.maps = {};
|
||||||
|
// full list of i18n records for synchronized call
|
||||||
|
this.fullList = [];
|
||||||
|
this.fullMap = {};
|
||||||
|
|
||||||
|
this.config = {};
|
||||||
|
this.ready = false;
|
||||||
|
this.isInited = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_prepareItems(items, isFull = false, isSilent = false) {
|
||||||
|
this[isFull ? 'fullList' : 'items'] = items.map((dict) => {
|
||||||
|
let item = this[isFull ? 'fullMap' : 'maps'][dict.key];
|
||||||
|
if (item) {
|
||||||
|
item.setDoc(dict, null, true);
|
||||||
|
} else {
|
||||||
|
item = new DocItem(this, dict);
|
||||||
|
this[isFull ? 'fullMap' : 'maps'][dict.key] = item;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.ready && !isSilent) {
|
||||||
|
this.emitter.emit('rowschange');
|
||||||
|
this.emitter.emit('change');
|
||||||
|
} else {
|
||||||
|
this.ready = true;
|
||||||
|
this.emitter.emit('ready');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_load(configs = {}, silent) {
|
||||||
|
if (!this.config.loader) {
|
||||||
|
console.error(new Error('Please load loader while init I18nUtil.'));
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.config.loader(configs).then((data) => {
|
||||||
|
if (configs.i18nKey) {
|
||||||
|
return Promise.resolve(data.i18nText);
|
||||||
|
}
|
||||||
|
this._prepareItems(data.data, configs.isFull, silent);
|
||||||
|
// set pagination data to i18nData
|
||||||
|
this.i18nData = data;
|
||||||
|
if (!silent) {
|
||||||
|
this.emitter.emit('rowschange');
|
||||||
|
this.emitter.emit('change');
|
||||||
|
}
|
||||||
|
return Promise.resolve(this.items.map(i => i.getDoc()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveToItems(key, dict) {
|
||||||
|
let item = null;
|
||||||
|
item = this.items.find(doc => doc.getKey() === key);
|
||||||
|
if (!item) {
|
||||||
|
item = this.fullList.find(doc => doc.getKey() === key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
item.setDoc(dict);
|
||||||
|
} else {
|
||||||
|
item = new DocItem(this, {
|
||||||
|
key,
|
||||||
|
...dict,
|
||||||
|
});
|
||||||
|
this.items.unshift(item);
|
||||||
|
this.fullList.unshift(item);
|
||||||
|
this.maps[key] = item;
|
||||||
|
this.fullMap[key] = item;
|
||||||
|
this._saveChange(key, dict, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveChange(key, dict, rowschange) {
|
||||||
|
if (rowschange) {
|
||||||
|
this.emitter.emit('rowschange');
|
||||||
|
}
|
||||||
|
this.emitter.emit('change');
|
||||||
|
if (dict === null) {
|
||||||
|
delete this.maps[key];
|
||||||
|
delete this.fullMap[key];
|
||||||
|
}
|
||||||
|
return this._save(key, dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
_save(key, dict) {
|
||||||
|
const saver = dict === null ? this.config.remover : this.config.saver;
|
||||||
|
if (!saver) return Promise.reject('Saver function is not set');
|
||||||
|
return saver(key, dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(config) {
|
||||||
|
if (this.isInited) return;
|
||||||
|
this.config = config || {};
|
||||||
|
if (this.config.items) {
|
||||||
|
// inject to current page
|
||||||
|
this._prepareItems(this.config.items);
|
||||||
|
}
|
||||||
|
if (!this.config.disableInstantLoad) {
|
||||||
|
this._load({ isFull: !this.config.disableFullLoad });
|
||||||
|
}
|
||||||
|
this.isInited = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
isInitialized() {
|
||||||
|
return this.isInited;
|
||||||
|
}
|
||||||
|
|
||||||
|
isReady() {
|
||||||
|
return this.ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add events updater when i18n record change
|
||||||
|
// we should notify engine's view to change
|
||||||
|
attach(prop, value, updator) {
|
||||||
|
const isI18nValue = value && value.type === 'i18n' && value.key;
|
||||||
|
const key = isI18nValue ? value.key : null;
|
||||||
|
if (prop.i18nLink) {
|
||||||
|
if (isI18nValue && (key === prop.i18nLink.key)) {
|
||||||
|
return prop.i18nLink;
|
||||||
|
}
|
||||||
|
prop.i18nLink.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isI18nValue) {
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
detach: this.getItem(key, value).onChange(updator),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索 i18n 词条
|
||||||
|
*
|
||||||
|
* @param {any} keyword 搜索关键字
|
||||||
|
* @param {boolean} [silent=false] 是否刷新左侧的 i18n 数据
|
||||||
|
* @returns
|
||||||
|
*
|
||||||
|
* @memberof I18nUtil
|
||||||
|
*/
|
||||||
|
search(keyword, silent = false) {
|
||||||
|
return this._load({ keyword }, silent);
|
||||||
|
}
|
||||||
|
|
||||||
|
load(configs = {}) {
|
||||||
|
return this._load(configs);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key, lang) {
|
||||||
|
const item = this.getItem(key);
|
||||||
|
if (item) {
|
||||||
|
return item.getDoc(lang);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFromRemote(key) {
|
||||||
|
return this._load({ i18nKey: key });
|
||||||
|
}
|
||||||
|
|
||||||
|
getItem(key, forceData) {
|
||||||
|
if (forceData && !this.maps[key] && !this.fullList[key]) {
|
||||||
|
const item = new DocItem(this, {
|
||||||
|
key,
|
||||||
|
...forceData,
|
||||||
|
}, true);
|
||||||
|
this.maps[key] = item;
|
||||||
|
this.fullMap[key] = item;
|
||||||
|
this.fullList.push(item);
|
||||||
|
this.items.push(item);
|
||||||
|
}
|
||||||
|
return this.maps[key] || this.fullMap[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
getItems() {
|
||||||
|
return this.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(key, doc, lang) {
|
||||||
|
let dict = this.get(key) || {};
|
||||||
|
if (!lang) {
|
||||||
|
dict = doc;
|
||||||
|
} else {
|
||||||
|
dict[lang] = doc;
|
||||||
|
}
|
||||||
|
this._saveToItems(key, dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
create(doc, lang) {
|
||||||
|
const dict = lang ? { [lang]: doc } : doc;
|
||||||
|
const key = keygen(this.fullMap);
|
||||||
|
this._saveToItems(key, dict);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(key) {
|
||||||
|
const index = this.items.findIndex(item => item.getKey() === key);
|
||||||
|
const indexG = this.fullList.findIndex(item => item.getKey() === key);
|
||||||
|
if (index > -1) {
|
||||||
|
this.items.splice(index, 1);
|
||||||
|
}
|
||||||
|
if (indexG > -1) {
|
||||||
|
this.fullList.splice(index, 1);
|
||||||
|
}
|
||||||
|
return this._saveChange(key, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
onReady(func) {
|
||||||
|
this.emitter.on('ready', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('ready', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onRowsChange(func) {
|
||||||
|
this.emitter.on('rowschange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('rowschange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onChange(func) {
|
||||||
|
this.emitter.on('change', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('change', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new I18nUtil();
|
||||||
180
packages/vision-polyfill/src/index.ts
Normal file
180
packages/vision-polyfill/src/index.ts
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
import { createElement } from 'react';
|
||||||
|
import { render } from 'react-dom';
|
||||||
|
import * as utils from '@ali/ve-utils';
|
||||||
|
import Popup from '@ali/ve-popups';
|
||||||
|
import Icons from '@ali/ve-icons';
|
||||||
|
import logger from '@ali/vu-logger';
|
||||||
|
import I18nUtil from './i18n-util';
|
||||||
|
import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS, VERSION as Version } from './base/const';
|
||||||
|
import Bus from './bus';
|
||||||
|
import { skeleton, designer, editor, plugins, init, hotkey as Hotkey, monitor, designerHelper } from '@ali/lowcode-engine';
|
||||||
|
import Panes from './panes';
|
||||||
|
import Exchange from './exchange';
|
||||||
|
import context from './context';
|
||||||
|
import VisualManager from './base/visualManager';
|
||||||
|
import VisualDesigner from './base/visualDesigner';
|
||||||
|
import Trunk from './bundle/trunk';
|
||||||
|
import Prototype from './bundle/prototype';
|
||||||
|
import Bundle from './bundle/bundle';
|
||||||
|
import Pages from './pages';
|
||||||
|
import * as Field from './fields';
|
||||||
|
import Prop from './prop';
|
||||||
|
import Env from './env';
|
||||||
|
import DragEngine from './drag-engine';
|
||||||
|
// import Flags from './base/flags';
|
||||||
|
import Viewport from './viewport';
|
||||||
|
import Project from './project';
|
||||||
|
import Symbols from './symbols';
|
||||||
|
import { invariant } from './utils';
|
||||||
|
import '@ali/lowcode-editor-setters';
|
||||||
|
import './reducers';
|
||||||
|
|
||||||
|
import './vision.less';
|
||||||
|
|
||||||
|
invariant((window as any).AliLowCodeEngine, 'AliLowCodeEngine is required, since vision polyfill is totally based on AliLowCodeEngine');
|
||||||
|
|
||||||
|
// async function init(container?: Element) {
|
||||||
|
// if (!container) {
|
||||||
|
// container = document.createElement('div');
|
||||||
|
// document.body.appendChild(container);
|
||||||
|
// }
|
||||||
|
// container.id = 'engine';
|
||||||
|
|
||||||
|
// await plugins.init();
|
||||||
|
// render(
|
||||||
|
// createElement(Workbench, {
|
||||||
|
// skeleton,
|
||||||
|
// className: 'engine-main',
|
||||||
|
// topAreaItemClassName: 'engine-actionitem',
|
||||||
|
// }),
|
||||||
|
// container,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VE.ui.xxx
|
||||||
|
*
|
||||||
|
* Core UI Components
|
||||||
|
*/
|
||||||
|
const ui = {
|
||||||
|
Field,
|
||||||
|
Icon: Icons,
|
||||||
|
Icons,
|
||||||
|
Popup,
|
||||||
|
};
|
||||||
|
|
||||||
|
const modules = {
|
||||||
|
VisualManager,
|
||||||
|
VisualDesigner,
|
||||||
|
I18nUtil,
|
||||||
|
Prop,
|
||||||
|
};
|
||||||
|
|
||||||
|
// const designerHelper = {
|
||||||
|
// registerMetadataTransducer,
|
||||||
|
// addBuiltinComponentAction,
|
||||||
|
// removeBuiltinComponentAction,
|
||||||
|
// // modifyBuiltinComponentAction,
|
||||||
|
// };
|
||||||
|
const { registerMetadataTransducer } = designerHelper;
|
||||||
|
|
||||||
|
const VisualEngine = {
|
||||||
|
designer,
|
||||||
|
designerHelper,
|
||||||
|
editor,
|
||||||
|
skeleton,
|
||||||
|
/**
|
||||||
|
* VE.Popup
|
||||||
|
*/
|
||||||
|
Popup,
|
||||||
|
/**
|
||||||
|
* VE Utils
|
||||||
|
*/
|
||||||
|
utils,
|
||||||
|
I18nUtil,
|
||||||
|
Hotkey,
|
||||||
|
Env,
|
||||||
|
monitor,
|
||||||
|
/* pub/sub 集线器 */
|
||||||
|
Bus,
|
||||||
|
/* 事件 */
|
||||||
|
EVENTS,
|
||||||
|
/* 修饰方法 */
|
||||||
|
HOOKS,
|
||||||
|
Exchange,
|
||||||
|
context,
|
||||||
|
/**
|
||||||
|
* VE.init
|
||||||
|
*
|
||||||
|
* Initialized the whole VisualEngine UI
|
||||||
|
*/
|
||||||
|
init,
|
||||||
|
ui,
|
||||||
|
Panes,
|
||||||
|
modules,
|
||||||
|
Trunk,
|
||||||
|
Prototype,
|
||||||
|
Bundle,
|
||||||
|
Pages,
|
||||||
|
DragEngine,
|
||||||
|
Viewport,
|
||||||
|
Version,
|
||||||
|
Project,
|
||||||
|
logger,
|
||||||
|
Symbols,
|
||||||
|
registerMetadataTransducer,
|
||||||
|
plugins,
|
||||||
|
// Flags,
|
||||||
|
};
|
||||||
|
|
||||||
|
(window as any).VisualEngine = VisualEngine;
|
||||||
|
|
||||||
|
export default VisualEngine;
|
||||||
|
|
||||||
|
export {
|
||||||
|
designer,
|
||||||
|
designerHelper,
|
||||||
|
editor,
|
||||||
|
skeleton,
|
||||||
|
/**
|
||||||
|
* VE.Popup
|
||||||
|
*/
|
||||||
|
Popup,
|
||||||
|
/**
|
||||||
|
* VE Utils
|
||||||
|
*/
|
||||||
|
utils,
|
||||||
|
I18nUtil,
|
||||||
|
Hotkey,
|
||||||
|
Env,
|
||||||
|
monitor,
|
||||||
|
/* pub/sub 集线器 */
|
||||||
|
Bus,
|
||||||
|
/* 事件 */
|
||||||
|
EVENTS,
|
||||||
|
/* 修饰方法 */
|
||||||
|
HOOKS,
|
||||||
|
Exchange,
|
||||||
|
context,
|
||||||
|
/**
|
||||||
|
* VE.init
|
||||||
|
*
|
||||||
|
* Initialized the whole VisualEngine UI
|
||||||
|
*/
|
||||||
|
init,
|
||||||
|
ui,
|
||||||
|
Panes,
|
||||||
|
modules,
|
||||||
|
Trunk,
|
||||||
|
Prototype,
|
||||||
|
Bundle,
|
||||||
|
Pages,
|
||||||
|
DragEngine,
|
||||||
|
Viewport,
|
||||||
|
Version,
|
||||||
|
Project,
|
||||||
|
logger,
|
||||||
|
Symbols,
|
||||||
|
registerMetadataTransducer,
|
||||||
|
plugins,
|
||||||
|
};
|
||||||
1
packages/vision-polyfill/src/module.d.ts
vendored
Normal file
1
packages/vision-polyfill/src/module.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
declare module '@ali/vu-css-style';
|
||||||
145
packages/vision-polyfill/src/pages.ts
Normal file
145
packages/vision-polyfill/src/pages.ts
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import { RootSchema } from '@ali/lowcode-types';
|
||||||
|
import { DocumentModel } from '@ali/lowcode-designer';
|
||||||
|
import { designer } from '@ali/lowcode-engine';
|
||||||
|
import NodeCacheVisitor from './rootNodeVisitor';
|
||||||
|
|
||||||
|
const { project } = designer;
|
||||||
|
|
||||||
|
export interface PageDataV1 {
|
||||||
|
id: string;
|
||||||
|
componentsTree: RootSchema[];
|
||||||
|
layout: RootSchema;
|
||||||
|
[dataAddon: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PageDataV2 {
|
||||||
|
id: string;
|
||||||
|
componentsTree: RootSchema[];
|
||||||
|
[dataAddon: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPageDataV1(obj: any): obj is PageDataV1 {
|
||||||
|
return obj && obj.layout;
|
||||||
|
}
|
||||||
|
function isPageDataV2(obj: any): obj is PageDataV2 {
|
||||||
|
return obj && obj.componentsTree && Array.isArray(obj.componentsTree);
|
||||||
|
}
|
||||||
|
|
||||||
|
type OldPageData = PageDataV1 | PageDataV2;
|
||||||
|
|
||||||
|
const pages = Object.assign(project, {
|
||||||
|
setPages(pages: OldPageData[]) {
|
||||||
|
if (!pages || !Array.isArray(pages) || pages.length === 0) {
|
||||||
|
throw new Error('pages schema 不合法');
|
||||||
|
}
|
||||||
|
// todo: miniapp
|
||||||
|
let componentsTree: any = [];
|
||||||
|
if (window.pageConfig?.isNoCodeMiniApp) {
|
||||||
|
// 小程序多页面
|
||||||
|
pages.forEach((item: any) => {
|
||||||
|
if (isPageDataV1(item)) {
|
||||||
|
componentsTree.push(item.layout);
|
||||||
|
} else {
|
||||||
|
componentsTree.push(item.componentsTree[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (isPageDataV1(pages[0])) {
|
||||||
|
componentsTree = [pages[0].layout];
|
||||||
|
} else {
|
||||||
|
// if (!pages[0].componentsTree) return;
|
||||||
|
componentsTree = pages[0].componentsTree;
|
||||||
|
if (componentsTree[0]) {
|
||||||
|
componentsTree[0].componentName = componentsTree[0].componentName || 'Page';
|
||||||
|
// FIXME
|
||||||
|
if (componentsTree[0].componentName === 'Page' || componentsTree[0].componentName === 'Component') {
|
||||||
|
componentsTree[0].methods = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentsTree.forEach((item: any) => {
|
||||||
|
item.componentName = item.componentName || 'Page';
|
||||||
|
if (item.componentName === 'Page' || item.componentName === 'Component') {
|
||||||
|
item.methods = {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
project.load(
|
||||||
|
{
|
||||||
|
version: '1.0.0',
|
||||||
|
componentsMap: [],
|
||||||
|
componentsTree,
|
||||||
|
id: pages[0].id,
|
||||||
|
config: project.config,
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
// FIXME: 根本原因应该是 propStash 导致的,这样可以避免页面加载之后就被标记为 isModified
|
||||||
|
setTimeout(() => {
|
||||||
|
project.currentDocument?.history.savePoint();
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
addPage(data: OldPageData | RootSchema) {
|
||||||
|
if (isPageDataV1(data)) {
|
||||||
|
data = data.layout;
|
||||||
|
} else if (isPageDataV2(data)) {
|
||||||
|
data = data.componentsTree[0];
|
||||||
|
}
|
||||||
|
return project.open(data);
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
toData() {
|
||||||
|
return project.documents.map((doc) => doc.toData());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.defineProperty(pages, 'currentPage', {
|
||||||
|
get() {
|
||||||
|
return project.currentDocument;
|
||||||
|
},
|
||||||
|
set(_currentPage) {
|
||||||
|
// do nothing
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
pages.onCurrentPageChange((page: DocumentModel) => {
|
||||||
|
if (!page) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
page.acceptRootNodeVisitor('NodeCache', (rootNode) => {
|
||||||
|
const visitor: NodeCacheVisitor = page.getRootNodeVisitor('NodeCache');
|
||||||
|
if (visitor) {
|
||||||
|
visitor.destroy();
|
||||||
|
}
|
||||||
|
return new NodeCacheVisitor(page, rootNode);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default pages;
|
||||||
278
packages/vision-polyfill/src/panes.ts
Normal file
278
packages/vision-polyfill/src/panes.ts
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
import { skeleton, editor } from '@ali/lowcode-engine';
|
||||||
|
import { ReactElement } from 'react';
|
||||||
|
import { IWidgetBaseConfig } from '@ali/lowcode-editor-skeleton';
|
||||||
|
import { uniqueId } from '@ali/lowcode-utils';
|
||||||
|
import bus from './bus';
|
||||||
|
|
||||||
|
export interface IContentItemConfig {
|
||||||
|
title: string;
|
||||||
|
content: JSX.Element;
|
||||||
|
tip?: {
|
||||||
|
content: string;
|
||||||
|
url?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OldPaneConfig {
|
||||||
|
// 'dock' | 'action' | 'tab' | 'widget' | 'stage'
|
||||||
|
type?: string; // where
|
||||||
|
|
||||||
|
id?: string;
|
||||||
|
name: string;
|
||||||
|
title?: string;
|
||||||
|
content?: any;
|
||||||
|
|
||||||
|
place?: string; // align: left|right|top|center|bottom
|
||||||
|
description?: string; // tip?
|
||||||
|
tip?:
|
||||||
|
| string
|
||||||
|
| {
|
||||||
|
// as help tip
|
||||||
|
url?: string;
|
||||||
|
content?: string | JSX.Element;
|
||||||
|
}; // help
|
||||||
|
|
||||||
|
init?: () => any;
|
||||||
|
destroy?: () => any;
|
||||||
|
props?: any;
|
||||||
|
|
||||||
|
contents?: IContentItemConfig[];
|
||||||
|
hideTitleBar?: boolean;
|
||||||
|
width?: number;
|
||||||
|
maxWidth?: number;
|
||||||
|
height?: number;
|
||||||
|
maxHeight?: number;
|
||||||
|
position?: string | string[]; // todo
|
||||||
|
menu?: JSX.Element; // as title
|
||||||
|
index?: number; // todo
|
||||||
|
isAction?: boolean; // as normal dock
|
||||||
|
fullScreen?: boolean; // todo
|
||||||
|
canSetFixed?: boolean; // 是否可以设置固定模式
|
||||||
|
defaultFixed?: boolean; // 是否默认固定
|
||||||
|
}
|
||||||
|
|
||||||
|
function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: string } {
|
||||||
|
const { type, id, name, title, content, place, description, init, destroy, props, index } = config;
|
||||||
|
|
||||||
|
const newConfig: any = {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
content,
|
||||||
|
props: {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
align: place,
|
||||||
|
},
|
||||||
|
contentProps: props,
|
||||||
|
index: index || props?.index,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (type === 'dock') {
|
||||||
|
newConfig.type = 'PanelDock';
|
||||||
|
newConfig.area = 'left';
|
||||||
|
newConfig.props.description = description || title;
|
||||||
|
const {
|
||||||
|
contents,
|
||||||
|
hideTitleBar,
|
||||||
|
tip,
|
||||||
|
width,
|
||||||
|
maxWidth,
|
||||||
|
height,
|
||||||
|
maxHeight,
|
||||||
|
menu,
|
||||||
|
isAction,
|
||||||
|
canSetFixed,
|
||||||
|
defaultFixed,
|
||||||
|
} = config;
|
||||||
|
if (menu) {
|
||||||
|
newConfig.props.title = menu;
|
||||||
|
}
|
||||||
|
if (isAction) {
|
||||||
|
newConfig.type = 'Dock';
|
||||||
|
} else {
|
||||||
|
newConfig.panelProps = {
|
||||||
|
title,
|
||||||
|
hideTitleBar,
|
||||||
|
help: tip,
|
||||||
|
width,
|
||||||
|
maxWidth,
|
||||||
|
height,
|
||||||
|
maxHeight,
|
||||||
|
canSetFixed,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (defaultFixed) {
|
||||||
|
newConfig.panelProps.area = 'leftFixedArea';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contents && Array.isArray(contents)) {
|
||||||
|
newConfig.content = contents.map(({ title, content, tip }, index) => {
|
||||||
|
return {
|
||||||
|
type: 'Panel',
|
||||||
|
name: typeof title === 'string' ? title : `${name}:${index}`,
|
||||||
|
content,
|
||||||
|
contentProps: props,
|
||||||
|
props: {
|
||||||
|
title,
|
||||||
|
help: tip,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type === 'action') {
|
||||||
|
newConfig.area = 'top';
|
||||||
|
newConfig.type = 'Dock';
|
||||||
|
} else if (type === 'tab') {
|
||||||
|
newConfig.area = 'right';
|
||||||
|
newConfig.type = 'Panel';
|
||||||
|
} else if (type === 'stage') {
|
||||||
|
newConfig.area = 'stages';
|
||||||
|
newConfig.type = 'Widget';
|
||||||
|
} else {
|
||||||
|
newConfig.area = 'main';
|
||||||
|
newConfig.type = 'Widget';
|
||||||
|
}
|
||||||
|
newConfig.props.onInit = init;
|
||||||
|
newConfig.props.onDestroy = destroy;
|
||||||
|
|
||||||
|
return newConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
function add(config: (() => OldPaneConfig) | OldPaneConfig, extraConfig?: any) {
|
||||||
|
if (typeof config === 'function') {
|
||||||
|
config = config.call(null);
|
||||||
|
}
|
||||||
|
if (!config || !config.type) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (extraConfig) {
|
||||||
|
config = { ...config, ...extraConfig };
|
||||||
|
}
|
||||||
|
|
||||||
|
const upgraded = upgradeConfig(config);
|
||||||
|
if (upgraded.area === 'stages') {
|
||||||
|
if (upgraded.id) {
|
||||||
|
upgraded.name = upgraded.id;
|
||||||
|
} else if (!upgraded.name) {
|
||||||
|
upgraded.name = uniqueId('stage');
|
||||||
|
}
|
||||||
|
const stage = skeleton.add(upgraded);
|
||||||
|
return stage?.getName();
|
||||||
|
} else {
|
||||||
|
return skeleton.add(upgraded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionPane = Object.assign(skeleton.topArea, {
|
||||||
|
/**
|
||||||
|
* compatible *VE.actionPane.getActions*
|
||||||
|
*/
|
||||||
|
getActions(): any {
|
||||||
|
return skeleton.topArea.container.items;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* compatible *VE.actionPane.activeDock*
|
||||||
|
*/
|
||||||
|
setActions() {
|
||||||
|
// empty
|
||||||
|
},
|
||||||
|
get actions() {
|
||||||
|
return skeleton.topArea.container.items;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const dockPane = Object.assign(skeleton.leftArea, {
|
||||||
|
/**
|
||||||
|
* compatible *VE.dockPane.activeDock*
|
||||||
|
*/
|
||||||
|
activeDock(item: any) {
|
||||||
|
if (!item) {
|
||||||
|
skeleton.leftFloatArea?.current?.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const name = item.name || item;
|
||||||
|
const pane = skeleton.getPanel(name);
|
||||||
|
if (!pane) {
|
||||||
|
console.warn(`Could not find pane with name ${name}`);
|
||||||
|
}
|
||||||
|
pane?.active();
|
||||||
|
bus.emit('ve.dock_pane.active_doc', pane);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* compatible *VE.dockPane.onDockShow*
|
||||||
|
*/
|
||||||
|
onDockShow(fn: (dock: any) => void): () => void {
|
||||||
|
const f = (_: any, dock: any) => {
|
||||||
|
fn(dock);
|
||||||
|
};
|
||||||
|
editor.on('skeleton.panel-dock.active', f);
|
||||||
|
return () => {
|
||||||
|
editor.removeListener('skeleton.panel-dock.active', f);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* compatible *VE.dockPane.onDockHide*
|
||||||
|
*/
|
||||||
|
onDockHide(fn: (dock: any) => void): () => void {
|
||||||
|
const f = (_: any, dock: any) => {
|
||||||
|
fn(dock);
|
||||||
|
};
|
||||||
|
editor.on('skeleton.panel-dock.unactive', f);
|
||||||
|
return () => {
|
||||||
|
editor.removeListener('skeleton.panel-dock.unactive', f);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* compatible *VE.dockPane.setFixed*
|
||||||
|
*/
|
||||||
|
setFixed(flag: boolean) {
|
||||||
|
// todo:
|
||||||
|
},
|
||||||
|
getDocks() {
|
||||||
|
return skeleton.leftFloatArea?.container.items;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const tabPane = Object.assign(skeleton.rightArea, {
|
||||||
|
setFloat(flag: boolean) {
|
||||||
|
// todo:
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const toolbar = Object.assign(skeleton.toolbar, {
|
||||||
|
setContents(contents: ReactElement) {
|
||||||
|
// todo:
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const widgets = skeleton.mainArea;
|
||||||
|
|
||||||
|
const stages = Object.assign(skeleton.stages, {
|
||||||
|
getStage(name: string) {
|
||||||
|
return skeleton.stages.container.get(name);
|
||||||
|
},
|
||||||
|
|
||||||
|
createStage(config: any) {
|
||||||
|
config = upgradeConfig(config);
|
||||||
|
if (config.id) {
|
||||||
|
config.name = config.id;
|
||||||
|
} else if (!config.name) {
|
||||||
|
config.name = uniqueId('stage');
|
||||||
|
}
|
||||||
|
|
||||||
|
const stage = skeleton.stages.add(config);
|
||||||
|
return stage.getName();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default {
|
||||||
|
ActionPane: actionPane, // topArea
|
||||||
|
actionPane, //
|
||||||
|
DockPane: dockPane, // leftArea
|
||||||
|
dockPane,
|
||||||
|
TabPane: tabPane, // rightArea
|
||||||
|
tabPane,
|
||||||
|
add,
|
||||||
|
toolbar, // toolbar
|
||||||
|
Stages: stages,
|
||||||
|
Widgets: widgets, // centerArea
|
||||||
|
widgets,
|
||||||
|
};
|
||||||
19
packages/vision-polyfill/src/project.ts
Normal file
19
packages/vision-polyfill/src/project.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { designer } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
const { project } = designer;
|
||||||
|
|
||||||
|
Object.assign(project, {
|
||||||
|
getSchema(): any {
|
||||||
|
return this.schema || {};
|
||||||
|
},
|
||||||
|
|
||||||
|
setSchema(schema: any) {
|
||||||
|
this.schema = schema;
|
||||||
|
},
|
||||||
|
|
||||||
|
setConfig(config: any) {
|
||||||
|
this.set('config', config);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default project;
|
||||||
630
packages/vision-polyfill/src/prop.ts
Normal file
630
packages/vision-polyfill/src/prop.ts
Normal file
@ -0,0 +1,630 @@
|
|||||||
|
import { Component } from 'react';
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import { fromJS, Iterable, Map as IMMap } from 'immutable';
|
||||||
|
import logger from '@ali/vu-logger';
|
||||||
|
import { cloneDeep, isDataEqual, combineInitial, Transducer } from '@ali/ve-utils';
|
||||||
|
import I18nUtil from '@ali/ve-i18n-util';
|
||||||
|
import { editor, setters } from '@ali/lowcode-engine';
|
||||||
|
import { OldPropConfig, DISPLAY_TYPE } from './bundle/upgrade-metadata';
|
||||||
|
import { uniqueId } from '@ali/lowcode-utils';
|
||||||
|
|
||||||
|
const { getSetter } = setters;
|
||||||
|
|
||||||
|
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('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;
|
||||||
|
if (accessor && (!options || !options.disableAccessor)) {
|
||||||
|
const value = accessor.call(this as any, 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;
|
||||||
|
if (sync) {
|
||||||
|
const value = sync.call(this as any, 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 = 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 as any, { 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;
|
||||||
|
|
||||||
|
if (!extraOptions) {
|
||||||
|
extraOptions = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mutator && !extraOptions.disableMutator) {
|
||||||
|
mutator.call(this as any, 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;
|
||||||
|
if (mutator) {
|
||||||
|
mutator.call(this as any, 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;
|
||||||
|
if (typeof hidden === 'function') {
|
||||||
|
hidden = hidden.call(this as any, this.getValue());
|
||||||
|
}
|
||||||
|
return hidden === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isDisabled() {
|
||||||
|
let { disabled } = this.config;
|
||||||
|
if (typeof disabled === 'function') {
|
||||||
|
disabled = disabled.call(this as any, this.getValue());
|
||||||
|
}
|
||||||
|
return disabled === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isIgnore() {
|
||||||
|
if (this.isDisabled()) { return true; }
|
||||||
|
|
||||||
|
let { ignore } = this.config;
|
||||||
|
if (typeof ignore === 'function') {
|
||||||
|
ignore = ignore.call(this as any, 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
isPlainObject,
|
||||||
|
} from '@ali/lowcode-utils';
|
||||||
|
import { isJSExpression, isJSSlot } from '@ali/lowcode-types';
|
||||||
|
|
||||||
|
export function compatibleReducer(props: any) {
|
||||||
|
if (!props || !isPlainObject(props)) {
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
// 为了能降级到老版本,建议在后期版本去掉以下代码
|
||||||
|
if (isJSSlot(props)) {
|
||||||
|
return {
|
||||||
|
type: 'JSBlock',
|
||||||
|
value: {
|
||||||
|
componentName: 'Slot',
|
||||||
|
children: props.value,
|
||||||
|
props: {
|
||||||
|
slotTitle: props.title,
|
||||||
|
slotName: props.name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (isJSExpression(props) && !props.events) {
|
||||||
|
return {
|
||||||
|
type: 'variable',
|
||||||
|
value: props.mock,
|
||||||
|
variable: props.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const newProps: any = {};
|
||||||
|
Object.entries<any>(props).forEach(([key, val]) => {
|
||||||
|
newProps[key] = compatibleReducer(val);
|
||||||
|
});
|
||||||
|
return newProps;
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import logger from '@ali/vu-logger';
|
||||||
|
import { hasOwnProperty } from '@ali/lowcode-utils';
|
||||||
|
|
||||||
|
export function filterReducer(props: any, node: Node): any {
|
||||||
|
const filters = node.componentMeta.getMetadata().experimental?.filters;
|
||||||
|
if (filters && filters.length) {
|
||||||
|
const newProps = { ...props };
|
||||||
|
filters.forEach((item) => {
|
||||||
|
// FIXME! item.name could be 'xxx.xxx'
|
||||||
|
if (!hasOwnProperty(newProps, item.name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (item.filter(node.settingEntry.getProp(item.name), props[item.name]) === false) {
|
||||||
|
delete newProps[item.name];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e);
|
||||||
|
logger.trace(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newProps;
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
8
packages/vision-polyfill/src/props-reducers/index.ts
Normal file
8
packages/vision-polyfill/src/props-reducers/index.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export * from './downgrade-schema-reducer';
|
||||||
|
export * from './filter-reducer';
|
||||||
|
export * from './init-node-reducer';
|
||||||
|
export * from './live-lifecycle-reducer';
|
||||||
|
export * from './remove-empty-prop-reducer';
|
||||||
|
export * from './style-reducer';
|
||||||
|
export * from './upgrade-reducer';
|
||||||
|
export * from './node-top-fixed-reducer';
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
import {
|
||||||
|
hasOwnProperty,
|
||||||
|
isI18NObject,
|
||||||
|
isUseI18NSetter,
|
||||||
|
convertToI18NObject,
|
||||||
|
isString,
|
||||||
|
} from '@ali/lowcode-utils';
|
||||||
|
import { isJSExpression, isJSBlock, isJSSlot } from '@ali/lowcode-types';
|
||||||
|
import { isVariable, getCurrentFieldIds } from '../utils';
|
||||||
|
|
||||||
|
export function initNodeReducer(props, node) {
|
||||||
|
// run initials
|
||||||
|
const newProps: any = {
|
||||||
|
...props,
|
||||||
|
};
|
||||||
|
if (newProps.fieldId) {
|
||||||
|
const fieldIds = getCurrentFieldIds();
|
||||||
|
|
||||||
|
// 全局的关闭 uniqueIdChecker 信号,在 ve-utils 中实现
|
||||||
|
if (fieldIds.indexOf(props.fieldId) >= 0 && !(window as any).__disable_unique_id_checker__) {
|
||||||
|
newProps.fieldId = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const initials = node.componentMeta.getMetadata().experimental?.initials;
|
||||||
|
|
||||||
|
if (initials) {
|
||||||
|
const getRealValue = (propValue: any) => {
|
||||||
|
if (isVariable(propValue)) {
|
||||||
|
return propValue.value;
|
||||||
|
}
|
||||||
|
if (isJSExpression(propValue)) {
|
||||||
|
return propValue.mock;
|
||||||
|
}
|
||||||
|
return propValue;
|
||||||
|
};
|
||||||
|
initials.forEach(item => {
|
||||||
|
// FIXME! this implements SettingTarget
|
||||||
|
try {
|
||||||
|
// FIXME! item.name could be 'xxx.xxx'
|
||||||
|
const ov = newProps[item.name];
|
||||||
|
const v = item.initial(node as any, getRealValue(ov));
|
||||||
|
if (ov === undefined && v !== undefined) {
|
||||||
|
newProps[item.name] = v;
|
||||||
|
}
|
||||||
|
// 兼容 props 中的属性为 i18n 类型,但是仅提供了一个字符串值,非变量绑定
|
||||||
|
if (
|
||||||
|
isUseI18NSetter(node.componentMeta.prototype, item.name) &&
|
||||||
|
!isI18NObject(ov) &&
|
||||||
|
!isJSExpression(ov) &&
|
||||||
|
!isJSBlock(ov) &&
|
||||||
|
!isJSSlot(ov) &&
|
||||||
|
!isVariable(ov) &&
|
||||||
|
(isString(v) || isI18NObject(v))
|
||||||
|
) {
|
||||||
|
newProps[item.name] = convertToI18NObject(v);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (hasOwnProperty(props, item.name)) {
|
||||||
|
newProps[item.name] = props[item.name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newProps[item.name] && !node.props.has(item.name)) {
|
||||||
|
node.props.add(newProps[item.name], item.name, false, { skipSetSlot: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return newProps;
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
import { editor } from '@ali/lowcode-engine';
|
||||||
|
import { Node } from '@ali/lowcode-designer';
|
||||||
|
|
||||||
|
export function liveLifecycleReducer(props: any, node: Node) {
|
||||||
|
// live 模式下解析 lifeCycles
|
||||||
|
if (node.isRoot() && props && props.lifeCycles) {
|
||||||
|
if (editor.get('designMode') === 'live') {
|
||||||
|
const lifeCycleMap = {
|
||||||
|
didMount: 'componentDidMount',
|
||||||
|
willUnmount: 'componentWillUnMount',
|
||||||
|
};
|
||||||
|
const lifeCycles = props.lifeCycles;
|
||||||
|
Object.keys(lifeCycleMap).forEach(key => {
|
||||||
|
if (lifeCycles[key]) {
|
||||||
|
lifeCycles[lifeCycleMap[key]] = lifeCycles[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
lifeCycles: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import { Node } from '@ali/lowcode-designer';
|
||||||
|
|
||||||
|
export function nodeTopFixedReducer(props: any, node: Node) {
|
||||||
|
if (node.componentMeta.isTopFixed) {
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
// experimental prop value
|
||||||
|
__isTopFixed__: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import {
|
||||||
|
cloneDeep,
|
||||||
|
} from '@ali/lowcode-utils';
|
||||||
|
|
||||||
|
// 清除空的 props value
|
||||||
|
export function removeEmptyPropsReducer(props: any, node: Node) {
|
||||||
|
if (node.isRoot() && props.dataSource && Array.isArray(props.dataSource.online)) {
|
||||||
|
const online = cloneDeep(props.dataSource.online);
|
||||||
|
online.forEach((item: any) => {
|
||||||
|
const newParam: any = {};
|
||||||
|
if (Array.isArray(item?.options?.params)) {
|
||||||
|
item.options.params.forEach((element: any) => {
|
||||||
|
if (element.name) {
|
||||||
|
newParam[element.name] = element.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item.options.params = newParam;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
props.dataSource.list = online;
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
48
packages/vision-polyfill/src/props-reducers/style-reducer.ts
Normal file
48
packages/vision-polyfill/src/props-reducers/style-reducer.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { editor, designer } from '@ali/lowcode-engine';
|
||||||
|
import { toCss } from '@ali/vu-css-style';
|
||||||
|
|
||||||
|
export function stylePropsReducer(props: any, node: any) {
|
||||||
|
if (props && typeof props === 'object' && props.__style__) {
|
||||||
|
const cssId = `_style_pesudo_${ node.id.replace(/\$/g, '_')}`;
|
||||||
|
const cssClass = `_css_pesudo_${ node.id.replace(/\$/g, '_')}`;
|
||||||
|
const styleProp = props.__style__;
|
||||||
|
appendStyleNode(props, styleProp, cssClass, cssId);
|
||||||
|
}
|
||||||
|
if (props && typeof props === 'object' && props.pageStyle) {
|
||||||
|
const cssId = '_style_pesudo_engine-document';
|
||||||
|
const cssClass = 'engine-document';
|
||||||
|
const styleProp = props.pageStyle;
|
||||||
|
appendStyleNode(props, styleProp, cssClass, cssId);
|
||||||
|
}
|
||||||
|
if (props && typeof props === 'object' && props.containerStyle) {
|
||||||
|
const cssId = `_style_pesudo_${ node.id}`;
|
||||||
|
const cssClass = `_css_pesudo_${ node.id.replace(/\$/g, '_')}`;
|
||||||
|
const styleProp = props.containerStyle;
|
||||||
|
appendStyleNode(props, styleProp, cssClass, cssId);
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendStyleNode(props: any, styleProp: any, cssClass: string, cssId: string) {
|
||||||
|
const doc = designer.currentDocument?.simulator?.contentDocument;
|
||||||
|
if (!doc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const dom = doc.getElementById(cssId);
|
||||||
|
if (dom) {
|
||||||
|
dom.parentNode?.removeChild(dom);
|
||||||
|
}
|
||||||
|
if (typeof styleProp === 'object') {
|
||||||
|
styleProp = toCss(styleProp);
|
||||||
|
}
|
||||||
|
if (typeof styleProp === 'string') {
|
||||||
|
const s = doc.createElement('style');
|
||||||
|
props.className = cssClass;
|
||||||
|
s.setAttribute('type', 'text/css');
|
||||||
|
s.setAttribute('id', cssId);
|
||||||
|
doc.getElementsByTagName('head')[0].appendChild(s);
|
||||||
|
s.appendChild(doc.createTextNode(styleProp.replace(/(\d+)rpx/g, (a, b) => {
|
||||||
|
return `${b / 2}px`;
|
||||||
|
}).replace(/:root/g, `.${cssClass}`)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
import {
|
||||||
|
isPlainObject,
|
||||||
|
} from '@ali/lowcode-utils';
|
||||||
|
import { isJSBlock } from '@ali/lowcode-types';
|
||||||
|
import { isVariable } from '../utils';
|
||||||
|
import { designerHelper } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
const { getConvertedExtraKey } = designerHelper;
|
||||||
|
|
||||||
|
export function upgradePropsReducer(props: any) {
|
||||||
|
if (!props || !isPlainObject(props)) {
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
if (isJSBlock(props)) {
|
||||||
|
if (props.value.componentName === 'Slot') {
|
||||||
|
return {
|
||||||
|
type: 'JSSlot',
|
||||||
|
title: (props.value.props as any)?.slotTitle,
|
||||||
|
name: (props.value.props as any)?.slotName,
|
||||||
|
value: props.value.children,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return props.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isVariable(props)) {
|
||||||
|
return {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: props.variable,
|
||||||
|
mock: props.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const newProps: any = {};
|
||||||
|
Object.keys(props).forEach((key) => {
|
||||||
|
if (/^__slot__/.test(key) && props[key] === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newProps[key] = upgradePropsReducer(props[key]);
|
||||||
|
});
|
||||||
|
return newProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function upgradePageLifeCyclesReducer(props: any, node: Node) {
|
||||||
|
const lifeCycleNames = ['didMount', 'willUnmount'];
|
||||||
|
if (node.isRoot()) {
|
||||||
|
lifeCycleNames.forEach(key => {
|
||||||
|
if (props[key]) {
|
||||||
|
const lifeCycles = node.props.getPropValue(getConvertedExtraKey('lifeCycles')) || {};
|
||||||
|
lifeCycles[key] = props[key];
|
||||||
|
node.props.setPropValue(getConvertedExtraKey('lifeCycles'), lifeCycles);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return props;
|
||||||
|
}
|
||||||
58
packages/vision-polyfill/src/reducers.ts
Normal file
58
packages/vision-polyfill/src/reducers.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { isJSBlock, isJSExpression, isJSSlot } from '@ali/lowcode-types';
|
||||||
|
import { isPlainObject, hasOwnProperty, cloneDeep, isI18NObject, isUseI18NSetter, convertToI18NObject, isString } from '@ali/lowcode-utils';
|
||||||
|
import { editor, designer, designerHelper } from '@ali/lowcode-engine';
|
||||||
|
import bus from './bus';
|
||||||
|
import { VE_EVENTS } from './base/const';
|
||||||
|
|
||||||
|
import { deepValueParser } from './deep-value-parser';
|
||||||
|
import { liveEditingRule, liveEditingSaveHander } from './vc-live-editing';
|
||||||
|
import {
|
||||||
|
compatibleReducer,
|
||||||
|
upgradePageLifeCyclesReducer,
|
||||||
|
stylePropsReducer,
|
||||||
|
upgradePropsReducer,
|
||||||
|
filterReducer,
|
||||||
|
removeEmptyPropsReducer,
|
||||||
|
initNodeReducer,
|
||||||
|
liveLifecycleReducer,
|
||||||
|
nodeTopFixedReducer,
|
||||||
|
} from './props-reducers';
|
||||||
|
|
||||||
|
const { LiveEditing, TransformStage } = designerHelper;
|
||||||
|
|
||||||
|
LiveEditing.addLiveEditingSpecificRule(liveEditingRule);
|
||||||
|
LiveEditing.addLiveEditingSaveHandler(liveEditingSaveHander);
|
||||||
|
|
||||||
|
designer.project.onCurrentDocumentChange((doc) => {
|
||||||
|
bus.emit(VE_EVENTS.VE_PAGE_PAGE_READY);
|
||||||
|
editor.set('currentDocument', doc);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 升级 Props
|
||||||
|
designer.addPropsReducer(upgradePropsReducer, TransformStage.Upgrade);
|
||||||
|
|
||||||
|
// 节点 props 初始化
|
||||||
|
designer.addPropsReducer(initNodeReducer, TransformStage.Init);
|
||||||
|
|
||||||
|
designer.addPropsReducer(liveLifecycleReducer, TransformStage.Render);
|
||||||
|
|
||||||
|
designer.addPropsReducer(filterReducer, TransformStage.Save);
|
||||||
|
designer.addPropsReducer(filterReducer, TransformStage.Render);
|
||||||
|
|
||||||
|
// FIXME: Dirty fix, will remove this reducer
|
||||||
|
designer.addPropsReducer(compatibleReducer, TransformStage.Save);
|
||||||
|
// 兼容历史版本的 Page 组件
|
||||||
|
designer.addPropsReducer(upgradePageLifeCyclesReducer, TransformStage.Save);
|
||||||
|
|
||||||
|
// 设计器组件样式处理
|
||||||
|
designer.addPropsReducer(stylePropsReducer, TransformStage.Render);
|
||||||
|
// 国际化 & Expression 渲染时处理
|
||||||
|
designer.addPropsReducer(deepValueParser, TransformStage.Render);
|
||||||
|
|
||||||
|
// Init 的时候没有拿到 dataSource, 只能在 Render 和 Save 的时候都调用一次,理论上执行时机在 Init
|
||||||
|
// Render 和 Save 都要各调用一次,感觉也是有问题的,是不是应该在 Render 执行一次就行了?见上 filterReducer 也是一样的处理方式。
|
||||||
|
designer.addPropsReducer(removeEmptyPropsReducer, TransformStage.Render);
|
||||||
|
designer.addPropsReducer(removeEmptyPropsReducer, TransformStage.Save);
|
||||||
|
|
||||||
|
designer.addPropsReducer(nodeTopFixedReducer, TransformStage.Render);
|
||||||
|
designer.addPropsReducer(nodeTopFixedReducer, TransformStage.Save);
|
||||||
95
packages/vision-polyfill/src/rootNodeVisitor.ts
Normal file
95
packages/vision-polyfill/src/rootNodeVisitor.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { findIndex } from 'lodash';
|
||||||
|
import { DocumentModel, Node, RootNode } from '@ali/lowcode-designer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RootNodeVisitor for VisualEngine Page
|
||||||
|
*
|
||||||
|
* - store / cache node
|
||||||
|
* - quickly find / search or do operations on Node
|
||||||
|
*/
|
||||||
|
export default class RootNodeVisitor {
|
||||||
|
public nodeIdMap: {[id: string]: Node} = {};
|
||||||
|
|
||||||
|
public nodeFieldIdMap: {[fieldId: string]: Node} = {};
|
||||||
|
|
||||||
|
public nodeList: Node[] = [];
|
||||||
|
|
||||||
|
private page: DocumentModel;
|
||||||
|
|
||||||
|
private root: RootNode;
|
||||||
|
|
||||||
|
private cancelers: Function[] = [];
|
||||||
|
|
||||||
|
constructor(page: DocumentModel, rootNode: RootNode) {
|
||||||
|
this.page = page;
|
||||||
|
this.root = rootNode;
|
||||||
|
|
||||||
|
this._findNode(this.root);
|
||||||
|
this._init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNodeList() {
|
||||||
|
return this.nodeList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNodeIdMap() {
|
||||||
|
return this.nodeIdMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNodeFieldIdMap() {
|
||||||
|
return this.nodeFieldIdMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNodeById(id?: string) {
|
||||||
|
if (!id) { return this.nodeIdMap; }
|
||||||
|
return this.nodeIdMap[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNodeByFieldId(fieldId?: string) {
|
||||||
|
if (!fieldId) { return this.nodeFieldIdMap; }
|
||||||
|
return this.nodeFieldIdMap[fieldId];
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
this.cancelers.forEach((canceler) => canceler());
|
||||||
|
}
|
||||||
|
|
||||||
|
private _init() {
|
||||||
|
this.cancelers.push(
|
||||||
|
this.page.onNodeCreate((node) => {
|
||||||
|
this.nodeList.push(node);
|
||||||
|
this.nodeIdMap[node.id] = node;
|
||||||
|
if (node.getPropValue('fieldId')) {
|
||||||
|
this.nodeFieldIdMap[node.getPropValue('fieldId')] = node;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.cancelers.push(
|
||||||
|
this.page.onNodeDestroy((node) => {
|
||||||
|
const idx = findIndex(this.nodeList, (n) => node.id === n.id);
|
||||||
|
this.nodeList.splice(idx, 1);
|
||||||
|
delete this.nodeIdMap[node.id];
|
||||||
|
if (node.getPropValue('fieldId')) {
|
||||||
|
delete this.nodeFieldIdMap[node.getPropValue('fieldId')];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _findNode(node: Node) {
|
||||||
|
const props = node.getProps();
|
||||||
|
const fieldId = props && props.getPropValue('fieldId');
|
||||||
|
|
||||||
|
this.nodeIdMap[node.getId()] = node;
|
||||||
|
this.nodeList.push(node);
|
||||||
|
if (fieldId) {
|
||||||
|
this.nodeFieldIdMap[fieldId] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const children = node.getChildren();
|
||||||
|
if (children) {
|
||||||
|
children.forEach((child) => this._findNode(child));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
packages/vision-polyfill/src/symbols.ts
Normal file
17
packages/vision-polyfill/src/symbols.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export class SymbolManager {
|
||||||
|
private symbolMap: { [symbolName: string]: symbol } = {};
|
||||||
|
|
||||||
|
public create(name: string): symbol {
|
||||||
|
if (this.symbolMap[name]) {
|
||||||
|
return this.symbolMap[name];
|
||||||
|
}
|
||||||
|
this.symbolMap[name] = Symbol(name);
|
||||||
|
return this.symbolMap[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(name: string) {
|
||||||
|
return this.symbolMap[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new SymbolManager();
|
||||||
30
packages/vision-polyfill/src/utils/index.ts
Normal file
30
packages/vision-polyfill/src/utils/index.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { designer } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
interface Variable {
|
||||||
|
type: 'variable';
|
||||||
|
variable: string;
|
||||||
|
value: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isVariable(obj: any): obj is Variable {
|
||||||
|
return obj && obj.type === 'variable';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCurrentFieldIds() {
|
||||||
|
const fieldIds: any = [];
|
||||||
|
const nodesMap = designer?.currentDocument?.nodesMap || new Map();
|
||||||
|
nodesMap.forEach((curNode: any) => {
|
||||||
|
const fieldId = nodesMap?.get(curNode.id)?.getPropValue('fieldId');
|
||||||
|
if (fieldId) {
|
||||||
|
fieldIds.push(fieldId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return fieldIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function invariant(check: any, message: string, thing?: any) {
|
||||||
|
if (!check) {
|
||||||
|
throw new Error('Invariant failed: ' + message + (thing ? ` in '${thing}'` : ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
116
packages/vision-polyfill/src/vc-live-editing.ts
Normal file
116
packages/vision-polyfill/src/vc-live-editing.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import { EditingTarget, Node as DocNode, SaveHandler } from '@ali/lowcode-designer';
|
||||||
|
import Env from './env';
|
||||||
|
import { isJSExpression, isI18nData } from '@ali/lowcode-types';
|
||||||
|
import i18nUtil from './i18n-util';
|
||||||
|
|
||||||
|
interface I18nObject {
|
||||||
|
type?: string;
|
||||||
|
use?: string;
|
||||||
|
key?: string;
|
||||||
|
[lang: string]: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getI18nText(obj: I18nObject) {
|
||||||
|
let locale = Env.getLocale();
|
||||||
|
if (obj.key) {
|
||||||
|
return i18nUtil.get(obj.key, locale);
|
||||||
|
}
|
||||||
|
if (locale !== 'zh_CN' && locale !== 'zh_TW' && !obj[locale]) {
|
||||||
|
locale = 'en_US';
|
||||||
|
}
|
||||||
|
return obj[obj.use || locale] || obj.zh_CN;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getText(node: DocNode, prop: string) {
|
||||||
|
const p = node.getProp(prop, false);
|
||||||
|
if (!p || p.isUnset()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let v = p.getValue();
|
||||||
|
if (isJSExpression(v)) {
|
||||||
|
v = v.mock;
|
||||||
|
}
|
||||||
|
if (v == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (p.type === 'literal') {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
if ((v as any).type === 'i18n') {
|
||||||
|
return getI18nText(v as any);
|
||||||
|
}
|
||||||
|
return Symbol.for('not-literal');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function liveEditingRule(target: EditingTarget) {
|
||||||
|
// for vision components specific
|
||||||
|
const { node, rootElement, event } = target;
|
||||||
|
|
||||||
|
const targetElement = event.target as HTMLElement;
|
||||||
|
|
||||||
|
if (!Array.from(targetElement.childNodes).every(item => item.nodeType === Node.TEXT_NODE)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { innerText } = targetElement;
|
||||||
|
const propTarget = ['title', 'label', 'text', 'content'].find(prop => {
|
||||||
|
return equalText(getText(node, prop), innerText);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (propTarget) {
|
||||||
|
return {
|
||||||
|
propElement: targetElement,
|
||||||
|
propTarget,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function equalText(v: any, innerText: string) {
|
||||||
|
// TODO: enhance compare text logic
|
||||||
|
if (typeof v !== 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return v.trim() === innerText;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const liveEditingSaveHander: SaveHandler = {
|
||||||
|
condition: (prop) => {
|
||||||
|
const v = prop.getValue();
|
||||||
|
return prop.type === 'expression' || isI18nData(v);
|
||||||
|
},
|
||||||
|
onSaveContent: (content, prop) => {
|
||||||
|
const v = prop.getValue();
|
||||||
|
const locale = Env.getLocale();
|
||||||
|
let data = v;
|
||||||
|
if (isJSExpression(v)) {
|
||||||
|
data = v.mock;
|
||||||
|
}
|
||||||
|
if (isI18nData(data)) {
|
||||||
|
const i18n = data.key ? i18nUtil.getItem(data.key) : null;
|
||||||
|
if (i18n) {
|
||||||
|
i18n.setDoc(content, locale);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
data = {
|
||||||
|
...(data as any),
|
||||||
|
[locale]: content,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
data = content;
|
||||||
|
}
|
||||||
|
if (isJSExpression(v)) {
|
||||||
|
prop.setValue({
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: v.value,
|
||||||
|
mock: data,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
prop.setValue(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// TODO:
|
||||||
|
// 非文本编辑
|
||||||
|
// 国际化数据,改变当前
|
||||||
|
// JSExpression, 改变 mock 或 弹出绑定变量
|
||||||
289
packages/vision-polyfill/src/viewport.ts
Normal file
289
packages/vision-polyfill/src/viewport.ts
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
import { EventEmitter } from 'events';
|
||||||
|
import Flags from './flags';
|
||||||
|
import { editor } from '@ali/lowcode-engine';
|
||||||
|
|
||||||
|
const domReady = require('domready');
|
||||||
|
|
||||||
|
function enterFullscreen() {
|
||||||
|
const elem = document.documentElement;
|
||||||
|
if (elem.requestFullscreen) {
|
||||||
|
elem.requestFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function exitFullscreen() {
|
||||||
|
if (document.exitFullscreen) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isFullscreen() {
|
||||||
|
return document.fullscreen || false;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IStyleResourceConfig {
|
||||||
|
media?: string;
|
||||||
|
type?: string;
|
||||||
|
content?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class StyleResource {
|
||||||
|
config: IStyleResourceConfig;
|
||||||
|
|
||||||
|
styleElement: HTMLStyleElement;
|
||||||
|
|
||||||
|
mounted: boolean;
|
||||||
|
|
||||||
|
inited: boolean;
|
||||||
|
|
||||||
|
constructor(config: IStyleResourceConfig) {
|
||||||
|
this.config = config || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
matchDevice(device: string) {
|
||||||
|
const { media } = this.config;
|
||||||
|
|
||||||
|
if (!media || media === 'ALL' || media === '*') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return media.toUpperCase() === device.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
if (this.inited) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.inited = true;
|
||||||
|
|
||||||
|
const { type, content } = this.config;
|
||||||
|
let styleElement: any;
|
||||||
|
if (type === 'URL') {
|
||||||
|
styleElement = document.createElement('link');
|
||||||
|
styleElement.href = content || '';
|
||||||
|
styleElement.rel = 'stylesheet';
|
||||||
|
} else {
|
||||||
|
styleElement = document.createElement('style');
|
||||||
|
styleElement.setAttribute('type', 'text/css');
|
||||||
|
if (styleElement.styleSheet) {
|
||||||
|
styleElement.styleSheet.cssText = content;
|
||||||
|
} else {
|
||||||
|
styleElement.appendChild(document.createTextNode(content || ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.styleElement = styleElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply() {
|
||||||
|
if (this.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
document.head.appendChild(this.styleElement);
|
||||||
|
this.mounted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unmount() {
|
||||||
|
if (!this.mounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
document.head.removeChild(this.styleElement);
|
||||||
|
this.mounted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Viewport {
|
||||||
|
preview: boolean;
|
||||||
|
|
||||||
|
focused: boolean;
|
||||||
|
|
||||||
|
slateFixed: boolean;
|
||||||
|
|
||||||
|
emitter: EventEmitter;
|
||||||
|
|
||||||
|
device: string;
|
||||||
|
|
||||||
|
focusTarget: any;
|
||||||
|
|
||||||
|
cssResourceSet: StyleResource[];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.preview = false;
|
||||||
|
this.emitter = new EventEmitter();
|
||||||
|
document.addEventListener('webkitfullscreenchange', () => {
|
||||||
|
this.emitter.emit('fullscreenchange', this.isFullscreen());
|
||||||
|
});
|
||||||
|
domReady(() => this.applyMediaCSS());
|
||||||
|
}
|
||||||
|
|
||||||
|
setFullscreen(flag: boolean) {
|
||||||
|
const fullscreen = this.isFullscreen();
|
||||||
|
if (fullscreen && !flag) {
|
||||||
|
exitFullscreen();
|
||||||
|
} else if (!fullscreen && flag) {
|
||||||
|
enterFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleFullscreen() {
|
||||||
|
if (this.isFullscreen()) {
|
||||||
|
exitFullscreen();
|
||||||
|
} else {
|
||||||
|
enterFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isFullscreen() {
|
||||||
|
return isFullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocus(flag: boolean) {
|
||||||
|
if (this.focused && !flag) {
|
||||||
|
this.focused = false;
|
||||||
|
Flags.remove('view-focused');
|
||||||
|
this.emitter.emit('focuschange', false);
|
||||||
|
} else if (!this.focused && flag) {
|
||||||
|
this.focused = true;
|
||||||
|
Flags.add('view-focused');
|
||||||
|
this.emitter.emit('focuschange', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocusTarget(focusTarget: any) {
|
||||||
|
this.focusTarget = focusTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
returnFocus() {
|
||||||
|
if (this.focusTarget) {
|
||||||
|
this.focusTarget.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isFocus() {
|
||||||
|
return this.focused;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPreview(flag: boolean) {
|
||||||
|
if (this.preview && !flag) {
|
||||||
|
this.preview = false;
|
||||||
|
Flags.setPreviewMode(false);
|
||||||
|
this.emitter.emit('preview', false);
|
||||||
|
this.changeViewport();
|
||||||
|
} else if (!this.preview && flag) {
|
||||||
|
this.preview = true;
|
||||||
|
Flags.setPreviewMode(true);
|
||||||
|
this.emitter.emit('preview', true);
|
||||||
|
this.changeViewport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
togglePreview() {
|
||||||
|
if (this.isPreview()) {
|
||||||
|
this.setPreview(false);
|
||||||
|
} else {
|
||||||
|
this.setPreview(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isPreview() {
|
||||||
|
return this.preview;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setDevice(device = 'pc') {
|
||||||
|
if (this.getDevice() !== device) {
|
||||||
|
this.device = device;
|
||||||
|
const simulator = await editor.onceGot('simulator');
|
||||||
|
simulator?.set('device', device === 'mobile' ? 'mobile' : 'default');
|
||||||
|
// Flags.setSimulator(device);
|
||||||
|
// this.applyMediaCSS();
|
||||||
|
this.emitter.emit('devicechange', device);
|
||||||
|
this.changeViewport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getDevice() {
|
||||||
|
return this.device || 'pc';
|
||||||
|
}
|
||||||
|
|
||||||
|
changeViewport() {
|
||||||
|
this.emitter.emit('viewportchange', this.getViewport());
|
||||||
|
}
|
||||||
|
|
||||||
|
getViewport() {
|
||||||
|
return `${this.isPreview() ? 'preview' : 'design'}-${this.getDevice()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMediaCSS() {
|
||||||
|
if (!document.head || !this.cssResourceSet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const device = this.getDevice();
|
||||||
|
this.cssResourceSet.forEach((item) => {
|
||||||
|
if (item.matchDevice(device)) {
|
||||||
|
item.apply();
|
||||||
|
} else {
|
||||||
|
item.unmount();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setGlobalCSS(resourceSet: IStyleResourceConfig[]) {
|
||||||
|
if (this.cssResourceSet) {
|
||||||
|
this.cssResourceSet.forEach((item) => {
|
||||||
|
item.unmount();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.cssResourceSet = resourceSet.map((item: IStyleResourceConfig) => new StyleResource(item)).reverse();
|
||||||
|
this.applyMediaCSS();
|
||||||
|
}
|
||||||
|
|
||||||
|
setWithShell(shell: string) {
|
||||||
|
// Flags.setWithShell(shell);
|
||||||
|
}
|
||||||
|
|
||||||
|
onFullscreenChange(func: () => any) {
|
||||||
|
this.emitter.on('fullscreenchange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('fullscreenchange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onPreview(func: () => any) {
|
||||||
|
this.emitter.on('preview', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('preview', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeviceChange(func: () => any) {
|
||||||
|
this.emitter.on('devicechange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('devicechange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onSlateFixedChange(func: (flag: boolean) => any) {
|
||||||
|
this.emitter.on('slatefixed', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('slatefixed', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onViewportChange(func: () => any) {
|
||||||
|
this.emitter.on('viewportchange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('viewportchange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onFocusChange(func: (flag: boolean) => any) {
|
||||||
|
this.emitter.on('focuschange', func);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('focuschange', func);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Viewport();
|
||||||
128
packages/vision-polyfill/src/vision.less
Normal file
128
packages/vision-polyfill/src/vision.less
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
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: fixed;
|
||||||
|
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 {
|
||||||
|
filter: blur(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-main {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
|
||||||
|
.ve-icon-button {
|
||||||
|
> .ve-icon-contents {
|
||||||
|
color: var(--color-text, rgba(51,51,51,.6));
|
||||||
|
}
|
||||||
|
&:hover, &.active {
|
||||||
|
> .ve-icon-contents {
|
||||||
|
color: var(--color-text-light, rgba(51,51,51,.8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-empty {
|
||||||
|
background: #f2f3f5;
|
||||||
|
color: #a7b1bd;
|
||||||
|
outline: 1px dashed rgba(31, 56, 88, 0.2);
|
||||||
|
outline-offset: -1px !important;
|
||||||
|
height: 66px;
|
||||||
|
max-height: 100%;
|
||||||
|
min-width: 140px;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.engine-empty:before {
|
||||||
|
content: '\62D6\62FD\7EC4\4EF6\6216\6A21\677F\5230\8FD9\91CC';
|
||||||
|
font-size: 14px;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// dirty fix override vision reset
|
||||||
|
.engine-design-mode {
|
||||||
|
.next-input-group,
|
||||||
|
.next-checkbox-group,.next-date-picker,.next-input,.next-month-picker,
|
||||||
|
.next-number-picker,.next-radio-group,.next-range,.next-range-picker,
|
||||||
|
.next-rating,.next-select,.next-switch,.next-time-picker,.next-upload,
|
||||||
|
.next-year-picker,
|
||||||
|
.next-breadcrumb-item,.next-calendar-header,.next-calendar-table {
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vs-icon .vs-icon-del, .vs-icon .vs-icon-entry {
|
||||||
|
width: 16px!important;
|
||||||
|
height: 16px!important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .lc-left-float-pane {
|
||||||
|
// font-size: 14px;
|
||||||
|
// }
|
||||||
|
|
||||||
|
html.engine-preview-mode {
|
||||||
|
.lc-left-area, .lc-right-area {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ve-popups .ve-message {
|
||||||
|
right: calc(var(--right-area-width, 300px) + 10px);
|
||||||
|
|
||||||
|
.ve-message-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lc-setter-mixed {
|
||||||
|
flex: 1
|
||||||
|
}
|
||||||
116
packages/vision-polyfill/tests/bundle/bundle.test.ts
Normal file
116
packages/vision-polyfill/tests/bundle/bundle.test.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import { Component } from 'react';
|
||||||
|
import set from 'lodash/set';
|
||||||
|
import cloneDeep from 'lodash/clonedeep';
|
||||||
|
import '../fixtures/window';
|
||||||
|
import divPrototypeConfig from '../fixtures/prototype/div-vision';
|
||||||
|
import trunk from '../../src/bundle/trunk';
|
||||||
|
import Prototype from '../../src/bundle/prototype';
|
||||||
|
import Bundle from '../../src/bundle/bundle';
|
||||||
|
import { Editor } from '@ali/lowcode-editor-core';
|
||||||
|
|
||||||
|
jest.mock('../../src/bundle/trunk', () => {
|
||||||
|
// mockComponentPrototype = jest.fn();
|
||||||
|
// return {
|
||||||
|
// mockComponentPrototype: jest.fn().mockImplementation(() => {
|
||||||
|
// return proto;
|
||||||
|
// }),
|
||||||
|
// }
|
||||||
|
// return jest.fn().mockImplementation(() => {
|
||||||
|
// return {playSoundFile: fakePlaySoundFile};
|
||||||
|
// });
|
||||||
|
// return jest.fn().mockImplementation(() => {
|
||||||
|
// return { mockComponentPrototype };
|
||||||
|
// });
|
||||||
|
return {
|
||||||
|
__esModule: true,
|
||||||
|
default: {
|
||||||
|
mockComponentPrototype: jest.fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function wrap(name, thing) {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
componentName: name,
|
||||||
|
category: '布局',
|
||||||
|
module: thing,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const proto1 = new Prototype(divPrototypeConfig);
|
||||||
|
const protoConfig2 = cloneDeep(divPrototypeConfig);
|
||||||
|
set(protoConfig2, 'componentName', 'Div2');
|
||||||
|
const proto2 = new Prototype(protoConfig2);
|
||||||
|
|
||||||
|
const protoConfig3 = cloneDeep(divPrototypeConfig);
|
||||||
|
set(protoConfig3, 'componentName', 'Div3');
|
||||||
|
const proto3 = new Prototype(protoConfig3);
|
||||||
|
|
||||||
|
const protoConfig4 = cloneDeep(divPrototypeConfig);
|
||||||
|
set(protoConfig4, 'componentName', 'Div4');
|
||||||
|
const proto4 = new Prototype(protoConfig4);
|
||||||
|
|
||||||
|
const protoConfig5 = cloneDeep(divPrototypeConfig);
|
||||||
|
set(protoConfig5, 'componentName', 'Div5');
|
||||||
|
const proto5 = new Prototype(protoConfig5);
|
||||||
|
|
||||||
|
function getComponentProtos() {
|
||||||
|
return [
|
||||||
|
wrap('Div', proto1),
|
||||||
|
// wrap('Div2', proto2),
|
||||||
|
// wrap('Div3', proto3),
|
||||||
|
wrap('DivPortal', [proto2, proto3]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
class Div extends Component {}
|
||||||
|
Div.displayName = 'Div';
|
||||||
|
class Div2 extends Component {}
|
||||||
|
Div2.displayName = 'Div2';
|
||||||
|
class Div3 extends Component {}
|
||||||
|
Div3.displayName = 'Div3';
|
||||||
|
class Div4 extends Component {}
|
||||||
|
Div4.displayName = 'Div4';
|
||||||
|
class Div5 extends Component {}
|
||||||
|
Div5.displayName = 'Div5';
|
||||||
|
|
||||||
|
function getComponentViews() {
|
||||||
|
return [
|
||||||
|
wrap('Div', Div),
|
||||||
|
// wrap('Div2', Div2),
|
||||||
|
// wrap('Div3', Div3),
|
||||||
|
wrap('DivPortal', [Div2, Div3]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Bundle', () => {
|
||||||
|
it('构造函数', () => {
|
||||||
|
const protos = getComponentProtos();
|
||||||
|
const views = getComponentViews();
|
||||||
|
const bundle = new Bundle(protos, views);
|
||||||
|
expect(bundle.getList()).toHaveLength(3);
|
||||||
|
expect(bundle.get('Div')).toBe(proto1);
|
||||||
|
expect(bundle.get('Div2')).toBe(proto2);
|
||||||
|
expect(bundle.get('Div3')).toBe(proto3);
|
||||||
|
bundle.addComponentBundle([proto4, Div4]);
|
||||||
|
expect(bundle.getList()).toHaveLength(4);
|
||||||
|
expect(bundle.get('Div4')).toBe(proto4);
|
||||||
|
bundle.replacePrototype('Div4', proto3);
|
||||||
|
expect(proto3.getView()).toBe(Div4);
|
||||||
|
|
||||||
|
bundle.removeComponentBundle('Div2');
|
||||||
|
expect(bundle.getList()).toHaveLength(3);
|
||||||
|
expect(bundle.get('Div2')).toBeUndefined;
|
||||||
|
|
||||||
|
expect(bundle.getFromMeta('Div')).toBe(proto1);
|
||||||
|
bundle.getFromMeta('Div5');
|
||||||
|
expect(bundle.getList()).toHaveLength(4);
|
||||||
|
});
|
||||||
|
it('静态方法 create', () => {
|
||||||
|
const protos = getComponentProtos();
|
||||||
|
const views = getComponentViews();
|
||||||
|
const bundle = Bundle.create(protos, views);
|
||||||
|
expect(bundle).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
219
packages/vision-polyfill/tests/bundle/prototype.test.ts
Normal file
219
packages/vision-polyfill/tests/bundle/prototype.test.ts
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import { Component } from 'react';
|
||||||
|
import set from 'lodash/set';
|
||||||
|
import cloneDeep from 'lodash/clonedeep';
|
||||||
|
import '../fixtures/window';
|
||||||
|
// import { Project } from '../../src/project/project';
|
||||||
|
// import { Node } from '../../src/document/node/node';
|
||||||
|
// import { Designer } from '../../src/designer/designer';
|
||||||
|
import divPrototypeConfig from '../fixtures/prototype/div-vision';
|
||||||
|
import divFullPrototypeConfig from '../fixtures/prototype/div-vision-full';
|
||||||
|
import divPrototypeMeta from '../fixtures/prototype/div-meta';
|
||||||
|
// import VisualEngine from '../../src';
|
||||||
|
import { designer } from '../../src/reducers';
|
||||||
|
import Prototype, { isPrototype } from '../../src/bundle/prototype';
|
||||||
|
import { Editor } from '@ali/lowcode-editor-core';
|
||||||
|
// import { getIdsFromSchema, getNodeFromSchemaById } from '../utils';
|
||||||
|
|
||||||
|
describe('Prototype', () => {
|
||||||
|
it('构造函数 - OldPrototypeConfig', () => {
|
||||||
|
const proto = new Prototype(divPrototypeConfig);
|
||||||
|
expect(isPrototype(proto)).toBeTruthy;
|
||||||
|
expect(proto.getComponentName()).toBe('Div');
|
||||||
|
expect(proto.getId()).toBe('Div');
|
||||||
|
expect(proto.getCategory()).toBe('布局');
|
||||||
|
expect(proto.getDocUrl()).toBe(
|
||||||
|
'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||||
|
);
|
||||||
|
expect(proto.getIcon()).toBeUndefined;
|
||||||
|
expect(proto.getTitle()).toBe('Div');
|
||||||
|
expect(proto.isPrototype).toBeTruthy;
|
||||||
|
expect(proto.isContainer()).toBeTruthy;
|
||||||
|
expect(proto.isModal()).toBeFalsy;
|
||||||
|
});
|
||||||
|
it('构造函数 - 全量 OldPrototypeConfig', () => {
|
||||||
|
const proto = new Prototype(divFullPrototypeConfig);
|
||||||
|
expect(isPrototype(proto)).toBeTruthy;
|
||||||
|
expect(proto.getComponentName()).toBe('Div');
|
||||||
|
expect(proto.getId()).toBe('Div');
|
||||||
|
expect(proto.getCategory()).toBe('布局');
|
||||||
|
expect(proto.getDocUrl()).toBe(
|
||||||
|
'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||||
|
);
|
||||||
|
expect(proto.getIcon()).toBeUndefined;
|
||||||
|
expect(proto.getTitle()).toBe('Div');
|
||||||
|
expect(proto.isPrototype).toBeTruthy;
|
||||||
|
expect(proto.isContainer()).toBeTruthy;
|
||||||
|
expect(proto.isModal()).toBeFalsy;
|
||||||
|
});
|
||||||
|
it('构造函数 - ComponentMetadata', () => {
|
||||||
|
const proto = new Prototype(divPrototypeMeta);
|
||||||
|
expect(proto.getComponentName()).toBe('Div');
|
||||||
|
expect(proto.getId()).toBe('Div');
|
||||||
|
expect(proto.getCategory()).toBe('布局');
|
||||||
|
expect(proto.getDocUrl()).toBe(
|
||||||
|
'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||||
|
);
|
||||||
|
expect(proto.getIcon()).toBeUndefined;
|
||||||
|
expect(proto.getTitle()).toBe('Div');
|
||||||
|
expect(proto.isPrototype).toBeTruthy;
|
||||||
|
expect(proto.isContainer()).toBeTruthy;
|
||||||
|
expect(proto.isModal()).toBeFalsy;
|
||||||
|
});
|
||||||
|
it('构造函数 - ComponentMeta', () => {
|
||||||
|
const meta = designer.createComponentMeta(divPrototypeMeta);
|
||||||
|
const proto = new Prototype(meta);
|
||||||
|
expect(proto.getComponentName()).toBe('Div');
|
||||||
|
expect(proto.getId()).toBe('Div');
|
||||||
|
expect(proto.getCategory()).toBe('布局');
|
||||||
|
expect(proto.getDocUrl()).toBe(
|
||||||
|
'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||||
|
);
|
||||||
|
expect(proto.getIcon()).toBeUndefined;
|
||||||
|
expect(proto.getTitle()).toBe('Div');
|
||||||
|
expect(proto.isPrototype).toBeTruthy;
|
||||||
|
expect(proto.isContainer()).toBeTruthy;
|
||||||
|
expect(proto.isModal()).toBeFalsy;
|
||||||
|
});
|
||||||
|
it('构造函数 - 静态函数 create', () => {
|
||||||
|
const proto = Prototype.create(divPrototypeConfig);
|
||||||
|
expect(proto.getComponentName()).toBe('Div');
|
||||||
|
expect(proto.getId()).toBe('Div');
|
||||||
|
expect(proto.getCategory()).toBe('布局');
|
||||||
|
expect(proto.getDocUrl()).toBe(
|
||||||
|
'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||||
|
);
|
||||||
|
expect(proto.getIcon()).toBeUndefined;
|
||||||
|
expect(proto.getTitle()).toBe('Div');
|
||||||
|
expect(proto.isPrototype).toBeTruthy;
|
||||||
|
expect(proto.isContainer()).toBeTruthy;
|
||||||
|
expect(proto.isModal()).toBeFalsy;
|
||||||
|
});
|
||||||
|
it('构造函数 - lookup: true', () => {
|
||||||
|
const proto = Prototype.create(divPrototypeConfig);
|
||||||
|
const proto2 = Prototype.create(divPrototypeConfig, {}, true);
|
||||||
|
expect(proto).toBe(proto2);
|
||||||
|
});
|
||||||
|
describe('类成员函数', () => {
|
||||||
|
let proto: Prototype = null;
|
||||||
|
beforeEach(() => {
|
||||||
|
proto = new Prototype(divPrototypeConfig);
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
proto = null;
|
||||||
|
});
|
||||||
|
it('各种函数', () => {
|
||||||
|
expect(proto.componentName).toBe('Div');
|
||||||
|
expect(proto.getComponentName()).toBe('Div');
|
||||||
|
expect(proto.getId()).toBe('Div');
|
||||||
|
expect(proto.getContextInfo('anything')).toBeUndefined;
|
||||||
|
expect(proto.getPropConfigs()).toBe(divPrototypeConfig);
|
||||||
|
expect(proto.getConfig()).toBe(divPrototypeConfig);
|
||||||
|
expect(proto.getConfig('componentName')).toBe('Div');
|
||||||
|
expect(proto.getConfig('configure')).toBe(divPrototypeConfig.configure);
|
||||||
|
expect(proto.getConfig('docUrl')).toBe(
|
||||||
|
'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||||
|
);
|
||||||
|
expect(proto.getConfig('title')).toBe('容器');
|
||||||
|
expect(proto.getConfig('isContainer')).toBeTruthy;
|
||||||
|
|
||||||
|
class MockView extends Component {}
|
||||||
|
|
||||||
|
expect(proto.getView()).toBeUndefined;
|
||||||
|
proto.setView(MockView);
|
||||||
|
expect(proto.getView()).toBe(MockView);
|
||||||
|
expect(proto.meta.getMetadata().experimental?.view).toBe(MockView);
|
||||||
|
|
||||||
|
expect(proto.getPackageName()).toBeUndefined;
|
||||||
|
proto.setPackageName('@ali/vc-div');
|
||||||
|
expect(proto.getPackageName()).toBe('@ali/vc-div');
|
||||||
|
|
||||||
|
expect(proto.getConfig('category')).toBe('布局');
|
||||||
|
proto.setCategory('布局 new');
|
||||||
|
expect(proto.getConfig('category')).toBe('布局 new');
|
||||||
|
|
||||||
|
expect(proto.getConfigure()).toHaveLength(3);
|
||||||
|
expect(proto.getConfigure()[0].name).toBe('#props');
|
||||||
|
expect(proto.getConfigure()[1].name).toBe('#styles');
|
||||||
|
expect(proto.getConfigure()[2].name).toBe('#advanced');
|
||||||
|
|
||||||
|
expect(proto.getRectSelector()).toBeUndefined;
|
||||||
|
expect(proto.isAutoGenerated()).toBeFalsy;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('类成员函数', () => {
|
||||||
|
it('addGlobalPropsConfigure', () => {
|
||||||
|
Prototype.addGlobalPropsConfigure({
|
||||||
|
name: 'globalInsertProp1',
|
||||||
|
});
|
||||||
|
const proto1 = new Prototype(divPrototypeConfig);
|
||||||
|
expect(proto1.getConfigure()[2].items).toHaveLength(4);
|
||||||
|
expect(proto1.getConfigure()[2].items[3].name).toBe('globalInsertProp1');
|
||||||
|
Prototype.addGlobalPropsConfigure({
|
||||||
|
name: 'globalInsertProp2',
|
||||||
|
});
|
||||||
|
const proto2 = new Prototype(divPrototypeConfig);
|
||||||
|
expect(proto2.getConfigure()[2].items).toHaveLength(5);
|
||||||
|
expect(proto1.getConfigure()[2].items[4].name).toBe('globalInsertProp2');
|
||||||
|
|
||||||
|
Prototype.addGlobalPropsConfigure({
|
||||||
|
name: 'globalInsertProp3',
|
||||||
|
position: 'top',
|
||||||
|
});
|
||||||
|
|
||||||
|
const proto3 = new Prototype(divPrototypeConfig);
|
||||||
|
expect(proto3.getConfigure()[0].items).toHaveLength(3);
|
||||||
|
expect(proto1.getConfigure()[0].items[0].name).toBe('globalInsertProp3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removeGlobalPropsConfigure', () => {
|
||||||
|
Prototype.removeGlobalPropsConfigure('globalInsertProp1');
|
||||||
|
Prototype.removeGlobalPropsConfigure('globalInsertProp2');
|
||||||
|
Prototype.removeGlobalPropsConfigure('globalInsertProp3');
|
||||||
|
const proto2 = new Prototype(divPrototypeConfig);
|
||||||
|
expect(proto2.getConfigure()[0].items).toHaveLength(2);
|
||||||
|
expect(proto2.getConfigure()[2].items).toHaveLength(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('overridePropsConfigure', () => {
|
||||||
|
Prototype.addGlobalPropsConfigure({
|
||||||
|
name: 'globalInsertProp1',
|
||||||
|
title: 'globalInsertPropTitle',
|
||||||
|
position: 'top',
|
||||||
|
});
|
||||||
|
const proto1 = new Prototype(divPrototypeConfig);
|
||||||
|
expect(proto1.getConfigure()[0].items).toHaveLength(3);
|
||||||
|
expect(proto1.getConfigure()[0].items[0].name).toBe('globalInsertProp1');
|
||||||
|
expect(proto1.getConfigure()[0].items[0].title).toBe('globalInsertPropTitle');
|
||||||
|
|
||||||
|
Prototype.overridePropsConfigure('Div', [
|
||||||
|
{
|
||||||
|
name: 'globalInsertProp1',
|
||||||
|
title: 'globalInsertPropTitleChanged',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const proto2 = new Prototype(divPrototypeConfig);
|
||||||
|
expect(proto2.getConfigure()[0].name).toBe('globalInsertProp1');
|
||||||
|
expect(proto2.getConfigure()[0].title).toBe('globalInsertPropTitleChanged');
|
||||||
|
|
||||||
|
Prototype.overridePropsConfigure('Div', {
|
||||||
|
globalInsertProp1: {
|
||||||
|
name: 'globalInsertProp1',
|
||||||
|
title: 'globalInsertPropTitleChanged new',
|
||||||
|
position: 'top',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const proto3 = new Prototype(divPrototypeConfig);
|
||||||
|
expect(proto3.getConfigure()[0].items[0].name).toBe('globalInsertProp1');
|
||||||
|
expect(proto3.getConfigure()[0].items[0].title).toBe('globalInsertPropTitleChanged new');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('addGlobalExtraActions', () => {
|
||||||
|
function haha() { return 'heihei'; }
|
||||||
|
Prototype.addGlobalExtraActions(haha);
|
||||||
|
const proto1 = new Prototype(divPrototypeConfig);
|
||||||
|
expect(proto1.meta.availableActions).toHaveLength(4);
|
||||||
|
expect(proto1.meta.availableActions[3].name).toBe('haha');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
111
packages/vision-polyfill/tests/bundle/trunk.test.ts
Normal file
111
packages/vision-polyfill/tests/bundle/trunk.test.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { Component } from 'react';
|
||||||
|
import set from 'lodash/set';
|
||||||
|
import cloneDeep from 'lodash/clonedeep';
|
||||||
|
import '../fixtures/window';
|
||||||
|
import divPrototypeConfig from '../fixtures/prototype/div-vision';
|
||||||
|
import Prototype from '../../src/bundle/prototype';
|
||||||
|
import Bundle from '../../src/bundle/bundle';
|
||||||
|
import trunk from '../../src/bundle/trunk';
|
||||||
|
import lg from '@ali/vu-logger';
|
||||||
|
|
||||||
|
const proto1 = new Prototype(divPrototypeConfig);
|
||||||
|
const protoConfig2 = cloneDeep(divPrototypeConfig);
|
||||||
|
set(protoConfig2, 'componentName', 'Div2');
|
||||||
|
const proto2 = new Prototype(protoConfig2);
|
||||||
|
|
||||||
|
const protoConfig3 = cloneDeep(divPrototypeConfig);
|
||||||
|
set(protoConfig3, 'componentName', 'Div3');
|
||||||
|
const proto3 = new Prototype(protoConfig3);
|
||||||
|
|
||||||
|
const mockComponentPrototype = jest.fn();
|
||||||
|
jest.mock('../../src/bundle/bundle', () => {
|
||||||
|
// return {
|
||||||
|
// mockComponentPrototype: jest.fn().mockImplementation(() => {
|
||||||
|
// return proto;
|
||||||
|
// }),
|
||||||
|
// }
|
||||||
|
// return jest.fn().mockImplementation(() => {
|
||||||
|
// return {playSoundFile: fakePlaySoundFile};
|
||||||
|
// });
|
||||||
|
return jest.fn().mockImplementation(() => {
|
||||||
|
return {
|
||||||
|
get: () => {},
|
||||||
|
getList: () => { return []; },
|
||||||
|
getFromMeta: () => {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockError = jest.fn();
|
||||||
|
jest.mock('@ali/vu-logger');
|
||||||
|
lg.error = mockError;
|
||||||
|
|
||||||
|
function wrap(name, thing) {
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
componentName: name,
|
||||||
|
category: '布局',
|
||||||
|
module: thing,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getComponentProtos() {
|
||||||
|
return [
|
||||||
|
wrap('Div', proto1),
|
||||||
|
// wrap('Div2', proto2),
|
||||||
|
// wrap('Div3', proto3),
|
||||||
|
wrap('DivPortal', [proto2, proto3]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
class Div extends Component {}
|
||||||
|
Div.displayName = 'Div';
|
||||||
|
class Div2 extends Component {}
|
||||||
|
Div2.displayName = 'Div2';
|
||||||
|
class Div3 extends Component {}
|
||||||
|
Div3.displayName = 'Div3';
|
||||||
|
|
||||||
|
function getComponentViews() {
|
||||||
|
return [
|
||||||
|
wrap('Div', Div),
|
||||||
|
// wrap('Div2', Div2),
|
||||||
|
// wrap('Div3', Div3),
|
||||||
|
wrap('DivPortal', [Div2, Div3]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Trunk', () => {
|
||||||
|
it('构造函数', () => {
|
||||||
|
const warn = console.warn = jest.fn();
|
||||||
|
const trunkChangeHandler = jest.fn();
|
||||||
|
const off = trunk.onTrunkChange(trunkChangeHandler);
|
||||||
|
trunk.addBundle(new Bundle([proto1], [Div]));
|
||||||
|
trunk.addBundle(new Bundle([proto2], [Div2]));
|
||||||
|
expect(trunkChangeHandler).toHaveBeenCalledTimes(2);
|
||||||
|
off();
|
||||||
|
trunk.addBundle(new Bundle([proto3], [Div3]));
|
||||||
|
expect(trunkChangeHandler).toHaveBeenCalledTimes(2);
|
||||||
|
trunk.getList();
|
||||||
|
trunk.getPrototype('Div');
|
||||||
|
trunk.getPrototypeById('Div');
|
||||||
|
trunk.getPrototypeView('Div');
|
||||||
|
trunk.listByCategory();
|
||||||
|
expect(trunk.mockComponentPrototype(new Bundle([proto3], [Div3]))).toBeUndefined;
|
||||||
|
expect(mockError).toHaveBeenCalled();
|
||||||
|
trunk.registerComponentPrototypeMocker({ mockPrototype: jest.fn().mockImplementation(() => { return proto3; }) });
|
||||||
|
expect(trunk.mockComponentPrototype(new Bundle([proto3], [Div3]))).toBe(proto3);
|
||||||
|
const hahaSetter = () => 'haha';
|
||||||
|
trunk.registerSetter('haha', hahaSetter);
|
||||||
|
expect(trunk.getSetter('haha')).toBe(hahaSetter);
|
||||||
|
trunk.getRecents(5);
|
||||||
|
trunk.setPackages();
|
||||||
|
expect(warn).toHaveBeenCalledTimes(1);
|
||||||
|
trunk.beforeLoadBundle();
|
||||||
|
expect(warn).toHaveBeenCalledTimes(2);
|
||||||
|
trunk.afterLoadBundle();
|
||||||
|
expect(warn).toHaveBeenCalledTimes(3);
|
||||||
|
trunk.getBundle();
|
||||||
|
expect(warn).toHaveBeenCalledTimes(4);
|
||||||
|
expect(trunk.isReady()).toBeTruthy;
|
||||||
|
});
|
||||||
|
});
|
||||||
259
packages/vision-polyfill/tests/fixtures/prototype/div-meta.ts
vendored
Normal file
259
packages/vision-polyfill/tests/fixtures/prototype/div-meta.ts
vendored
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
export default {
|
||||||
|
componentName: 'Div',
|
||||||
|
title: '容器',
|
||||||
|
docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||||
|
devMode: 'procode',
|
||||||
|
tags: ['布局'],
|
||||||
|
configure: {
|
||||||
|
props: [
|
||||||
|
{
|
||||||
|
type: 'field',
|
||||||
|
name: 'behavior',
|
||||||
|
title: '默认状态',
|
||||||
|
extraProps: {
|
||||||
|
display: 'inline',
|
||||||
|
defaultValue: 'NORMAL',
|
||||||
|
},
|
||||||
|
setter: {
|
||||||
|
componentName: 'MixedSetter',
|
||||||
|
props: {
|
||||||
|
setters: [
|
||||||
|
{
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
title: '普通',
|
||||||
|
value: 'NORMAL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '隐藏',
|
||||||
|
value: 'HIDDEN',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
loose: false,
|
||||||
|
cancelable: false,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
'VariableSetter',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'field',
|
||||||
|
name: '__style__',
|
||||||
|
title: {
|
||||||
|
label: '样式设置',
|
||||||
|
tip: '点击 ? 查看样式设置器用法指南',
|
||||||
|
docUrl: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||||
|
},
|
||||||
|
extraProps: {
|
||||||
|
display: 'accordion',
|
||||||
|
defaultValue: {},
|
||||||
|
},
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
advanced: true,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'group',
|
||||||
|
name: 'groupkh97h5kc',
|
||||||
|
title: '高级',
|
||||||
|
extraProps: {
|
||||||
|
display: 'accordion',
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'field',
|
||||||
|
name: 'fieldId',
|
||||||
|
title: {
|
||||||
|
label: '唯一标识',
|
||||||
|
},
|
||||||
|
extraProps: {
|
||||||
|
display: 'block',
|
||||||
|
},
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
placeholder: '请输入唯一标识',
|
||||||
|
multiline: false,
|
||||||
|
rows: 10,
|
||||||
|
required: false,
|
||||||
|
pattern: null,
|
||||||
|
maxLength: null,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'field',
|
||||||
|
name: 'useFieldIdAsDomId',
|
||||||
|
title: {
|
||||||
|
label: '将唯一标识用作 DOM ID',
|
||||||
|
},
|
||||||
|
extraProps: {
|
||||||
|
display: 'block',
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'field',
|
||||||
|
name: 'customClassName',
|
||||||
|
title: '自定义样式类',
|
||||||
|
extraProps: {
|
||||||
|
display: 'block',
|
||||||
|
defaultValue: '',
|
||||||
|
},
|
||||||
|
setter: {
|
||||||
|
componentName: 'MixedSetter',
|
||||||
|
props: {
|
||||||
|
setters: [
|
||||||
|
{
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
placeholder: null,
|
||||||
|
multiline: false,
|
||||||
|
rows: 10,
|
||||||
|
required: false,
|
||||||
|
pattern: null,
|
||||||
|
maxLength: null,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
'VariableSetter',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'field',
|
||||||
|
name: 'events',
|
||||||
|
title: {
|
||||||
|
label: '动作设置',
|
||||||
|
tip: '点击 ? 查看如何设置组件的事件响应动作',
|
||||||
|
docUrl: 'https://lark.alipay.com/legao/legao/events-call',
|
||||||
|
},
|
||||||
|
extraProps: {
|
||||||
|
display: 'accordion',
|
||||||
|
defaultValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
name: 'onClick',
|
||||||
|
title: '当点击时',
|
||||||
|
initialValue:
|
||||||
|
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseEnter',
|
||||||
|
title: '当鼠标进入时',
|
||||||
|
initialValue:
|
||||||
|
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseLeave',
|
||||||
|
title: '当鼠标离开时',
|
||||||
|
initialValue:
|
||||||
|
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'field',
|
||||||
|
name: 'onClick',
|
||||||
|
extraProps: {
|
||||||
|
defaultValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setter: 'I18nSetter',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'field',
|
||||||
|
name: 'onMouseEnter',
|
||||||
|
extraProps: {
|
||||||
|
defaultValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setter: 'I18nSetter',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'field',
|
||||||
|
name: 'onMouseLeave',
|
||||||
|
extraProps: {
|
||||||
|
defaultValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setter: 'I18nSetter',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
component: {
|
||||||
|
isContainer: true,
|
||||||
|
nestingRule: {},
|
||||||
|
},
|
||||||
|
supports: {},
|
||||||
|
},
|
||||||
|
experimental: {
|
||||||
|
callbacks: {},
|
||||||
|
initials: [
|
||||||
|
{
|
||||||
|
name: 'behavior',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '__style__',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'fieldId',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'useFieldIdAsDomId',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customClassName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'events',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onClick',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseEnter',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseLeave',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
filters: [],
|
||||||
|
autoruns: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
293
packages/vision-polyfill/tests/fixtures/prototype/div-vision-full.ts
vendored
Normal file
293
packages/vision-polyfill/tests/fixtures/prototype/div-vision-full.ts
vendored
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
export default {
|
||||||
|
title: '容器',
|
||||||
|
componentName: 'Div',
|
||||||
|
docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||||
|
category: '布局',
|
||||||
|
isContainer: true,
|
||||||
|
canOperating: false,
|
||||||
|
extraActions: [],
|
||||||
|
canContain: 'Form',
|
||||||
|
canDropTo: 'Div',
|
||||||
|
canDropIn: 'Div',
|
||||||
|
canResizing: true,
|
||||||
|
canDraging: false,
|
||||||
|
context: {},
|
||||||
|
initialChildren() {},
|
||||||
|
didDropIn() {},
|
||||||
|
didDropOut() {},
|
||||||
|
subtreeModified() {},
|
||||||
|
onResize() {},
|
||||||
|
onResizeStart() {},
|
||||||
|
onResizeEnd() {},
|
||||||
|
canUseCondition: true,
|
||||||
|
canLoop: true,
|
||||||
|
snippets: [
|
||||||
|
{
|
||||||
|
screenshot: 'https://img.alicdn.com/tfs/TB1CHN3u4z1gK0jSZSgXXavwpXa-112-64.png',
|
||||||
|
label: '普通型',
|
||||||
|
schema: {
|
||||||
|
componentName: 'Div',
|
||||||
|
props: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
configure: [
|
||||||
|
{
|
||||||
|
name: 'myName',
|
||||||
|
title: '我的名字',
|
||||||
|
display: 'tab',
|
||||||
|
initialValue: 'NORMAL',
|
||||||
|
defaultValue: 'NORMAL',
|
||||||
|
collapsed: true,
|
||||||
|
supportVariable: true,
|
||||||
|
accessor(field, val) {},
|
||||||
|
mutator(field, val) {},
|
||||||
|
disabled() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
useVariableChange() {},
|
||||||
|
allowTextInput: true,
|
||||||
|
liveTextEditing: true,
|
||||||
|
setter: [
|
||||||
|
{
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
title: '普通',
|
||||||
|
value: 'NORMAL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '隐藏',
|
||||||
|
value: 'HIDDEN',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
loose: false,
|
||||||
|
cancelable: false,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
title: '普通',
|
||||||
|
value: 'NORMAL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '隐藏',
|
||||||
|
value: 'HIDDEN',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
loose: false,
|
||||||
|
cancelable: false,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mySlotName',
|
||||||
|
slotName: 'mySlotName',
|
||||||
|
slotTitle: '我的 Slot 名字',
|
||||||
|
display: 'tab',
|
||||||
|
initialValue: 'NORMAL',
|
||||||
|
defaultValue: 'NORMAL',
|
||||||
|
collapsed: true,
|
||||||
|
supportVariable: true,
|
||||||
|
accessor(field, val) {},
|
||||||
|
mutator(field, val) {},
|
||||||
|
disabled() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
title: '普通',
|
||||||
|
value: 'NORMAL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '隐藏',
|
||||||
|
value: 'HIDDEN',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
loose: false,
|
||||||
|
cancelable: false,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'behavior',
|
||||||
|
title: '默认状态',
|
||||||
|
display: 'inline',
|
||||||
|
initialValue: 'NORMAL',
|
||||||
|
supportVariable: true,
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
title: '普通',
|
||||||
|
value: 'NORMAL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '隐藏',
|
||||||
|
value: 'HIDDEN',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
loose: false,
|
||||||
|
cancelable: false,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '__style__',
|
||||||
|
title: '样式设置',
|
||||||
|
display: 'accordion',
|
||||||
|
collapsed: false,
|
||||||
|
initialValue: {},
|
||||||
|
tip: {
|
||||||
|
url: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||||
|
content: '点击 ? 查看样式设置器用法指南',
|
||||||
|
},
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
advanced: true,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'group',
|
||||||
|
title: '高级',
|
||||||
|
display: 'accordion',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'fieldId',
|
||||||
|
title: '唯一标识',
|
||||||
|
display: 'block',
|
||||||
|
tip:
|
||||||
|
'组件的唯一标识符,不能够与其它组件重名,不能够为空,且只能够使用以字母开头的,下划线以及数字的组合。',
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
placeholder: '请输入唯一标识',
|
||||||
|
multiline: false,
|
||||||
|
rows: 10,
|
||||||
|
required: false,
|
||||||
|
pattern: null,
|
||||||
|
maxLength: null,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'useFieldIdAsDomId',
|
||||||
|
title: '将唯一标识用作 DOM ID',
|
||||||
|
display: 'block',
|
||||||
|
tip:
|
||||||
|
'开启这个配置项后,会在当前组件的 HTML 元素上加入 id="当前组件的 fieldId",一般用于做 utils 的绑定,不常用',
|
||||||
|
initialValue: false,
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customClassName',
|
||||||
|
title: '自定义样式类',
|
||||||
|
display: 'block',
|
||||||
|
supportVariable: true,
|
||||||
|
initialValue: '',
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
placeholder: null,
|
||||||
|
multiline: false,
|
||||||
|
rows: 10,
|
||||||
|
required: false,
|
||||||
|
pattern: null,
|
||||||
|
maxLength: null,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'events',
|
||||||
|
title: '动作设置',
|
||||||
|
tip: {
|
||||||
|
url: 'https://lark.alipay.com/legao/legao/events-call',
|
||||||
|
content: '点击 ? 查看如何设置组件的事件响应动作',
|
||||||
|
},
|
||||||
|
display: 'accordion',
|
||||||
|
initialValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
name: 'onClick',
|
||||||
|
title: '当点击时',
|
||||||
|
initialValue:
|
||||||
|
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseEnter',
|
||||||
|
title: '当鼠标进入时',
|
||||||
|
initialValue:
|
||||||
|
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseLeave',
|
||||||
|
title: '当鼠标离开时',
|
||||||
|
initialValue:
|
||||||
|
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onClick',
|
||||||
|
display: 'none',
|
||||||
|
initialValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseEnter',
|
||||||
|
display: 'none',
|
||||||
|
initialValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseLeave',
|
||||||
|
display: 'none',
|
||||||
|
initialValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
175
packages/vision-polyfill/tests/fixtures/prototype/div-vision.ts
vendored
Normal file
175
packages/vision-polyfill/tests/fixtures/prototype/div-vision.ts
vendored
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
export default {
|
||||||
|
title: '容器',
|
||||||
|
componentName: 'Div',
|
||||||
|
docUrl: 'http://gitlab.alibaba-inc.com/vision-components/vc-block/blob/master/README.md',
|
||||||
|
category: '布局',
|
||||||
|
isContainer: true,
|
||||||
|
configure: [
|
||||||
|
{
|
||||||
|
name: 'behavior',
|
||||||
|
title: '默认状态',
|
||||||
|
display: 'inline',
|
||||||
|
initialValue: 'NORMAL',
|
||||||
|
supportVariable: true,
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
title: '普通',
|
||||||
|
value: 'NORMAL',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '隐藏',
|
||||||
|
value: 'HIDDEN',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
loose: false,
|
||||||
|
cancelable: false,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '__style__',
|
||||||
|
title: '样式设置',
|
||||||
|
display: 'accordion',
|
||||||
|
collapsed: false,
|
||||||
|
initialValue: {},
|
||||||
|
tip: {
|
||||||
|
url: 'https://lark.alipay.com/legao/help/design-tool-style',
|
||||||
|
content: '点击 ? 查看样式设置器用法指南',
|
||||||
|
},
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
advanced: true,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'group',
|
||||||
|
title: '高级',
|
||||||
|
display: 'accordion',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
name: 'fieldId',
|
||||||
|
title: '唯一标识',
|
||||||
|
display: 'block',
|
||||||
|
tip:
|
||||||
|
'组件的唯一标识符,不能够与其它组件重名,不能够为空,且只能够使用以字母开头的,下划线以及数字的组合。',
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
placeholder: '请输入唯一标识',
|
||||||
|
multiline: false,
|
||||||
|
rows: 10,
|
||||||
|
required: false,
|
||||||
|
pattern: null,
|
||||||
|
maxLength: null,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'useFieldIdAsDomId',
|
||||||
|
title: '将唯一标识用作 DOM ID',
|
||||||
|
display: 'block',
|
||||||
|
tip:
|
||||||
|
'开启这个配置项后,会在当前组件的 HTML 元素上加入 id="当前组件的 fieldId",一般用于做 utils 的绑定,不常用',
|
||||||
|
initialValue: false,
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customClassName',
|
||||||
|
title: '自定义样式类',
|
||||||
|
display: 'block',
|
||||||
|
supportVariable: true,
|
||||||
|
initialValue: '',
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
placeholder: null,
|
||||||
|
multiline: false,
|
||||||
|
rows: 10,
|
||||||
|
required: false,
|
||||||
|
pattern: null,
|
||||||
|
maxLength: null,
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'events',
|
||||||
|
title: '动作设置',
|
||||||
|
tip: {
|
||||||
|
url: 'https://lark.alipay.com/legao/legao/events-call',
|
||||||
|
content: '点击 ? 查看如何设置组件的事件响应动作',
|
||||||
|
},
|
||||||
|
display: 'accordion',
|
||||||
|
initialValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
setter: {
|
||||||
|
key: null,
|
||||||
|
ref: null,
|
||||||
|
props: {
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
name: 'onClick',
|
||||||
|
title: '当点击时',
|
||||||
|
initialValue:
|
||||||
|
"/**\n * 容器 当点击时\n */\nfunction onClick(event) {\n console.log('onClick', event);\n}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseEnter',
|
||||||
|
title: '当鼠标进入时',
|
||||||
|
initialValue:
|
||||||
|
"/**\n * 容器 当鼠标进入时\n */\nfunction onMouseEnter(event) {\n console.log('onMouseEnter', event);\n}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseLeave',
|
||||||
|
title: '当鼠标离开时',
|
||||||
|
initialValue:
|
||||||
|
"/**\n * 容器 当鼠标离开时\n */\nfunction onMouseLeave(event) {\n console.log('onMouseLeave', event);\n}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
_owner: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onClick',
|
||||||
|
display: 'none',
|
||||||
|
initialValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseEnter',
|
||||||
|
display: 'none',
|
||||||
|
initialValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onMouseLeave',
|
||||||
|
display: 'none',
|
||||||
|
initialValue: {
|
||||||
|
ignored: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
955
packages/vision-polyfill/tests/fixtures/schema/form.ts
vendored
Normal file
955
packages/vision-polyfill/tests/fixtures/schema/form.ts
vendored
Normal file
@ -0,0 +1,955 @@
|
|||||||
|
export default {
|
||||||
|
componentName: 'Page',
|
||||||
|
id: 'node_k1ow3cb9',
|
||||||
|
props: {
|
||||||
|
extensions: {
|
||||||
|
启用页头: true,
|
||||||
|
},
|
||||||
|
pageStyle: {
|
||||||
|
backgroundColor: '#f2f3f5',
|
||||||
|
},
|
||||||
|
containerStyle: {},
|
||||||
|
className: 'page_kh05zf9c',
|
||||||
|
templateVersion: '1.0.0',
|
||||||
|
},
|
||||||
|
lifeCycles: {
|
||||||
|
constructor: {
|
||||||
|
type: 'js',
|
||||||
|
compiled:
|
||||||
|
"function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}",
|
||||||
|
source:
|
||||||
|
"function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
css:
|
||||||
|
'body{background-color:#f2f3f5}.card_kh05zf9d {\n margin-bottom: 12px;\n}.card_kh05zf9e {\n margin-bottom: 12px;\n}.button_kh05zf9f {\n margin-right: 16px;\n width: 80px\n}.button_kh05zf9g {\n width: 80px;\n}.div_kh05zf9h {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}',
|
||||||
|
methods: {
|
||||||
|
__initMethods__: {
|
||||||
|
type: 'js',
|
||||||
|
source: 'function (exports, module) { /*set actions code here*/ }',
|
||||||
|
compiled: 'function (exports, module) { /*set actions code here*/ }',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dataSource: {
|
||||||
|
offline: [],
|
||||||
|
globalConfig: {
|
||||||
|
fit: {
|
||||||
|
compiled: '',
|
||||||
|
source: '',
|
||||||
|
type: 'js',
|
||||||
|
error: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
online: [],
|
||||||
|
sync: true,
|
||||||
|
list: [],
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'RootHeader',
|
||||||
|
id: 'node_k1ow3cba',
|
||||||
|
props: {},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'PageHeader',
|
||||||
|
id: 'node_k1ow3cbd',
|
||||||
|
props: {
|
||||||
|
extraContent: '',
|
||||||
|
__slot__extraContent: false,
|
||||||
|
__slot__action: false,
|
||||||
|
title: '',
|
||||||
|
content: '',
|
||||||
|
__slot__logo: false,
|
||||||
|
__slot__crumb: false,
|
||||||
|
crumb: '',
|
||||||
|
tab: '',
|
||||||
|
logo: '',
|
||||||
|
action: '',
|
||||||
|
__slot__tab: false,
|
||||||
|
__style__: {},
|
||||||
|
__slot__content: false,
|
||||||
|
fieldId: 'pageHeader_k1ow3h1i',
|
||||||
|
subTitle: '',
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
componentName: 'RootContent',
|
||||||
|
id: 'node_k1ow3cbb',
|
||||||
|
props: {
|
||||||
|
contentBgColor: 'transparent',
|
||||||
|
contentPadding: '0',
|
||||||
|
contentMargin: '20',
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'Form',
|
||||||
|
id: 'form',
|
||||||
|
props: {
|
||||||
|
size: 'medium',
|
||||||
|
labelAlign: 'top',
|
||||||
|
autoValidate: true,
|
||||||
|
scrollToFirstError: true,
|
||||||
|
autoUnmount: true,
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
dataSource: {
|
||||||
|
type: 'variable',
|
||||||
|
variable: 'state.formData',
|
||||||
|
},
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'form',
|
||||||
|
fieldOptions: {},
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'Card',
|
||||||
|
id: 'node_k1ow3cbj',
|
||||||
|
props: {
|
||||||
|
__slot__title: false,
|
||||||
|
subTitle: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__slot__subTitle: false,
|
||||||
|
extra: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
className: 'card_kh05zf9d',
|
||||||
|
title: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'Title',
|
||||||
|
zh_CN: '基本信息',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__slot__extra: false,
|
||||||
|
showHeadDivider: true,
|
||||||
|
__style__: ':root {\n margin-bottom: 12px;\n}',
|
||||||
|
showTitleBullet: true,
|
||||||
|
contentHeight: '',
|
||||||
|
fieldId: 'card_k1ow3h1l',
|
||||||
|
dividerNoInset: false,
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'CardContent',
|
||||||
|
id: 'node_k1ow3cbk',
|
||||||
|
props: {},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'ColumnsLayout',
|
||||||
|
id: 'node_k1ow3cbw',
|
||||||
|
props: {
|
||||||
|
layout: '4:8',
|
||||||
|
columnGap: '20',
|
||||||
|
rowGap: 0,
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'columns_k1ow3h1v',
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'Column',
|
||||||
|
id: 'node_k1ow3cbx',
|
||||||
|
props: {
|
||||||
|
colSpan: '',
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'column_k1p1bnjm',
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'TextField',
|
||||||
|
id: 'node_k1ow3cbz',
|
||||||
|
props: {
|
||||||
|
fieldName: 'name',
|
||||||
|
hasClear: false,
|
||||||
|
autoFocus: false,
|
||||||
|
tips: {
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
trim: false,
|
||||||
|
labelTextAlign: 'right',
|
||||||
|
placeholder: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'please input',
|
||||||
|
zh_CN: '请输入',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
state: '',
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
value: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
addonBefore: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
validation: [
|
||||||
|
{
|
||||||
|
type: 'required',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
hasLimitHint: false,
|
||||||
|
cutString: false,
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'textField_k1ow3h1w',
|
||||||
|
htmlType: 'input',
|
||||||
|
autoHeight: false,
|
||||||
|
labelColOffset: 0,
|
||||||
|
label: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'TextField',
|
||||||
|
zh_CN: '姓名',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__category__: 'form',
|
||||||
|
labelColSpan: 4,
|
||||||
|
wrapperColSpan: 0,
|
||||||
|
rows: 4,
|
||||||
|
addonAfter: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
wrapperColOffset: 0,
|
||||||
|
size: 'medium',
|
||||||
|
labelAlign: 'top',
|
||||||
|
__useMediator: 'value',
|
||||||
|
labelTipsTypes: 'none',
|
||||||
|
labelTipsIcon: '',
|
||||||
|
labelTipsText: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
},
|
||||||
|
maxLength: 200,
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
componentName: 'TextField',
|
||||||
|
id: 'node_k1ow3cc1',
|
||||||
|
props: {
|
||||||
|
fieldName: 'englishName',
|
||||||
|
hasClear: false,
|
||||||
|
autoFocus: false,
|
||||||
|
tips: {
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
trim: false,
|
||||||
|
labelTextAlign: 'right',
|
||||||
|
placeholder: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'please input',
|
||||||
|
zh_CN: '请输入',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
state: '',
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
value: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
addonBefore: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
validation: [],
|
||||||
|
hasLimitHint: false,
|
||||||
|
cutString: false,
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'textField_k1ow3h1y',
|
||||||
|
htmlType: 'input',
|
||||||
|
autoHeight: false,
|
||||||
|
labelColOffset: 0,
|
||||||
|
label: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'TextField',
|
||||||
|
zh_CN: '英文名',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__category__: 'form',
|
||||||
|
labelColSpan: 4,
|
||||||
|
wrapperColSpan: 0,
|
||||||
|
rows: 4,
|
||||||
|
addonAfter: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
wrapperColOffset: 0,
|
||||||
|
size: 'medium',
|
||||||
|
labelAlign: 'top',
|
||||||
|
__useMediator: 'value',
|
||||||
|
labelTipsTypes: 'none',
|
||||||
|
labelTipsIcon: '',
|
||||||
|
labelTipsText: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
},
|
||||||
|
maxLength: 200,
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
componentName: 'TextField',
|
||||||
|
id: 'node_k1ow3cc3',
|
||||||
|
props: {
|
||||||
|
fieldName: 'jobTitle',
|
||||||
|
hasClear: false,
|
||||||
|
autoFocus: false,
|
||||||
|
tips: {
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
trim: false,
|
||||||
|
labelTextAlign: 'right',
|
||||||
|
placeholder: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'please input',
|
||||||
|
zh_CN: '请输入',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
state: '',
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
value: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
addonBefore: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
validation: [],
|
||||||
|
hasLimitHint: false,
|
||||||
|
cutString: false,
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'textField_k1ow3h20',
|
||||||
|
htmlType: 'input',
|
||||||
|
autoHeight: false,
|
||||||
|
labelColOffset: 0,
|
||||||
|
label: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'TextField',
|
||||||
|
zh_CN: '职位',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__category__: 'form',
|
||||||
|
labelColSpan: 4,
|
||||||
|
wrapperColSpan: 0,
|
||||||
|
rows: 4,
|
||||||
|
addonAfter: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
wrapperColOffset: 0,
|
||||||
|
size: 'medium',
|
||||||
|
labelAlign: 'top',
|
||||||
|
__useMediator: 'value',
|
||||||
|
labelTipsTypes: 'none',
|
||||||
|
labelTipsIcon: '',
|
||||||
|
labelTipsText: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
},
|
||||||
|
maxLength: 200,
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
componentName: 'Column',
|
||||||
|
id: 'node_k1ow3cby',
|
||||||
|
props: {
|
||||||
|
colSpan: '',
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'column_k1p1bnjn',
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'TextField',
|
||||||
|
id: 'node_k1ow3cc2',
|
||||||
|
props: {
|
||||||
|
fieldName: 'nickName',
|
||||||
|
hasClear: false,
|
||||||
|
autoFocus: false,
|
||||||
|
tips: {
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
trim: false,
|
||||||
|
labelTextAlign: 'right',
|
||||||
|
placeholder: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'please input',
|
||||||
|
zh_CN: '请输入',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
state: '',
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
value: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
addonBefore: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
validation: [],
|
||||||
|
hasLimitHint: false,
|
||||||
|
cutString: false,
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'textField_k1ow3h1z',
|
||||||
|
htmlType: 'input',
|
||||||
|
autoHeight: false,
|
||||||
|
labelColOffset: 0,
|
||||||
|
label: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'TextField',
|
||||||
|
zh_CN: '花名',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__category__: 'form',
|
||||||
|
labelColSpan: 4,
|
||||||
|
wrapperColSpan: 0,
|
||||||
|
rows: 4,
|
||||||
|
addonAfter: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
wrapperColOffset: 0,
|
||||||
|
size: 'medium',
|
||||||
|
labelAlign: 'top',
|
||||||
|
__useMediator: 'value',
|
||||||
|
labelTipsTypes: 'none',
|
||||||
|
labelTipsIcon: '',
|
||||||
|
labelTipsText: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
},
|
||||||
|
maxLength: 200,
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
componentName: 'SelectField',
|
||||||
|
id: 'node_k1ow3cc0',
|
||||||
|
props: {
|
||||||
|
fieldName: 'gender',
|
||||||
|
hasClear: false,
|
||||||
|
tips: {
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
mode: 'single',
|
||||||
|
showSearch: false,
|
||||||
|
autoWidth: true,
|
||||||
|
labelTextAlign: 'right',
|
||||||
|
placeholder: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'please select',
|
||||||
|
zh_CN: '请选择',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
hasBorder: true,
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
value: '',
|
||||||
|
validation: [
|
||||||
|
{
|
||||||
|
type: 'required',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'select_k1ow3h1x',
|
||||||
|
notFoundContent: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
labelColOffset: 0,
|
||||||
|
label: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'SelectField',
|
||||||
|
zh_CN: '性别',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__category__: 'form',
|
||||||
|
labelColSpan: 4,
|
||||||
|
wrapperColSpan: 0,
|
||||||
|
wrapperColOffset: 0,
|
||||||
|
hasSelectAll: false,
|
||||||
|
hasArrow: true,
|
||||||
|
size: 'medium',
|
||||||
|
labelAlign: 'top',
|
||||||
|
filterLocal: true,
|
||||||
|
dataSource: [
|
||||||
|
{
|
||||||
|
defaultChecked: false,
|
||||||
|
text: {
|
||||||
|
en_US: 'Option 1',
|
||||||
|
zh_CN: '男',
|
||||||
|
type: 'i18n',
|
||||||
|
__sid__: 'param_k1owc4tb',
|
||||||
|
},
|
||||||
|
__sid__: 'serial_k1owc4t1',
|
||||||
|
value: 'M',
|
||||||
|
sid: 'opt_k1owc4t2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
defaultChecked: false,
|
||||||
|
text: {
|
||||||
|
en_US: 'Option 2',
|
||||||
|
zh_CN: '女',
|
||||||
|
type: 'i18n',
|
||||||
|
__sid__: 'param_k1owc4tf',
|
||||||
|
},
|
||||||
|
__sid__: 'serial_k1owc4t2',
|
||||||
|
value: 'F',
|
||||||
|
sid: 'opt_k1owc4t3',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
__useMediator: 'value',
|
||||||
|
labelTipsTypes: 'none',
|
||||||
|
labelTipsIcon: '',
|
||||||
|
labelTipsText: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
},
|
||||||
|
useDetailValue: false,
|
||||||
|
searchDelay: 300,
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
componentName: 'Card',
|
||||||
|
id: 'node_k1ow3cbl',
|
||||||
|
props: {
|
||||||
|
__slot__title: false,
|
||||||
|
subTitle: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__slot__subTitle: false,
|
||||||
|
extra: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
className: 'card_kh05zf9e',
|
||||||
|
title: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'Title',
|
||||||
|
zh_CN: '部门信息',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__slot__extra: false,
|
||||||
|
showHeadDivider: true,
|
||||||
|
__style__: ':root {\n margin-bottom: 12px;\n}',
|
||||||
|
showTitleBullet: true,
|
||||||
|
contentHeight: '',
|
||||||
|
fieldId: 'card_k1ow3h1m',
|
||||||
|
dividerNoInset: false,
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'CardContent',
|
||||||
|
id: 'node_k1ow3cbm',
|
||||||
|
props: {},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'TextField',
|
||||||
|
id: 'node_k1ow3cc4',
|
||||||
|
props: {
|
||||||
|
fieldName: 'department',
|
||||||
|
hasClear: false,
|
||||||
|
autoFocus: false,
|
||||||
|
tips: {
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
trim: false,
|
||||||
|
labelTextAlign: 'right',
|
||||||
|
placeholder: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'please input',
|
||||||
|
zh_CN: '请输入',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
state: '',
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
value: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
addonBefore: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
validation: [],
|
||||||
|
hasLimitHint: false,
|
||||||
|
cutString: false,
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'textField_k1ow3h21',
|
||||||
|
htmlType: 'input',
|
||||||
|
autoHeight: false,
|
||||||
|
labelColOffset: 0,
|
||||||
|
label: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'TextField',
|
||||||
|
zh_CN: '所属部门',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__category__: 'form',
|
||||||
|
labelColSpan: 4,
|
||||||
|
wrapperColSpan: 0,
|
||||||
|
rows: 4,
|
||||||
|
addonAfter: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
wrapperColOffset: 0,
|
||||||
|
size: 'medium',
|
||||||
|
labelAlign: 'top',
|
||||||
|
__useMediator: 'value',
|
||||||
|
labelTipsTypes: 'none',
|
||||||
|
labelTipsIcon: '',
|
||||||
|
labelTipsText: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
},
|
||||||
|
maxLength: 200,
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
componentName: 'ColumnsLayout',
|
||||||
|
id: 'node_k1ow3cc5',
|
||||||
|
props: {
|
||||||
|
layout: '6:6',
|
||||||
|
columnGap: '20',
|
||||||
|
rowGap: 0,
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'columns_k1ow3h22',
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'Column',
|
||||||
|
id: 'node_k1ow3cc6',
|
||||||
|
props: {
|
||||||
|
colSpan: '',
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'column_k1p1bnjo',
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'TextField',
|
||||||
|
id: 'node_k1ow3cc8',
|
||||||
|
props: {
|
||||||
|
fieldName: 'leader',
|
||||||
|
hasClear: false,
|
||||||
|
autoFocus: false,
|
||||||
|
tips: {
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
trim: false,
|
||||||
|
labelTextAlign: 'right',
|
||||||
|
placeholder: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'please input',
|
||||||
|
zh_CN: '请输入',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
state: '',
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
value: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
addonBefore: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
validation: [],
|
||||||
|
hasLimitHint: false,
|
||||||
|
cutString: false,
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'textField_k1ow3h23',
|
||||||
|
htmlType: 'input',
|
||||||
|
autoHeight: false,
|
||||||
|
labelColOffset: 0,
|
||||||
|
label: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'TextField',
|
||||||
|
zh_CN: '主管',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__category__: 'form',
|
||||||
|
labelColSpan: 4,
|
||||||
|
wrapperColSpan: 0,
|
||||||
|
rows: 4,
|
||||||
|
addonAfter: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
wrapperColOffset: 0,
|
||||||
|
size: 'medium',
|
||||||
|
labelAlign: 'top',
|
||||||
|
__useMediator: 'value',
|
||||||
|
labelTipsTypes: 'none',
|
||||||
|
labelTipsIcon: '',
|
||||||
|
labelTipsText: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
},
|
||||||
|
maxLength: 200,
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
componentName: 'Column',
|
||||||
|
id: 'node_k1ow3cc7',
|
||||||
|
props: {
|
||||||
|
colSpan: '',
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'column_k1p1bnjp',
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'TextField',
|
||||||
|
id: 'node_k1ow3cc9',
|
||||||
|
props: {
|
||||||
|
fieldName: 'hrg',
|
||||||
|
hasClear: false,
|
||||||
|
autoFocus: false,
|
||||||
|
tips: {
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
trim: false,
|
||||||
|
labelTextAlign: 'right',
|
||||||
|
placeholder: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'please input',
|
||||||
|
zh_CN: '请输入',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
state: '',
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
value: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
addonBefore: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
validation: [],
|
||||||
|
hasLimitHint: false,
|
||||||
|
cutString: false,
|
||||||
|
__style__: {},
|
||||||
|
fieldId: 'textField_k1ow3h24',
|
||||||
|
htmlType: 'input',
|
||||||
|
autoHeight: false,
|
||||||
|
labelColOffset: 0,
|
||||||
|
label: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'TextField',
|
||||||
|
zh_CN: 'HRG',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__category__: 'form',
|
||||||
|
labelColSpan: 4,
|
||||||
|
wrapperColSpan: 0,
|
||||||
|
rows: 4,
|
||||||
|
addonAfter: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
wrapperColOffset: 0,
|
||||||
|
size: 'medium',
|
||||||
|
labelAlign: 'top',
|
||||||
|
__useMediator: 'value',
|
||||||
|
labelTipsTypes: 'none',
|
||||||
|
labelTipsIcon: '',
|
||||||
|
labelTipsText: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: '',
|
||||||
|
zh_CN: '',
|
||||||
|
},
|
||||||
|
maxLength: 200,
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
componentName: 'Div',
|
||||||
|
id: 'node_k1ow3cbo',
|
||||||
|
props: {
|
||||||
|
className: 'div_kh05zf9h',
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
__style__:
|
||||||
|
':root {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}',
|
||||||
|
events: {},
|
||||||
|
fieldId: 'div_k1ow3h1o',
|
||||||
|
useFieldIdAsDomId: false,
|
||||||
|
customClassName: '',
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
componentName: 'Button',
|
||||||
|
id: 'node_k1ow3cbn',
|
||||||
|
props: {
|
||||||
|
triggerEventsWhenLoading: false,
|
||||||
|
onClick: {
|
||||||
|
rawType: 'events',
|
||||||
|
type: 'JSExpression',
|
||||||
|
value:
|
||||||
|
'this.utils.legaoBuiltin.execEventFlow.bind(this, [this.submit])',
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
name: 'submit',
|
||||||
|
id: 'submit',
|
||||||
|
params: {},
|
||||||
|
type: 'actionRef',
|
||||||
|
uuid: '1570966253282_0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
size: 'medium',
|
||||||
|
baseIcon: '',
|
||||||
|
otherIcon: '',
|
||||||
|
className: 'button_kh05zf9f',
|
||||||
|
type: 'primary',
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
loading: false,
|
||||||
|
content: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'Button',
|
||||||
|
zh_CN: '提交',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__style__: ':root {\n margin-right: 16px;\n width: 80px\n}',
|
||||||
|
fieldId: 'button_k1ow3h1n',
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
componentName: 'Button',
|
||||||
|
id: 'node_k1ow3cbp',
|
||||||
|
props: {
|
||||||
|
triggerEventsWhenLoading: false,
|
||||||
|
size: 'medium',
|
||||||
|
baseIcon: '',
|
||||||
|
otherIcon: '',
|
||||||
|
className: 'button_kh05zf9g',
|
||||||
|
type: 'normal',
|
||||||
|
behavior: 'NORMAL',
|
||||||
|
loading: false,
|
||||||
|
content: {
|
||||||
|
use: 'zh_CN',
|
||||||
|
en_US: 'Button',
|
||||||
|
zh_CN: '取消',
|
||||||
|
type: 'i18n',
|
||||||
|
},
|
||||||
|
__style__: ':root {\n width: 80px;\n}',
|
||||||
|
fieldId: 'button_k1ow3h1p',
|
||||||
|
},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
componentName: 'RootFooter',
|
||||||
|
id: 'node_k1ow3cbc',
|
||||||
|
props: {},
|
||||||
|
condition: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
8
packages/vision-polyfill/tests/fixtures/window.ts
vendored
Normal file
8
packages/vision-polyfill/tests/fixtures/window.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
React.PropTypes = PropTypes;
|
||||||
|
window.React = React;
|
||||||
|
|
||||||
|
document.documentElement.requestFullscreen = () => {};
|
||||||
|
document.exitFullscreen = () => {};
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`deepValueParser 测试 designMode: design 1`] = `
|
||||||
|
Object {
|
||||||
|
"a": "111",
|
||||||
|
"arr": Array [
|
||||||
|
"111",
|
||||||
|
"111",
|
||||||
|
],
|
||||||
|
"b": "222",
|
||||||
|
"c": "中文",
|
||||||
|
"slot": Object {
|
||||||
|
"type": "JSSlot",
|
||||||
|
"value": Array [
|
||||||
|
Object {
|
||||||
|
"componentName": "Div",
|
||||||
|
"props": Object {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`deepValueParser 测试 designMode: live 1`] = `
|
||||||
|
Object {
|
||||||
|
"a": Object {
|
||||||
|
"mock": "111",
|
||||||
|
"type": "JSExpression",
|
||||||
|
"value": "state.a",
|
||||||
|
},
|
||||||
|
"arr": Array [
|
||||||
|
Object {
|
||||||
|
"mock": "111",
|
||||||
|
"type": "JSExpression",
|
||||||
|
"value": "state.a",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"mock": "111",
|
||||||
|
"type": "JSExpression",
|
||||||
|
"value": "state.b",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"b": Object {
|
||||||
|
"mock": "222",
|
||||||
|
"type": "JSExpression",
|
||||||
|
"value": "state.b",
|
||||||
|
},
|
||||||
|
"c": "中文",
|
||||||
|
}
|
||||||
|
`;
|
||||||
240
packages/vision-polyfill/tests/master/bus.test.ts
Normal file
240
packages/vision-polyfill/tests/master/bus.test.ts
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import bus from '../../src/bus';
|
||||||
|
import { editor } from '../../src/reducers';
|
||||||
|
|
||||||
|
describe('bus 测试', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
bus.unsub('evt1');
|
||||||
|
});
|
||||||
|
it('sub / pub 测试', () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
const off1 = bus.sub('evt1', mockFn1);
|
||||||
|
|
||||||
|
const evtData = { a: 1 };
|
||||||
|
bus.pub('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
|
||||||
|
off1();
|
||||||
|
|
||||||
|
bus.pub('evt1', evtData);
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('on / emit 测试', () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
const off1 = bus.on('evt1', mockFn1);
|
||||||
|
|
||||||
|
const evtData = { a: 1 };
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
|
||||||
|
off1();
|
||||||
|
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('once / emit 测试', () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
bus.once('evt1', mockFn1);
|
||||||
|
|
||||||
|
const evtData = { a: 1 };
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('once / emit 测试,调用解绑函数', () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
const off1 = bus.once('evt1', mockFn1);
|
||||||
|
|
||||||
|
off1();
|
||||||
|
|
||||||
|
const evtData = { a: 1 };
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('removeListener 测试', () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
bus.on('evt1', mockFn1);
|
||||||
|
|
||||||
|
const evtData = { a: 1 };
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
|
||||||
|
bus.removeListener('evt1', mockFn1);
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('unsub 测试 - 只有一个 handler', () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
bus.on('evt1', mockFn1);
|
||||||
|
|
||||||
|
const evtData = { a: 1 };
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
|
||||||
|
bus.unsub('evt1', mockFn1);
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('unsub 测试 - 只 unsub 一个 handler', () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
const mockFn2 = jest.fn();
|
||||||
|
bus.on('evt1', mockFn1);
|
||||||
|
bus.on('evt1', mockFn2);
|
||||||
|
|
||||||
|
const evtData = { a: 1 };
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
expect(mockFn2).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn2).toHaveBeenCalledWith(evtData);
|
||||||
|
|
||||||
|
bus.unsub('evt1', mockFn1);
|
||||||
|
const evtData2 = { a: 2 };
|
||||||
|
bus.emit('evt1', evtData2);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
expect(mockFn2).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockFn2).toHaveBeenLastCalledWith(evtData2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('unsub 测试 - 多个 handler', () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
const mockFn2 = jest.fn();
|
||||||
|
bus.on('evt1', mockFn1);
|
||||||
|
bus.on('evt1', mockFn2);
|
||||||
|
|
||||||
|
const evtData = { a: 1 };
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
expect(mockFn2).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn2).toHaveBeenCalledWith(evtData);
|
||||||
|
|
||||||
|
bus.unsub('evt1');
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
expect(mockFn2).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn2).toHaveBeenCalledWith(evtData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('off 测试 - 只有一个 handler', () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
bus.on('evt1', mockFn1);
|
||||||
|
|
||||||
|
const evtData = { a: 1 };
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
|
||||||
|
bus.off('evt1', mockFn1);
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('off 测试 - 只 off 一个 handler', () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
const mockFn2 = jest.fn();
|
||||||
|
bus.on('evt1', mockFn1);
|
||||||
|
bus.on('evt1', mockFn2);
|
||||||
|
|
||||||
|
const evtData = { a: 1 };
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
expect(mockFn2).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn2).toHaveBeenCalledWith(evtData);
|
||||||
|
|
||||||
|
bus.off('evt1', mockFn1);
|
||||||
|
const evtData2 = { a: 2 };
|
||||||
|
bus.emit('evt1', evtData2);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
expect(mockFn2).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockFn2).toHaveBeenLastCalledWith(evtData2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('off 测试 - 多个 handler', () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
const mockFn2 = jest.fn();
|
||||||
|
bus.on('evt1', mockFn1);
|
||||||
|
bus.on('evt1', mockFn2);
|
||||||
|
|
||||||
|
const evtData = { a: 1 };
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
expect(mockFn2).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn2).toHaveBeenCalledWith(evtData);
|
||||||
|
|
||||||
|
bus.off('evt1');
|
||||||
|
bus.emit('evt1', evtData);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData);
|
||||||
|
expect(mockFn2).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn2).toHaveBeenCalledWith(evtData);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('简单测试(dummy)', () => {
|
||||||
|
bus.getEmitter();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('editor 事件转发', () => {
|
||||||
|
const fwdEvtMap = {
|
||||||
|
've.hotkey.callback.call': 'hotkey.callback.call',
|
||||||
|
've.history.back': 'history.back',
|
||||||
|
've.history.forward': 'history.forward',
|
||||||
|
'node.prop.change': 'node.prop.change',
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(fwdEvtMap).forEach(veEventName => {
|
||||||
|
it(`${veEventName} 测试`, () => {
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
const evtData1 = { a: 1 };
|
||||||
|
bus.on(veEventName, mockFn1);
|
||||||
|
|
||||||
|
editor.emit(fwdEvtMap[veEventName], evtData1);
|
||||||
|
|
||||||
|
expect(mockFn1).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(evtData1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
62
packages/vision-polyfill/tests/master/context.test.ts
Normal file
62
packages/vision-polyfill/tests/master/context.test.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
// import { Project } from '../../src/project/project';
|
||||||
|
// import { Node } from '../../src/document/node/node';
|
||||||
|
// import { Designer } from '../../src/designer/designer';
|
||||||
|
import { VisualEngineContext } from '../../src/context';
|
||||||
|
import { autorun } from '@ali/lowcode-editor-core';
|
||||||
|
|
||||||
|
describe('VisualEngineContext 测试', () => {
|
||||||
|
it('registerManager | getManager', () => {
|
||||||
|
const ctx = new VisualEngineContext();
|
||||||
|
|
||||||
|
ctx.registerManager({
|
||||||
|
mgr1: {},
|
||||||
|
});
|
||||||
|
ctx.registerManager('mgr2', {});
|
||||||
|
expect(ctx.getManager('mgr1')).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('registerModule | getModule', () => {
|
||||||
|
const ctx = new VisualEngineContext();
|
||||||
|
|
||||||
|
ctx.registerModule({
|
||||||
|
mod1: {},
|
||||||
|
});
|
||||||
|
ctx.registerModule('mod2', {});
|
||||||
|
expect(ctx.getModule('mod1')).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('use | getPlugin', () => {
|
||||||
|
const ctx = new VisualEngineContext();
|
||||||
|
|
||||||
|
ctx.use('plugin1', { plugin: 1 });
|
||||||
|
ctx.registerManager({
|
||||||
|
mgr1: { manager: 1 },
|
||||||
|
});
|
||||||
|
ctx.registerModule({
|
||||||
|
mod1: { mod: 1 },
|
||||||
|
});
|
||||||
|
expect(ctx.getPlugin('plugin1')).toEqual({ plugin: 1 });
|
||||||
|
expect(ctx.getPlugin('mgr1')).toEqual({ manager: 1 });
|
||||||
|
expect(ctx.getPlugin('mod1')).toEqual({ mod: 1 });
|
||||||
|
expect(ctx.getPlugin()).toBeUndefined;
|
||||||
|
|
||||||
|
ctx.use('ve.settingField.variableSetter', {});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('registerTreePane | getModule', () => {
|
||||||
|
const ctx = new VisualEngineContext();
|
||||||
|
|
||||||
|
ctx.registerTreePane({ pane: 1 }, { core: 2 });
|
||||||
|
expect(ctx.getModule('TreePane')).toEqual({ pane: 1 });
|
||||||
|
expect(ctx.getModule('TreeCore')).toEqual({ core: 2 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('registerDynamicSetterProvider', () => {
|
||||||
|
const ctx = new VisualEngineContext();
|
||||||
|
|
||||||
|
ctx.registerDynamicSetterProvider({});
|
||||||
|
expect(ctx.getPlugin('ve.plugin.setterProvider')).toEqual({});
|
||||||
|
ctx.registerDynamicSetterProvider();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import { deepValueParser } from '../../src/deep-value-parser';
|
||||||
|
import { editor } from '../../src/reducers';
|
||||||
|
|
||||||
|
describe('deepValueParser 测试', () => {
|
||||||
|
it('null & undefined', () => {
|
||||||
|
expect(deepValueParser()).toBeNull;
|
||||||
|
expect(deepValueParser()).toBeUndefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('designMode: design', () => {
|
||||||
|
expect(deepValueParser({
|
||||||
|
a: {
|
||||||
|
type: 'variable',
|
||||||
|
variable: 'state.a',
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: 'state.b',
|
||||||
|
mock: '222',
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '中文',
|
||||||
|
en_US: 'eng',
|
||||||
|
},
|
||||||
|
slot: {
|
||||||
|
type: 'JSSlot',
|
||||||
|
value: [{
|
||||||
|
componentName: 'Div',
|
||||||
|
props: {},
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
arr: [
|
||||||
|
{
|
||||||
|
type: 'variable',
|
||||||
|
variable: 'state.a',
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'variable',
|
||||||
|
variable: 'state.b',
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('designMode: live', () => {
|
||||||
|
editor.set('designMode', 'live');
|
||||||
|
expect(deepValueParser({
|
||||||
|
a: {
|
||||||
|
type: 'variable',
|
||||||
|
variable: 'state.a',
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: 'state.b',
|
||||||
|
mock: '222',
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '中文',
|
||||||
|
en_US: 'eng',
|
||||||
|
},
|
||||||
|
arr: [
|
||||||
|
{
|
||||||
|
type: 'variable',
|
||||||
|
variable: 'state.a',
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'variable',
|
||||||
|
variable: 'state.b',
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
182
packages/vision-polyfill/tests/master/drag-engine.test.ts
Normal file
182
packages/vision-polyfill/tests/master/drag-engine.test.ts
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
// import { Project } from '../../src/project/project';
|
||||||
|
// import { Node } from '../../src/document/node/node';
|
||||||
|
// import { Editor } from '@ali/lowcode-editor-core';
|
||||||
|
// import { Designer } from '@ali/lowcode-designer';
|
||||||
|
import { designer } from '../../src/reducers';
|
||||||
|
import DragEngine from '../../src/drag-engine';
|
||||||
|
import formSchema from '../fixtures/schema/form';
|
||||||
|
|
||||||
|
// const editor = new Editor();
|
||||||
|
// const designer = new Designer({ editor });
|
||||||
|
designer.project.open(formSchema);
|
||||||
|
|
||||||
|
const mockBoostPrototype = jest.fn((e: MouseEvent) => {
|
||||||
|
return {
|
||||||
|
isPrototype: true,
|
||||||
|
getComponentName() {
|
||||||
|
return 'Div';
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockBoostNode = jest.fn((e: MouseEvent) => {
|
||||||
|
return designer.currentDocument?.getNode('node_k1ow3cbo');
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockBoostNodeData = jest.fn((e: MouseEvent) => {
|
||||||
|
return {
|
||||||
|
type: 'NodeData',
|
||||||
|
componentName: 'Div',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockBoostNull = jest.fn((e: MouseEvent) => {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockDragstart = jest.fn();
|
||||||
|
const mockDrag = jest.fn();
|
||||||
|
const mockDragend = jest.fn();
|
||||||
|
|
||||||
|
describe('drag-engine 测试', () => {
|
||||||
|
it('prototype', async () => {
|
||||||
|
DragEngine.from(document, mockBoostPrototype);
|
||||||
|
|
||||||
|
DragEngine.onDragstart(mockDragstart);
|
||||||
|
DragEngine.onDrag(mockDrag);
|
||||||
|
DragEngine.onDragend(mockDragend);
|
||||||
|
|
||||||
|
const mousedownEvt = new MouseEvent('mousedown');
|
||||||
|
document.dispatchEvent(mousedownEvt);
|
||||||
|
designer.dragon.emitter.emit('dragstart', {
|
||||||
|
dragObject: {
|
||||||
|
nodes: [designer.currentDocument?.getNode('node_k1ow3cbo')],
|
||||||
|
},
|
||||||
|
originalEvent: mousedownEvt,
|
||||||
|
});
|
||||||
|
|
||||||
|
// await new Promise(resolve => resolve(setTimeout, 500));
|
||||||
|
|
||||||
|
expect(mockDragstart).toHaveBeenCalled();
|
||||||
|
|
||||||
|
designer.dragon.emitter.emit('drag', {
|
||||||
|
dragObject: {
|
||||||
|
nodes: [designer.currentDocument?.getNode('node_k1ow3cbo')]
|
||||||
|
},
|
||||||
|
originalEvent: mousedownEvt,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockDrag).toHaveBeenCalled();
|
||||||
|
expect(DragEngine.inDragging()).toBeTruthy;
|
||||||
|
|
||||||
|
designer.dragon.emitter.emit('dragend', {
|
||||||
|
dragObject: {
|
||||||
|
nodes: [designer.currentDocument?.getNode('node_k1ow3cbo')]
|
||||||
|
},
|
||||||
|
originalEvent: mousedownEvt,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockDragend).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Node', async () => {
|
||||||
|
DragEngine.from(document, mockBoostNode);
|
||||||
|
|
||||||
|
DragEngine.onDragstart(mockDragstart);
|
||||||
|
DragEngine.onDrag(mockDrag);
|
||||||
|
DragEngine.onDragend(mockDragend);
|
||||||
|
|
||||||
|
const mousedownEvt = new MouseEvent('mousedown');
|
||||||
|
document.dispatchEvent(mousedownEvt);
|
||||||
|
designer.dragon.emitter.emit('dragstart', {
|
||||||
|
dragObject: {
|
||||||
|
nodes: [designer.currentDocument?.getNode('node_k1ow3cbo')],
|
||||||
|
},
|
||||||
|
originalEvent: mousedownEvt,
|
||||||
|
});
|
||||||
|
|
||||||
|
// await new Promise(resolve => resolve(setTimeout, 500));
|
||||||
|
|
||||||
|
expect(mockDragstart).toHaveBeenCalled();
|
||||||
|
|
||||||
|
designer.dragon.emitter.emit('drag', {
|
||||||
|
dragObject: {
|
||||||
|
nodes: [designer.currentDocument?.getNode('node_k1ow3cbo')]
|
||||||
|
},
|
||||||
|
originalEvent: mousedownEvt,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockDrag).toHaveBeenCalled();
|
||||||
|
|
||||||
|
designer.dragon.emitter.emit('dragend', {
|
||||||
|
dragObject: {
|
||||||
|
nodes: [designer.currentDocument?.getNode('node_k1ow3cbo')]
|
||||||
|
},
|
||||||
|
originalEvent: mousedownEvt,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockDragend).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('NodeData', async () => {
|
||||||
|
DragEngine.from(document, mockBoostNodeData);
|
||||||
|
|
||||||
|
DragEngine.onDragstart(mockDragstart);
|
||||||
|
DragEngine.onDrag(mockDrag);
|
||||||
|
DragEngine.onDragend(mockDragend);
|
||||||
|
|
||||||
|
const mousedownEvt = new MouseEvent('mousedown');
|
||||||
|
document.dispatchEvent(mousedownEvt);
|
||||||
|
designer.dragon.emitter.emit('dragstart', {
|
||||||
|
dragObject: {
|
||||||
|
nodes: [designer.currentDocument?.getNode('node_k1ow3cbo')],
|
||||||
|
},
|
||||||
|
originalEvent: mousedownEvt,
|
||||||
|
});
|
||||||
|
|
||||||
|
// await new Promise(resolve => resolve(setTimeout, 500));
|
||||||
|
|
||||||
|
expect(mockDragstart).toHaveBeenCalled();
|
||||||
|
|
||||||
|
designer.dragon.emitter.emit('drag', {
|
||||||
|
dragObject: {
|
||||||
|
nodes: [designer.currentDocument?.getNode('node_k1ow3cbo')]
|
||||||
|
},
|
||||||
|
originalEvent: mousedownEvt,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockDrag).toHaveBeenCalled();
|
||||||
|
|
||||||
|
designer.dragon.emitter.emit('dragend', {
|
||||||
|
dragObject: {
|
||||||
|
type: 'nodedata',
|
||||||
|
data: {
|
||||||
|
componentName: 'Div',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originalEvent: mousedownEvt,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockDragend).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('null', async () => {
|
||||||
|
DragEngine.from(document, mockBoostNull);
|
||||||
|
|
||||||
|
DragEngine.onDragstart(mockDragstart);
|
||||||
|
DragEngine.onDrag(mockDrag);
|
||||||
|
DragEngine.onDragend(mockDragend);
|
||||||
|
|
||||||
|
const mousedownEvt = new MouseEvent('mousedown');
|
||||||
|
document.dispatchEvent(mousedownEvt);
|
||||||
|
designer.dragon.emitter.emit('dragstart', {
|
||||||
|
dragObject: {
|
||||||
|
nodes: [designer.currentDocument?.getNode('node_k1ow3cbo')],
|
||||||
|
},
|
||||||
|
originalEvent: mousedownEvt,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockDragstart).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
111
packages/vision-polyfill/tests/master/env.test.ts
Normal file
111
packages/vision-polyfill/tests/master/env.test.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
// import { Project } from '../../src/project/project';
|
||||||
|
// import { Node } from '../../src/document/node/node';
|
||||||
|
// import { Designer } from '../../src/designer/designer';
|
||||||
|
import env from '../../src/env';
|
||||||
|
import { autorun } from '@ali/lowcode-editor-core';
|
||||||
|
|
||||||
|
describe('env 测试', () => {
|
||||||
|
describe('常规 API 测试', () => {
|
||||||
|
it('setEnv / getEnv / setEnvMap / set / get', () => {
|
||||||
|
expect(env.getEnv('xxx')).toBeUndefined;
|
||||||
|
|
||||||
|
const mockFn1 = jest.fn();
|
||||||
|
const off1 = env.onEnvChange(mockFn1);
|
||||||
|
|
||||||
|
const envData = { a: 1 };
|
||||||
|
env.setEnv('xxx', envData);
|
||||||
|
expect(env.getEnv('xxx')).toEqual(envData);
|
||||||
|
expect(env.get('xxx')).toEqual(envData);
|
||||||
|
expect(mockFn1).toHaveBeenCalled();
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(env.envs, 'xxx', envData);
|
||||||
|
mockFn1.mockClear();
|
||||||
|
|
||||||
|
// 设置相同的值
|
||||||
|
env.setEnv('xxx', envData);
|
||||||
|
expect(env.getEnv('xxx')).toEqual(envData);
|
||||||
|
expect(env.get('xxx')).toEqual(envData);
|
||||||
|
expect(mockFn1).not.toHaveBeenCalled();
|
||||||
|
mockFn1.mockClear();
|
||||||
|
|
||||||
|
// 设置另一个 envName
|
||||||
|
const envData2 = { b: 1 };
|
||||||
|
env.set('yyy', envData2);
|
||||||
|
expect(env.getEnv('yyy')).toEqual(envData2);
|
||||||
|
expect(env.get('yyy')).toEqual(envData2);
|
||||||
|
expect(mockFn1).toHaveBeenCalled();
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(env.envs, 'yyy', envData2);
|
||||||
|
mockFn1.mockClear();
|
||||||
|
|
||||||
|
env.setEnvMap({
|
||||||
|
zzz: { a: 1, b: 1 },
|
||||||
|
});
|
||||||
|
expect(env.getEnv('xxx')).toBeUndefined;
|
||||||
|
expect(env.getEnv('yyy')).toBeUndefined;
|
||||||
|
expect(env.getEnv('zzz')).toEqual({ a: 1, b: 1 });
|
||||||
|
expect(mockFn1).toHaveBeenCalled();
|
||||||
|
expect(mockFn1).toHaveBeenCalledWith(env.envs);
|
||||||
|
mockFn1.mockClear();
|
||||||
|
|
||||||
|
// 解绑事件
|
||||||
|
off1();
|
||||||
|
env.setEnvMap({
|
||||||
|
zzz: { a: 1, b: 1 },
|
||||||
|
});
|
||||||
|
expect(mockFn1).not.toHaveBeenCalled();
|
||||||
|
mockFn1.mockClear();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setLocale / getLocale', () => {
|
||||||
|
expect(env.getLocale()).toBe('zh_CN');
|
||||||
|
env.setLocale('en_US');
|
||||||
|
expect(env.getLocale()).toBe('en_US');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setExpertMode / isExpertMode', () => {
|
||||||
|
expect(env.isExpertMode()).toBeFalsy;
|
||||||
|
env.setExpertMode('truthy value');
|
||||||
|
expect(env.isExpertMode()).toBeTruthy;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSupportFeatures / setSupportFeatures / supports', () => {
|
||||||
|
expect(env.getSupportFeatures()).toEqual({});
|
||||||
|
env.setSupportFeatures({
|
||||||
|
mobile: true,
|
||||||
|
pc: true,
|
||||||
|
});
|
||||||
|
expect(env.getSupportFeatures()).toEqual({
|
||||||
|
mobile: true,
|
||||||
|
pc: true,
|
||||||
|
});
|
||||||
|
expect(env.supports('mobile')).toBeTruthy;
|
||||||
|
expect(env.supports('pc')).toBeTruthy;
|
||||||
|
expect(env.supports('iot')).toBeFalsy;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getAliSchemaVersion', () => {
|
||||||
|
expect(env.getAliSchemaVersion()).toBe('1.0.0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('envs obx 测试', async () => {
|
||||||
|
const mockFn = jest.fn();
|
||||||
|
env.clear();
|
||||||
|
|
||||||
|
autorun(() => {
|
||||||
|
mockFn(env.envs);
|
||||||
|
env.envs;
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 16));
|
||||||
|
|
||||||
|
expect(mockFn).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFn).toHaveBeenLastCalledWith({});
|
||||||
|
|
||||||
|
env.setEnv('abc', { a: 1 });
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 16));
|
||||||
|
expect(mockFn).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockFn).toHaveBeenLastCalledWith({ abc: { a: 1 } });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
71
packages/vision-polyfill/tests/master/flags.test.ts
Normal file
71
packages/vision-polyfill/tests/master/flags.test.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import flagsCtrl from '../../src/flags';
|
||||||
|
import domready from 'domready';
|
||||||
|
|
||||||
|
jest.mock('domready', () => {
|
||||||
|
return (fn) => fn();
|
||||||
|
});
|
||||||
|
// domready.mockImplementation((fn) => fn());
|
||||||
|
|
||||||
|
describe('flags 测试', () => {
|
||||||
|
it('flags', () => {
|
||||||
|
const mockFlagsChange = jest.fn();
|
||||||
|
flagsCtrl.flags = [];
|
||||||
|
const off = flagsCtrl.onFlagsChange(mockFlagsChange);
|
||||||
|
flagsCtrl.add('a');
|
||||||
|
expect(mockFlagsChange).toHaveBeenCalledTimes(1);
|
||||||
|
off();
|
||||||
|
flagsCtrl.add('b');
|
||||||
|
expect(mockFlagsChange).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['a', 'b']);
|
||||||
|
|
||||||
|
flagsCtrl.flags = [];
|
||||||
|
flagsCtrl.setDragMode(true);
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['drag-mode']);
|
||||||
|
flagsCtrl.setDragMode(false);
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual([]);
|
||||||
|
|
||||||
|
flagsCtrl.setPreviewMode(true);
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['preview-mode']);
|
||||||
|
flagsCtrl.setPreviewMode(false);
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['design-mode']);
|
||||||
|
|
||||||
|
flagsCtrl.flags = [];
|
||||||
|
flagsCtrl.setHideSlate(true);
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['hide-slate']);
|
||||||
|
flagsCtrl.setHideSlate(false);
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual([]);
|
||||||
|
|
||||||
|
flagsCtrl.flags = [];
|
||||||
|
flagsCtrl.setSlateFixedMode(true);
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['slate-fixed']);
|
||||||
|
flagsCtrl.setHideSlate(true);
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['slate-fixed']);
|
||||||
|
flagsCtrl.setSlateFixedMode(false);
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual([]);
|
||||||
|
|
||||||
|
flagsCtrl.flags = [];
|
||||||
|
flagsCtrl.setSlateFullMode(true);
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['slate-full-screen']);
|
||||||
|
flagsCtrl.setSlateFullMode(false);
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual([]);
|
||||||
|
|
||||||
|
expect([].slice.apply(document.documentElement.classList)).toEqual(flagsCtrl.getFlags());
|
||||||
|
|
||||||
|
flagsCtrl.flags = [];
|
||||||
|
// setWithShell
|
||||||
|
flagsCtrl.setWithShell('shellA');
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['with-iphone6shell']);
|
||||||
|
flagsCtrl.setWithShell('iPhone6');
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['with-iphone6shell']);
|
||||||
|
|
||||||
|
flagsCtrl.flags = [];
|
||||||
|
// setSimulator
|
||||||
|
flagsCtrl.setSimulator('simA');
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['simulator-simA']);
|
||||||
|
flagsCtrl.setSimulator('simB');
|
||||||
|
expect(flagsCtrl.getFlags()).toEqual(['simulator-simB']);
|
||||||
|
});
|
||||||
|
});
|
||||||
176
packages/vision-polyfill/tests/master/panes.test.ts
Normal file
176
packages/vision-polyfill/tests/master/panes.test.ts
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
// import { Project } from '../../src/project/project';
|
||||||
|
// import { Node } from '../../src/document/node/node';
|
||||||
|
// import { Designer } from '../../src/designer/designer';
|
||||||
|
import panes from '../../src/panes';
|
||||||
|
import { autorun } from '@ali/lowcode-editor-core';
|
||||||
|
|
||||||
|
describe('panes 测试', () => {
|
||||||
|
it('add: type dock | PanelDock', () => {
|
||||||
|
const mockDockShow = jest.fn();
|
||||||
|
const mockDockHide = jest.fn();
|
||||||
|
const { DockPane } = panes;
|
||||||
|
const offDockShow = DockPane.onDockShow(mockDockShow);
|
||||||
|
const offDockHide = DockPane.onDockHide(mockDockHide);
|
||||||
|
|
||||||
|
const pane1 = panes.add({
|
||||||
|
name: 'trunk',
|
||||||
|
type: 'dock',
|
||||||
|
width: 300,
|
||||||
|
description: '组件库',
|
||||||
|
contents: [
|
||||||
|
{
|
||||||
|
title: '普通组件',
|
||||||
|
tip: '普通组件',
|
||||||
|
content: () => 'haha',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
menu: '组件库',
|
||||||
|
defaultFixed: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const pane2 = panes.add({
|
||||||
|
name: 'trunk2',
|
||||||
|
type: 'dock',
|
||||||
|
width: 300,
|
||||||
|
description: '组件库',
|
||||||
|
contents: [
|
||||||
|
{
|
||||||
|
title: '普通组件',
|
||||||
|
tip: '普通组件',
|
||||||
|
content: () => 'haha',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
menu: '组件库',
|
||||||
|
defaultFixed: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const pane3 = panes.add({
|
||||||
|
name: 'trunk3',
|
||||||
|
type: 'dock',
|
||||||
|
isAction: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// DockPane.container.items.map(item => console.log(item.name))
|
||||||
|
// 2 trunks + 1 outline-pane
|
||||||
|
expect(DockPane.container.items.length).toBe(4);
|
||||||
|
|
||||||
|
DockPane.activeDock(pane1);
|
||||||
|
// expect(mockDockShow).toHaveBeenCalledTimes(1);
|
||||||
|
// expect(mockDockShow).toHaveBeenLastCalledWith(pane1);
|
||||||
|
expect(DockPane.container.items[1].visible).toBeTruthy;
|
||||||
|
|
||||||
|
DockPane.activeDock(pane2);
|
||||||
|
expect(DockPane.container.items[2].visible).toBeTruthy;
|
||||||
|
// expect(mockDockShow).toHaveBeenCalledTimes(2);
|
||||||
|
// expect(mockDockShow).toHaveBeenLastCalledWith(pane2);
|
||||||
|
// expect(mockDockHide).toHaveBeenCalledTimes(1);
|
||||||
|
// expect(mockDockHide).toHaveBeenLastCalledWith(pane1);
|
||||||
|
|
||||||
|
DockPane.activeDock();
|
||||||
|
DockPane.activeDock({ name: 'unexisting' });
|
||||||
|
|
||||||
|
offDockShow();
|
||||||
|
offDockHide();
|
||||||
|
|
||||||
|
// DockPane.activeDock(pane1);
|
||||||
|
// expect(mockDockShow).toHaveBeenCalledTimes(2);
|
||||||
|
// expect(mockDockHide).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
expect(typeof DockPane.getDocks).toBe('function');
|
||||||
|
DockPane.getDocks();
|
||||||
|
expect(typeof DockPane.setFixed).toBe('function');
|
||||||
|
DockPane.setFixed();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('add: type action', () => {
|
||||||
|
panes.add({
|
||||||
|
name: 'trunk',
|
||||||
|
type: 'action',
|
||||||
|
init() {},
|
||||||
|
destroy() {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { ActionPane } = panes;
|
||||||
|
expect(typeof ActionPane.getActions).toBe('function');
|
||||||
|
ActionPane.getActions();
|
||||||
|
expect(typeof ActionPane.setActions).toBe('function');
|
||||||
|
ActionPane.setActions();
|
||||||
|
expect(ActionPane.getActions()).toBe(ActionPane.actions);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('add: type action - extraConfig', () => {
|
||||||
|
panes.add({
|
||||||
|
name: 'trunk',
|
||||||
|
type: 'action',
|
||||||
|
init() {},
|
||||||
|
destroy() {},
|
||||||
|
}, {});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('add: type action - function', () => {
|
||||||
|
panes.add(() => ({
|
||||||
|
name: 'trunk',
|
||||||
|
type: 'action',
|
||||||
|
init() {},
|
||||||
|
destroy() {},
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('add: type tab', () => {
|
||||||
|
panes.add({
|
||||||
|
name: 'trunk',
|
||||||
|
type: 'tab',
|
||||||
|
});
|
||||||
|
const { TabPane } = panes;
|
||||||
|
expect(typeof TabPane.setFloat).toBe('function');
|
||||||
|
TabPane.setFloat();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('add: type stage', () => {
|
||||||
|
panes.add({
|
||||||
|
id: 'stage1',
|
||||||
|
type: 'stage',
|
||||||
|
});
|
||||||
|
panes.add({
|
||||||
|
type: 'stage',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { Stages } = panes;
|
||||||
|
expect(typeof Stages.getStage).toBe('function');
|
||||||
|
Stages.getStage();
|
||||||
|
expect(typeof Stages.createStage).toBe('function');
|
||||||
|
Stages.createStage({
|
||||||
|
id: 'stage1',
|
||||||
|
type: 'stage',
|
||||||
|
});
|
||||||
|
Stages.createStage({
|
||||||
|
type: 'stage',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('add: type stage - id', () => {
|
||||||
|
panes.add({
|
||||||
|
id: 'trunk',
|
||||||
|
name: 'trunk',
|
||||||
|
type: 'stage',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('add: type widget', () => {
|
||||||
|
panes.add({
|
||||||
|
name: 'trunk',
|
||||||
|
type: 'widget',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('add: type null', () => {
|
||||||
|
panes.add({
|
||||||
|
name: 'trunk',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { toolbar } = panes;
|
||||||
|
expect(typeof toolbar.setContents).toBe('function');
|
||||||
|
toolbar.setContents();
|
||||||
|
});
|
||||||
|
});
|
||||||
15
packages/vision-polyfill/tests/master/symbols.test.ts
Normal file
15
packages/vision-polyfill/tests/master/symbols.test.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import set from 'lodash/set';
|
||||||
|
import cloneDeep from 'lodash/clonedeep';
|
||||||
|
import '../fixtures/window';
|
||||||
|
// import { Project } from '../../src/project/project';
|
||||||
|
// import { Node } from '../../src/document/node/node';
|
||||||
|
// import { Designer } from '../../src/designer/designer';
|
||||||
|
import symbols from '../../src/symbols';
|
||||||
|
|
||||||
|
describe('symbols 测试', () => {
|
||||||
|
it('API', () => {
|
||||||
|
symbols.create('abc');
|
||||||
|
symbols.create('abc');
|
||||||
|
symbols.get('abc');
|
||||||
|
});
|
||||||
|
});
|
||||||
189
packages/vision-polyfill/tests/master/viewport.test.ts
Normal file
189
packages/vision-polyfill/tests/master/viewport.test.ts
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import { Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||||
|
import { editor } from '../../src/reducers';
|
||||||
|
import { Viewport } from '../../src/viewport';
|
||||||
|
import domready from 'domready';
|
||||||
|
|
||||||
|
// const editor = globalContext.get(Editor);
|
||||||
|
|
||||||
|
jest.mock('domready', () => {
|
||||||
|
return (fn) => fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 貌似 jsdom 没有响应 fullscreen 变更事件,先这么 mock 吧
|
||||||
|
const mockSetFullscreen = flag => { document.fullscreen = flag; };
|
||||||
|
|
||||||
|
describe('viewport 测试', () => {
|
||||||
|
mockSetFullscreen(true);
|
||||||
|
|
||||||
|
it('getDevice / setDevice / getViewport / onDeviceChange / onViewportChange', async () => {
|
||||||
|
const viewport = new Viewport();
|
||||||
|
const mockDeviceChange = jest.fn();
|
||||||
|
const mockViewportChange = jest.fn();
|
||||||
|
const offDevice = viewport.onDeviceChange(mockDeviceChange);
|
||||||
|
const offViewport = viewport.onViewportChange(mockViewportChange);
|
||||||
|
expect(viewport.getDevice()).toBe('pc');
|
||||||
|
expect(viewport.getViewport()).toBe('design-pc');
|
||||||
|
editor.set('currentDocument', { simulator: { set() {} } });
|
||||||
|
|
||||||
|
await viewport.setDevice('mobile');
|
||||||
|
expect(viewport.getDevice()).toBe('mobile');
|
||||||
|
expect(viewport.getViewport()).toBe('design-mobile');
|
||||||
|
expect(mockDeviceChange).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockViewportChange).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
offDevice();
|
||||||
|
offViewport();
|
||||||
|
await viewport.setDevice('pc');
|
||||||
|
expect(mockDeviceChange).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockViewportChange).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setPreview / isPreview / togglePreivew / getViewport / onViewportChange', () => {
|
||||||
|
const viewport = new Viewport();
|
||||||
|
const mockViewportChange = jest.fn();
|
||||||
|
const mockPreivewChange = jest.fn();
|
||||||
|
const off = viewport.onViewportChange(mockViewportChange);
|
||||||
|
const offPreview = viewport.onPreview(mockPreivewChange);
|
||||||
|
viewport.setPreview(true);
|
||||||
|
expect(viewport.isPreview).toBeTruthy;
|
||||||
|
expect(viewport.getViewport()).toBe('preview-pc');
|
||||||
|
expect(mockViewportChange).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockPreivewChange).toHaveBeenCalledTimes(1);
|
||||||
|
viewport.setPreview(false);
|
||||||
|
expect(viewport.isPreview).toBeFalsy;
|
||||||
|
expect(viewport.getViewport()).toBe('design-pc');
|
||||||
|
expect(mockViewportChange).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockPreivewChange).toHaveBeenCalledTimes(2);
|
||||||
|
viewport.togglePreview();
|
||||||
|
expect(viewport.getViewport()).toBe('preview-pc');
|
||||||
|
expect(mockViewportChange).toHaveBeenCalledTimes(3);
|
||||||
|
expect(mockPreivewChange).toHaveBeenCalledTimes(3);
|
||||||
|
viewport.togglePreview();
|
||||||
|
expect(viewport.getViewport()).toBe('design-pc');
|
||||||
|
expect(mockViewportChange).toHaveBeenCalledTimes(4);
|
||||||
|
expect(mockPreivewChange).toHaveBeenCalledTimes(4);
|
||||||
|
|
||||||
|
off();
|
||||||
|
offPreview();
|
||||||
|
viewport.togglePreview();
|
||||||
|
expect(mockViewportChange).toHaveBeenCalledTimes(4);
|
||||||
|
expect(mockPreivewChange).toHaveBeenCalledTimes(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setFocusTarget / returnFocus / setFocus / isFocus / onFocusChange', () => {
|
||||||
|
const viewport = new Viewport();
|
||||||
|
const mockFocusChange = jest.fn();
|
||||||
|
const off = viewport.onFocusChange(mockFocusChange);
|
||||||
|
viewport.setFocusTarget(document.createElement('div'));
|
||||||
|
viewport.returnFocus();
|
||||||
|
|
||||||
|
viewport.setFocus(true);
|
||||||
|
expect(viewport.isFocus()).toBeTruthy();
|
||||||
|
expect(mockFocusChange).toHaveBeenCalledTimes(1);
|
||||||
|
expect(mockFocusChange).toHaveBeenLastCalledWith(true);
|
||||||
|
viewport.setFocus(false);
|
||||||
|
expect(viewport.isFocus()).toBeFalsy();
|
||||||
|
expect(mockFocusChange).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockFocusChange).toHaveBeenLastCalledWith(false);
|
||||||
|
|
||||||
|
off();
|
||||||
|
viewport.setFocus(false);
|
||||||
|
expect(mockFocusChange).toHaveBeenCalledTimes(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isFullscreen / toggleFullscreen / setFullscreen / onFullscreenChange', () => {
|
||||||
|
const viewport = new Viewport();
|
||||||
|
const mockFullscreenChange = jest.fn();
|
||||||
|
const off = viewport.onFullscreenChange(mockFullscreenChange);
|
||||||
|
|
||||||
|
mockSetFullscreen(false);
|
||||||
|
viewport.setFullscreen(true);
|
||||||
|
mockSetFullscreen(true);
|
||||||
|
expect(viewport.isFullscreen()).toBeTruthy;
|
||||||
|
// expect(mockFullscreenChange).toHaveBeenCalledTimes(1);
|
||||||
|
viewport.setFullscreen(true);
|
||||||
|
// expect(mockFullscreenChange).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
mockSetFullscreen(true);
|
||||||
|
viewport.setFullscreen(false);
|
||||||
|
mockSetFullscreen(false);
|
||||||
|
expect(viewport.isFullscreen()).toBeFalsy;
|
||||||
|
// expect(mockFullscreenChange).toHaveBeenCalledTimes(2);
|
||||||
|
viewport.setFullscreen(false);
|
||||||
|
// expect(mockFullscreenChange).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
mockSetFullscreen(true);
|
||||||
|
viewport.toggleFullscreen();
|
||||||
|
mockSetFullscreen(false);
|
||||||
|
// expect(mockFullscreenChange).toHaveBeenCalledTimes(3);
|
||||||
|
viewport.toggleFullscreen();
|
||||||
|
// expect(mockFullscreenChange).toHaveBeenCalledTimes(4);
|
||||||
|
|
||||||
|
off();
|
||||||
|
viewport.toggleFullscreen();
|
||||||
|
// expect(mockFullscreenChange).toHaveBeenCalledTimes(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setWithShell', () => {
|
||||||
|
const viewport = new Viewport();
|
||||||
|
viewport.setWithShell();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('onSlateFixedChange', () => {
|
||||||
|
const viewport = new Viewport();
|
||||||
|
const mockSlateFixedChange = jest.fn();
|
||||||
|
const off = viewport.onSlateFixedChange(mockSlateFixedChange);
|
||||||
|
|
||||||
|
viewport.emitter.emit('slatefixed');
|
||||||
|
expect(mockSlateFixedChange).toHaveBeenCalledTimes(1);
|
||||||
|
off();
|
||||||
|
viewport.emitter.emit('slatefixed');
|
||||||
|
expect(mockSlateFixedChange).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setGlobalCSS', () => {
|
||||||
|
const viewport = new Viewport();
|
||||||
|
viewport.setGlobalCSS([{
|
||||||
|
media: '*',
|
||||||
|
type: 'URL',
|
||||||
|
content: '//path/to.css',
|
||||||
|
}, {
|
||||||
|
media: 'ALL',
|
||||||
|
type: 'text',
|
||||||
|
content: 'body {font-size: 50px;}',
|
||||||
|
}, {
|
||||||
|
media: '',
|
||||||
|
type: 'text',
|
||||||
|
content: 'body {font-size: 50px;}',
|
||||||
|
}, {
|
||||||
|
media: 'mobile',
|
||||||
|
type: 'text',
|
||||||
|
content: 'body {font-size: 50px;}',
|
||||||
|
}]);
|
||||||
|
|
||||||
|
viewport.cssResourceSet[0].apply();
|
||||||
|
viewport.cssResourceSet[0].init();
|
||||||
|
viewport.cssResourceSet[1].apply();
|
||||||
|
viewport.cssResourceSet[1].apply();
|
||||||
|
viewport.cssResourceSet[1].unmount();
|
||||||
|
|
||||||
|
viewport.setGlobalCSS([{
|
||||||
|
media: '*',
|
||||||
|
type: 'URL',
|
||||||
|
content: '//path/to.css',
|
||||||
|
}, {
|
||||||
|
media: 'ALL',
|
||||||
|
type: 'text',
|
||||||
|
content: 'body {font-size: 50px;}',
|
||||||
|
}, {
|
||||||
|
media: '',
|
||||||
|
type: 'text',
|
||||||
|
content: 'body {font-size: 50px;}',
|
||||||
|
}, {
|
||||||
|
media: 'mobile',
|
||||||
|
type: 'text',
|
||||||
|
content: 'body {font-size: 50px;}',
|
||||||
|
}]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import { Node, Designer, getConvertedExtraKey } from '@ali/lowcode-designer';
|
||||||
|
import { Editor } from '@ali/lowcode-editor-core';
|
||||||
|
import {
|
||||||
|
compatibleReducer,
|
||||||
|
} from '../../src/props-reducers/downgrade-schema-reducer';
|
||||||
|
import formSchema from '../fixtures/schema/form';
|
||||||
|
|
||||||
|
describe('compatibleReducer 测试', () => {
|
||||||
|
it('compatibleReducer 测试', () => {
|
||||||
|
const downgradedProps = {
|
||||||
|
a: {
|
||||||
|
type: 'JSBlock',
|
||||||
|
value: {
|
||||||
|
componentName: 'Slot',
|
||||||
|
props: {
|
||||||
|
slotTitle: '标题',
|
||||||
|
slotName: 'title',
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
c1: {
|
||||||
|
type: 'JSBlock',
|
||||||
|
value: {
|
||||||
|
componentName: 'Slot',
|
||||||
|
props: {
|
||||||
|
slotTitle: '标题',
|
||||||
|
slotName: 'title',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
d: {
|
||||||
|
type: 'variable',
|
||||||
|
variable: 'state.a',
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
e: {
|
||||||
|
e1: {
|
||||||
|
type: 'variable',
|
||||||
|
variable: 'state.b',
|
||||||
|
value: '222',
|
||||||
|
},
|
||||||
|
e2: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: 'state.b',
|
||||||
|
mock: '222',
|
||||||
|
events: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(compatibleReducer({
|
||||||
|
a: {
|
||||||
|
type: 'JSSlot',
|
||||||
|
title: '标题',
|
||||||
|
name: 'title',
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
c1: {
|
||||||
|
type: 'JSSlot',
|
||||||
|
title: '标题',
|
||||||
|
name: 'title',
|
||||||
|
value: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
d: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: 'state.a',
|
||||||
|
mock: '111',
|
||||||
|
},
|
||||||
|
e: {
|
||||||
|
e1: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: 'state.b',
|
||||||
|
mock: '222',
|
||||||
|
},
|
||||||
|
e2: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: 'state.b',
|
||||||
|
mock: '222',
|
||||||
|
events: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})).toEqual(downgradedProps);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('空值', () => {
|
||||||
|
expect(compatibleReducer(null)).toBeNull;
|
||||||
|
expect(compatibleReducer(undefined)).toBeUndefined;
|
||||||
|
expect(compatibleReducer(111)).toBe(111);
|
||||||
|
});
|
||||||
|
});
|
||||||
81
packages/vision-polyfill/tests/props-reducers/filter.test.ts
Normal file
81
packages/vision-polyfill/tests/props-reducers/filter.test.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import { Node, Designer, getConvertedExtraKey } from '@ali/lowcode-designer';
|
||||||
|
import { Editor } from '@ali/lowcode-editor-core';
|
||||||
|
import { filterReducer } from '../../src/props-reducers/filter-reducer';
|
||||||
|
import formSchema from '../fixtures/schema/form';
|
||||||
|
|
||||||
|
describe('filterReducer 测试', () => {
|
||||||
|
it('filterReducer 测试 - 有 filters', () => {
|
||||||
|
const mockNode = {
|
||||||
|
componentMeta: {
|
||||||
|
getMetadata() {
|
||||||
|
return {
|
||||||
|
experimental: {
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: 'shouldBeFitlered',
|
||||||
|
filter: () => false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'keeped',
|
||||||
|
filter: () => true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'throwErr',
|
||||||
|
filter: () => { throw new Error('xxx'); },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'zzz',
|
||||||
|
filter: () => true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settingEntry: {
|
||||||
|
getProp(propName) {
|
||||||
|
return { name: propName };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(filterReducer({
|
||||||
|
shouldBeFitlered: 111,
|
||||||
|
keeped: 222,
|
||||||
|
noCorresponingFilter: 222,
|
||||||
|
throwErr: 111,
|
||||||
|
}, mockNode)).toEqual({
|
||||||
|
keeped: 222,
|
||||||
|
noCorresponingFilter: 222,
|
||||||
|
throwErr: 111,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filterReducer 测试 - 无 filters', () => {
|
||||||
|
const mockNode = {
|
||||||
|
componentMeta: {
|
||||||
|
getMetadata() {
|
||||||
|
return {
|
||||||
|
experimental: {
|
||||||
|
filters: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settingEntry: {
|
||||||
|
getProp(propName) {
|
||||||
|
return { name: propName };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(filterReducer({
|
||||||
|
shouldBeFitlered: 111,
|
||||||
|
keeped: 222,
|
||||||
|
noCorresponingFilter: 222,
|
||||||
|
}, mockNode)).toEqual({
|
||||||
|
shouldBeFitlered: 111,
|
||||||
|
keeped: 222,
|
||||||
|
noCorresponingFilter: 222,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
488
packages/vision-polyfill/tests/props-reducers/init-node.test.ts
Normal file
488
packages/vision-polyfill/tests/props-reducers/init-node.test.ts
Normal file
@ -0,0 +1,488 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import { Node, Designer, getConvertedExtraKey } from '@ali/lowcode-designer';
|
||||||
|
import { Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||||
|
import { initNodeReducer } from '../../src/props-reducers/init-node-reducer';
|
||||||
|
import formSchema from '../fixtures/schema/form';
|
||||||
|
|
||||||
|
describe('initNodeReducer 测试', () => {
|
||||||
|
it('initNodeReducer 测试 - 有 initials', () => {
|
||||||
|
const mockNode = {
|
||||||
|
componentMeta: {
|
||||||
|
getMetadata() {
|
||||||
|
return {
|
||||||
|
experimental: {
|
||||||
|
initials: [
|
||||||
|
{
|
||||||
|
name: 'propA',
|
||||||
|
initial: () => '111',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'propB',
|
||||||
|
initial: () => '111',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'propC',
|
||||||
|
initial: () => {
|
||||||
|
throw new Error('111');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'propD',
|
||||||
|
initial: () => '111',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'propE',
|
||||||
|
initial: () => '111',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'propF',
|
||||||
|
initial: () => '111',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
prototype: {
|
||||||
|
options: {
|
||||||
|
configure: [
|
||||||
|
{
|
||||||
|
name: 'propF',
|
||||||
|
setter: {
|
||||||
|
type: {
|
||||||
|
displayName: 'I18nSetter',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settingEntry: {
|
||||||
|
getProp(propName) {
|
||||||
|
return { name: propName };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
has() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
add() {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
initNodeReducer(
|
||||||
|
{
|
||||||
|
propA: '111',
|
||||||
|
propC: '222',
|
||||||
|
propD: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
mock: '111',
|
||||||
|
},
|
||||||
|
propE: {
|
||||||
|
type: 'variable',
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mockNode,
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propA: '111',
|
||||||
|
propB: '111',
|
||||||
|
propC: '222',
|
||||||
|
propD: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
mock: '111',
|
||||||
|
},
|
||||||
|
propE: {
|
||||||
|
type: 'variable',
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
propF: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '111',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filterReducer 测试 - 无 initials', () => {
|
||||||
|
const mockNode = {
|
||||||
|
componentMeta: {
|
||||||
|
getMetadata() {
|
||||||
|
return {
|
||||||
|
experimental: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settingEntry: {
|
||||||
|
getProp(propName) {
|
||||||
|
return { name: propName };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
initNodeReducer(
|
||||||
|
{
|
||||||
|
propA: 111,
|
||||||
|
},
|
||||||
|
mockNode,
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propA: 111,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('i18n', () => {
|
||||||
|
const mockNode = {
|
||||||
|
componentMeta: {
|
||||||
|
getMetadata() {
|
||||||
|
return {
|
||||||
|
experimental: {
|
||||||
|
initials: [
|
||||||
|
{
|
||||||
|
name: 'propF',
|
||||||
|
initial: () => 111,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prototype: {
|
||||||
|
options: {
|
||||||
|
configure: [
|
||||||
|
{
|
||||||
|
name: 'propF',
|
||||||
|
setter: {
|
||||||
|
type: {
|
||||||
|
displayName: 'I18nSetter',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
has() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
add() {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
it('isI18NObject(ov): true', () => {
|
||||||
|
expect(
|
||||||
|
initNodeReducer(
|
||||||
|
{
|
||||||
|
propF: {
|
||||||
|
type: 'i18n',
|
||||||
|
zh_CN: '222',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mockNode,
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propF: {
|
||||||
|
type: 'i18n',
|
||||||
|
zh_CN: '222',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isJSExpression(ov): true', () => {
|
||||||
|
expect(
|
||||||
|
initNodeReducer(
|
||||||
|
{
|
||||||
|
propF: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mockNode,
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propF: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isJSBlock(ov): true', () => {
|
||||||
|
expect(
|
||||||
|
initNodeReducer(
|
||||||
|
{
|
||||||
|
propF: {
|
||||||
|
type: 'JSBlock',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mockNode,
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propF: {
|
||||||
|
type: 'JSBlock',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isJSSlot(ov): true', () => {
|
||||||
|
expect(
|
||||||
|
initNodeReducer(
|
||||||
|
{
|
||||||
|
propF: {
|
||||||
|
type: 'JSSlot',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mockNode,
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propF: {
|
||||||
|
type: 'JSSlot',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isVariable(ov): true', () => {
|
||||||
|
expect(
|
||||||
|
initNodeReducer(
|
||||||
|
{
|
||||||
|
propF: {
|
||||||
|
type: 'variable',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mockNode,
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propF: {
|
||||||
|
type: 'variable',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isI18NObject(v): false', () => {
|
||||||
|
const mockNode = {
|
||||||
|
componentMeta: {
|
||||||
|
getMetadata() {
|
||||||
|
return {
|
||||||
|
experimental: {
|
||||||
|
initials: [
|
||||||
|
{
|
||||||
|
name: 'propF',
|
||||||
|
initial: () => 111,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
prototype: {
|
||||||
|
options: {
|
||||||
|
configure: [
|
||||||
|
{
|
||||||
|
name: 'propF',
|
||||||
|
setter: {
|
||||||
|
type: {
|
||||||
|
displayName: 'I18nSetter',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
has() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
add() {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
initNodeReducer(
|
||||||
|
{
|
||||||
|
propF: {
|
||||||
|
type: 'variable',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mockNode,
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propF: {
|
||||||
|
type: 'variable',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('isI18NObject(v): false', () => {
|
||||||
|
const mockNode = {
|
||||||
|
componentMeta: {
|
||||||
|
getMetadata() {
|
||||||
|
return {
|
||||||
|
experimental: {
|
||||||
|
initials: [{
|
||||||
|
name: 'propF',
|
||||||
|
initial: () => 111,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
prototype: {
|
||||||
|
options: {
|
||||||
|
configure: [
|
||||||
|
{
|
||||||
|
name: 'propF',
|
||||||
|
setter: {
|
||||||
|
type: {
|
||||||
|
displayName: 'I18nSetter',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
has() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
add() {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
initNodeReducer(
|
||||||
|
{
|
||||||
|
propF: {
|
||||||
|
type: 'variable',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mockNode,
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propF: {
|
||||||
|
type: 'variable',
|
||||||
|
value: 'state.a',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('成功使用兼容后的 i18n 对象', () => {
|
||||||
|
const mockNode = {
|
||||||
|
componentMeta: {
|
||||||
|
getMetadata() {
|
||||||
|
return {
|
||||||
|
experimental: {
|
||||||
|
initials: [{
|
||||||
|
name: 'propF',
|
||||||
|
initial: () => {
|
||||||
|
return {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '111',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
prototype: {
|
||||||
|
options: {
|
||||||
|
configure: [
|
||||||
|
{
|
||||||
|
name: 'propF',
|
||||||
|
setter: {
|
||||||
|
type: {
|
||||||
|
displayName: 'I18nSetter',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
has() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
add() {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(
|
||||||
|
initNodeReducer(
|
||||||
|
{
|
||||||
|
propF: '111',
|
||||||
|
},
|
||||||
|
mockNode,
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propF: {
|
||||||
|
type: 'i18n',
|
||||||
|
use: 'zh_CN',
|
||||||
|
zh_CN: '111',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fieldId', () => {
|
||||||
|
const mockNode = {
|
||||||
|
componentMeta: {
|
||||||
|
getMetadata() {
|
||||||
|
return {
|
||||||
|
experimental: {
|
||||||
|
initials: [
|
||||||
|
{
|
||||||
|
name: 'propA',
|
||||||
|
initial: () => '111',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settingEntry: {
|
||||||
|
getProp(propName) {
|
||||||
|
return { name: propName };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
has() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
add() {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const editor = new Editor();
|
||||||
|
globalContext.register(editor, Editor);
|
||||||
|
const designer = new Designer({ editor });
|
||||||
|
editor.set('designer', designer);
|
||||||
|
designer.project.open(formSchema);
|
||||||
|
it('fieldId - 已存在', () => {
|
||||||
|
expect(initNodeReducer({
|
||||||
|
propA: '111',
|
||||||
|
fieldId: 'form',
|
||||||
|
}, mockNode)).toEqual({
|
||||||
|
propA: '111',
|
||||||
|
fieldId: undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fieldId - 已存在,但有全局关闭标识', () => {
|
||||||
|
window.__disable_unique_id_checker__ = true;
|
||||||
|
expect(initNodeReducer({
|
||||||
|
propA: '111',
|
||||||
|
fieldId: 'form',
|
||||||
|
}, mockNode)).toEqual({
|
||||||
|
propA: '111',
|
||||||
|
fieldId: 'form',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import { Node, Designer, getConvertedExtraKey } from '@ali/lowcode-designer';
|
||||||
|
import { Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||||
|
import { liveLifecycleReducer } from '../../src/props-reducers/live-lifecycle-reducer';
|
||||||
|
import formSchema from '../fixtures/schema/form';
|
||||||
|
|
||||||
|
const editor = new Editor();
|
||||||
|
globalContext.register(editor, Editor);
|
||||||
|
|
||||||
|
it('liveLifecycleReducer 测试 - live', () => {
|
||||||
|
const mockDidMount = jest.fn();
|
||||||
|
const mockWillUnmount = jest.fn();
|
||||||
|
editor.set('designMode', 'live');
|
||||||
|
const newProps = liveLifecycleReducer(
|
||||||
|
{
|
||||||
|
lifeCycles: {
|
||||||
|
didMount: mockDidMount,
|
||||||
|
willUnmount: mockWillUnmount,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isRoot() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const { lifeCycles } = newProps;
|
||||||
|
expect(typeof lifeCycles.componentDidMount).toBe('function');
|
||||||
|
expect(typeof lifeCycles.componentWillUnMount).toBe('function');
|
||||||
|
|
||||||
|
lifeCycles.didMount();
|
||||||
|
lifeCycles.willUnmount();
|
||||||
|
|
||||||
|
expect(mockDidMount).toHaveBeenCalled();
|
||||||
|
expect(mockWillUnmount).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('liveLifecycleReducer 测试 - design', () => {
|
||||||
|
const mockDidMount = jest.fn();
|
||||||
|
const mockWillUnmount = jest.fn();
|
||||||
|
editor.set('designMode', 'design');
|
||||||
|
const newProps = liveLifecycleReducer(
|
||||||
|
{
|
||||||
|
lifeCycles: {
|
||||||
|
didMount: mockDidMount,
|
||||||
|
willUnmount: mockWillUnmount,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isRoot() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const { lifeCycles } = newProps;
|
||||||
|
expect(lifeCycles).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('liveLifecycleReducer 测试', () => {
|
||||||
|
const mockDidMount = jest.fn();
|
||||||
|
const mockWillUnmount = jest.fn();
|
||||||
|
editor.set('designMode', 'design');
|
||||||
|
const newProps = liveLifecycleReducer(
|
||||||
|
{
|
||||||
|
propA: '111',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isRoot() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const { lifeCycles } = newProps;
|
||||||
|
expect(lifeCycles).toBeUndefined;
|
||||||
|
});
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import { nodeTopFixedReducer } from '../../src/props-reducers/node-top-fixed-reducer';
|
||||||
|
import formSchema from '../fixtures/schema/form';
|
||||||
|
|
||||||
|
it('nodeTopFixedReducer 测试', () => {
|
||||||
|
expect(
|
||||||
|
nodeTopFixedReducer(
|
||||||
|
{
|
||||||
|
propA: '111',
|
||||||
|
},
|
||||||
|
{ componentMeta: { isTopFixed: true } },
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propA: '111',
|
||||||
|
__isTopFixed__: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
nodeTopFixedReducer(
|
||||||
|
{
|
||||||
|
propA: '111',
|
||||||
|
},
|
||||||
|
{ componentMeta: { } },
|
||||||
|
),
|
||||||
|
).toEqual({
|
||||||
|
propA: '111',
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import { Node, Designer, getConvertedExtraKey } from '@ali/lowcode-designer';
|
||||||
|
import { Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||||
|
import { removeEmptyPropsReducer } from '../../src/props-reducers/remove-empty-prop-reducer';
|
||||||
|
import formSchema from '../fixtures/schema/form';
|
||||||
|
|
||||||
|
it('removeEmptyPropsReducer 测试', () => {
|
||||||
|
const newProps = removeEmptyPropsReducer(
|
||||||
|
{
|
||||||
|
propA: '111',
|
||||||
|
dataSource: {
|
||||||
|
online: [
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
params: [
|
||||||
|
{
|
||||||
|
name: 'propA',
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isRoot() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(newProps).toEqual({
|
||||||
|
propA: '111',
|
||||||
|
dataSource: {
|
||||||
|
online: [
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
params: [{
|
||||||
|
name: 'propA',
|
||||||
|
value: '111',
|
||||||
|
}, {
|
||||||
|
value: '111',
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
options: {
|
||||||
|
params: {
|
||||||
|
propA: '111',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,121 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import { Node, Designer, getConvertedExtraKey } from '@ali/lowcode-designer';
|
||||||
|
import { Editor, globalContext } from '@ali/lowcode-editor-core';
|
||||||
|
import { stylePropsReducer } from '../../src/props-reducers/style-reducer';
|
||||||
|
import formSchema from '../fixtures/schema/form';
|
||||||
|
|
||||||
|
const editor: Editor = new Editor();
|
||||||
|
globalContext.register(editor, Editor);
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// const designer = new Designer({ editor });
|
||||||
|
editor.set('designer', {
|
||||||
|
currentDocument: {
|
||||||
|
simulator: {
|
||||||
|
contentDocument: document,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// designer.project.open(formSchema);
|
||||||
|
|
||||||
|
describe('stylePropsReducer 测试', () => {
|
||||||
|
it('无 style 相关属性', () => {
|
||||||
|
expect(stylePropsReducer({ propA: 1 })).toEqual({ propA: 1 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('__style__', () => {
|
||||||
|
const props = {
|
||||||
|
__style__: {
|
||||||
|
'font-size': '50px',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mockNode = { id: 'id1' };
|
||||||
|
expect(stylePropsReducer(props, mockNode)).toEqual({
|
||||||
|
className: '_css_pesudo_id1',
|
||||||
|
__style__: {
|
||||||
|
'font-size': '50px',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(document.querySelector('#_style_pesudo_id1')).textContent =
|
||||||
|
'._css_pesudo_id1 { font-size: 50px; }';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('__style__ - 无 contentDocument', () => {
|
||||||
|
editor.set('designer', {
|
||||||
|
currentDocument: {
|
||||||
|
simulator: {
|
||||||
|
contentDocument: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const props = {
|
||||||
|
__style__: {
|
||||||
|
'font-size': '50px',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mockNode = { id: 'id11' };
|
||||||
|
expect(stylePropsReducer(props, mockNode)).toEqual({
|
||||||
|
__style__: {
|
||||||
|
'font-size': '50px',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(document.querySelector('#_style_pesudo_id11')).toBeNull;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('__style__ - css id 已存在', () => {
|
||||||
|
const s = document.createElement('style');
|
||||||
|
s.setAttribute('type', 'text/css');
|
||||||
|
s.setAttribute('id', '_style_pesudo_id2');
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(s);
|
||||||
|
s.appendChild(document.createTextNode('body {}'));
|
||||||
|
const props = {
|
||||||
|
__style__: {
|
||||||
|
'font-size': '50px',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mockNode = { id: 'id2' };
|
||||||
|
expect(stylePropsReducer(props, mockNode)).toEqual({
|
||||||
|
className: '_css_pesudo_id2',
|
||||||
|
__style__: {
|
||||||
|
'font-size': '50px',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(document.querySelector('#_style_pesudo_id2')).textContent =
|
||||||
|
'._css_pesudo_id2 { font-size: 50px; }';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('containerStyle', () => {
|
||||||
|
const props = {
|
||||||
|
containerStyle: {
|
||||||
|
'font-size': '50px',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mockNode = { id: 'id3' };
|
||||||
|
expect(stylePropsReducer(props, mockNode)).toEqual({
|
||||||
|
className: '_css_pesudo_id3',
|
||||||
|
containerStyle: {
|
||||||
|
'font-size': '50px',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(document.querySelector('#_style_pesudo_id3')).textContent =
|
||||||
|
'._css_pesudo_id3 { font-size: 50px; }';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('pageStyle', () => {
|
||||||
|
const props = {
|
||||||
|
pageStyle: {
|
||||||
|
'font-size': '50rpx',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const mockNode = { id: 'id4' };
|
||||||
|
expect(stylePropsReducer(props, mockNode)).toEqual({
|
||||||
|
className: 'engine-document',
|
||||||
|
pageStyle: {
|
||||||
|
'font-size': '50rpx',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(document.querySelector('#_style_pesudo_id4')).textContent =
|
||||||
|
'._css_pesudo_id4 { font-size: 50px; }';
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
import '../fixtures/window';
|
||||||
|
import { Node, Designer, getConvertedExtraKey } from '@ali/lowcode-designer';
|
||||||
|
import { Editor } from '@ali/lowcode-editor-core';
|
||||||
|
import {
|
||||||
|
upgradePropsReducer,
|
||||||
|
upgradePageLifeCyclesReducer,
|
||||||
|
} from '../../src/props-reducers/upgrade-reducer';
|
||||||
|
import formSchema from '../fixtures/schema/form';
|
||||||
|
|
||||||
|
describe('upgradePropsReducer 测试', () => {
|
||||||
|
it('upgradePropsReducer 测试', () => {
|
||||||
|
const props = {
|
||||||
|
a: {
|
||||||
|
type: 'JSBlock',
|
||||||
|
value: {
|
||||||
|
componentName: 'Slot',
|
||||||
|
props: {
|
||||||
|
slotTitle: '标题',
|
||||||
|
slotName: 'title',
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
type: 'JSBlock',
|
||||||
|
value: {
|
||||||
|
componentName: 'Div',
|
||||||
|
props: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
c1: {
|
||||||
|
type: 'JSBlock',
|
||||||
|
value: {
|
||||||
|
componentName: 'Slot',
|
||||||
|
props: {
|
||||||
|
slotTitle: '标题',
|
||||||
|
slotName: 'title',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
d: {
|
||||||
|
type: 'variable',
|
||||||
|
variable: 'state.a',
|
||||||
|
value: '111',
|
||||||
|
},
|
||||||
|
__slot__haha: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(upgradePropsReducer(props)).toEqual({
|
||||||
|
a: {
|
||||||
|
type: 'JSSlot',
|
||||||
|
title: '标题',
|
||||||
|
name: 'title',
|
||||||
|
value: [],
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
componentName: 'Div',
|
||||||
|
props: {},
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
c1: {
|
||||||
|
type: 'JSSlot',
|
||||||
|
title: '标题',
|
||||||
|
name: 'title',
|
||||||
|
value: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
d: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: 'state.a',
|
||||||
|
mock: '111',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('空值', () => {
|
||||||
|
expect(upgradePropsReducer(null)).toBeNull;
|
||||||
|
expect(upgradePropsReducer(undefined)).toBeUndefined;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const editor = new Editor();
|
||||||
|
const designer = new Designer({ editor });
|
||||||
|
designer.project.open(formSchema);
|
||||||
|
|
||||||
|
it('upgradePageLifeCyclesReducer 测试', () => {
|
||||||
|
const rootNode = designer.currentDocument?.rootNode;
|
||||||
|
const mockDidMount = jest.fn();
|
||||||
|
const mockWillUnmount = jest.fn();
|
||||||
|
upgradePageLifeCyclesReducer({
|
||||||
|
didMount: mockDidMount,
|
||||||
|
willUnmount: mockWillUnmount,
|
||||||
|
}, rootNode);
|
||||||
|
|
||||||
|
const lifeCycles = rootNode?.getPropValue(getConvertedExtraKey('lifeCycles'));
|
||||||
|
|
||||||
|
expect(typeof lifeCycles.didMount).toBe('function');
|
||||||
|
expect(typeof lifeCycles.willUnmount).toBe('function');
|
||||||
|
|
||||||
|
lifeCycles.didMount();
|
||||||
|
lifeCycles.willUnmount();
|
||||||
|
|
||||||
|
expect(mockDidMount).toHaveBeenCalled();
|
||||||
|
expect(mockWillUnmount).toHaveBeenCalled();
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user